diff --git a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset
index c94410e0..34a52fdb 100644
--- a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset
+++ b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset
@@ -14,7 +14,17 @@ MonoBehaviour:
m_EditorClassIdentifier:
m_GroupName: Default Local Group
m_GUID: 6f3207429a65b3e4b83935ac19791077
- m_SerializeEntries: []
+ m_SerializeEntries:
+ - m_GUID: 1a9d24004b795e147b8544845a7a9ae3
+ m_Address: Puzzles/Testing
+ m_ReadOnly: 0
+ m_SerializedLabels: []
+ FlaggedDuringContentUpdateRestriction: 0
+ - m_GUID: d28c589c05c122f449a8b34e696cda53
+ m_Address: Puzzles/Quarry
+ m_ReadOnly: 0
+ m_SerializedLabels: []
+ FlaggedDuringContentUpdateRestriction: 0
m_ReadOnly: 0
m_Settings: {fileID: 11400000, guid: 11da9bb90d9dd5848b4f7629415a6937, type: 2}
m_SchemaSet:
diff --git a/Assets/Data/Puzzles/Quarry.meta b/Assets/Data/Puzzles/Quarry.meta
new file mode 100644
index 00000000..36f2ab7f
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a17ef190d30246143a8f7a83591f2c1f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/Puzzles/ChocolateBirdPuzzle.meta b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle.meta
similarity index 100%
rename from Assets/Data/Puzzles/ChocolateBirdPuzzle.meta
rename to Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle.meta
diff --git a/Assets/Data/Puzzles/ChocolateBirdPuzzle/LureWolter.asset b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/LureWolter.asset
similarity index 95%
rename from Assets/Data/Puzzles/ChocolateBirdPuzzle/LureWolter.asset
rename to Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/LureWolter.asset
index 8f148371..bebae427 100644
--- a/Assets/Data/Puzzles/ChocolateBirdPuzzle/LureWolter.asset
+++ b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/LureWolter.asset
@@ -13,7 +13,7 @@ MonoBehaviour:
m_Name: LureWolter
m_EditorClassIdentifier:
stepId: LureWolter
- displayName: Wolter Lured!
+ displayName: Wolter Lured!!
description: Place Chocolate in the luring spot.
icon: {fileID: 0}
unlocks:
diff --git a/Assets/Data/Puzzles/ChocolateBirdPuzzle/LureWolter.asset.meta b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/LureWolter.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/ChocolateBirdPuzzle/LureWolter.asset.meta
rename to Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/LureWolter.asset.meta
diff --git a/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/PickChocolate.asset b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/PickChocolate.asset
new file mode 100644
index 00000000..5510f772
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/PickChocolate.asset
@@ -0,0 +1,20 @@
+%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: 84e39aac66cf4a10a89abc01b04b13af, type: 3}
+ m_Name: PickChocolate
+ m_EditorClassIdentifier:
+ stepId: PickChocolate
+ displayName: Picked Up Chocolate!
+ description: Avoid the gaze of the picnic lovers and steal their loot.
+ icon: {fileID: 0}
+ unlocks:
+ - {fileID: 11400000, guid: d0851a7610551104fa285c0748549d90, type: 2}
diff --git a/Assets/Data/Puzzles/ChocolateBirdPuzzle/PickChocolate.asset.meta b/Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/PickChocolate.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/ChocolateBirdPuzzle/PickChocolate.asset.meta
rename to Assets/Data/Puzzles/Quarry/ChocolateBirdPuzzle/PickChocolate.asset.meta
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle.meta b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle.meta
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle.meta
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle.meta
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle/InteractWithBallTree.asset b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/InteractWithBallTree.asset
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle/InteractWithBallTree.asset
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/InteractWithBallTree.asset
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle/InteractWithBallTree.asset.meta b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/InteractWithBallTree.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle/InteractWithBallTree.asset.meta
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/InteractWithBallTree.asset.meta
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle/PickUpFootball.asset b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PickUpFootball.asset
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle/PickUpFootball.asset
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PickUpFootball.asset
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle/PickUpFootball.asset.meta b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PickUpFootball.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle/PickUpFootball.asset.meta
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PickUpFootball.asset.meta
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset
diff --git a/Assets/Data/Puzzles/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset.meta b/Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset.meta
rename to Assets/Data/Puzzles/Quarry/FootballBirdPuzzle/PlaceFootballInLureSpotA.asset.meta
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle.meta b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle.meta
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle.meta
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle.meta
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle/InteractWLawnMower.asset b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/InteractWLawnMower.asset
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle/InteractWLawnMower.asset
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/InteractWLawnMower.asset
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle/InteractWLawnMower.asset.meta b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/InteractWLawnMower.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle/InteractWLawnMower.asset.meta
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/InteractWLawnMower.asset.meta
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle/PickUpNails.asset b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PickUpNails.asset
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle/PickUpNails.asset
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PickUpNails.asset
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle/PickUpNails.asset.meta b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PickUpNails.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle/PickUpNails.asset.meta
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PickUpNails.asset.meta
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle/PlaceNailsinlureC.asset b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PlaceNailsinlureC.asset
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle/PlaceNailsinlureC.asset
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PlaceNailsinlureC.asset
diff --git a/Assets/Data/Puzzles/HammerBirdPuzzle/PlaceNailsinlureC.asset.meta b/Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PlaceNailsinlureC.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/HammerBirdPuzzle/PlaceNailsinlureC.asset.meta
rename to Assets/Data/Puzzles/Quarry/HammerBirdPuzzle/PlaceNailsinlureC.asset.meta
diff --git a/Assets/Data/Puzzles/Quarry/Quarry.asset b/Assets/Data/Puzzles/Quarry/Quarry.asset
new file mode 100644
index 00000000..021875da
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry/Quarry.asset
@@ -0,0 +1,31 @@
+%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: 0a79780a5a0d498084afd737d4515e3b, type: 3}
+ m_Name: Quarry
+ m_EditorClassIdentifier: AppleHillsScripts::PuzzleS.PuzzleLevelDataSO
+ levelId: Quarry
+ displayName: Quarry
+ allSteps:
+ - {fileID: 11400000, guid: d0851a7610551104fa285c0748549d90, type: 2}
+ - {fileID: 11400000, guid: ed967c44f3a8b914aabc1539f774efc7, type: 2}
+ - {fileID: 11400000, guid: 8ac614a698631554ab8ac39aed04a189, type: 2}
+ - {fileID: 11400000, guid: 6386246caab8faa40b2da221d9ab9b8a, type: 2}
+ - {fileID: 11400000, guid: 0fb0ab2b55d93a24685e9f6651adcf30, type: 2}
+ - {fileID: 11400000, guid: ea383d1dee861f54c9a1d4f32a2f6afc, type: 2}
+ - {fileID: 11400000, guid: f9da68caaae2a244885a13cf2e2e45c0, type: 2}
+ - {fileID: 11400000, guid: 28848561ff31fe24ea9f8590dee0bf8f, type: 2}
+ - {fileID: 11400000, guid: 5700dd3bf16fa9e4aa9905379118d1bd, type: 2}
+ - {fileID: 11400000, guid: 37409d749a15970438d761d1d658d7a6, type: 2}
+ initialSteps:
+ - {fileID: 11400000, guid: ed967c44f3a8b914aabc1539f774efc7, type: 2}
+ - {fileID: 11400000, guid: 8ac614a698631554ab8ac39aed04a189, type: 2}
+ - {fileID: 11400000, guid: ea383d1dee861f54c9a1d4f32a2f6afc, type: 2}
diff --git a/Assets/Data/Puzzles/Quarry/Quarry.asset.meta b/Assets/Data/Puzzles/Quarry/Quarry.asset.meta
new file mode 100644
index 00000000..fba4d3b7
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry/Quarry.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d28c589c05c122f449a8b34e696cda53
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/Puzzles/SoundBirdPuzzle.meta b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle.meta
similarity index 100%
rename from Assets/Data/Puzzles/SoundBirdPuzzle.meta
rename to Assets/Data/Puzzles/Quarry/SoundBirdPuzzle.meta
diff --git a/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/HeadbandPickup.asset b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/HeadbandPickup.asset
new file mode 100644
index 00000000..e6d15665
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/HeadbandPickup.asset
@@ -0,0 +1,20 @@
+%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: 84e39aac66cf4a10a89abc01b04b13af, type: 3}
+ m_Name: HeadbandPickup
+ m_EditorClassIdentifier:
+ stepId: HeadBandPickup
+ displayName: Picked up Headband!
+ description: Once Wolter jumps out of the bushes, the headband is interactable.
+ icon: {fileID: 0}
+ unlocks:
+ - {fileID: 11400000, guid: 37409d749a15970438d761d1d658d7a6, type: 2}
diff --git a/Assets/Data/Puzzles/SoundBirdPuzzle/HeadbandPickup.asset.meta b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/HeadbandPickup.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/SoundBirdPuzzle/HeadbandPickup.asset.meta
rename to Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/HeadbandPickup.asset.meta
diff --git a/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/SoundBirdDeafen.asset b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/SoundBirdDeafen.asset
new file mode 100644
index 00000000..3f9383a4
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/SoundBirdDeafen.asset
@@ -0,0 +1,19 @@
+%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: 84e39aac66cf4a10a89abc01b04b13af, type: 3}
+ m_Name: SoundBirdDeafen
+ m_EditorClassIdentifier:
+ stepId: Deafen Bird
+ displayName: Deaf SoundBird
+ description: sds
+ icon: {fileID: 0}
+ unlocks: []
diff --git a/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/SoundBirdDeafen.asset.meta b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/SoundBirdDeafen.asset.meta
new file mode 100644
index 00000000..efd5eb28
--- /dev/null
+++ b/Assets/Data/Puzzles/Quarry/SoundBirdPuzzle/SoundBirdDeafen.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 37409d749a15970438d761d1d658d7a6
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/Puzzles/Testing.meta b/Assets/Data/Puzzles/Testing.meta
new file mode 100644
index 00000000..f87db99d
--- /dev/null
+++ b/Assets/Data/Puzzles/Testing.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a9958845b52d0d7468c9c36a6969e9ea
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle.meta b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle.meta
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle.meta
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle.meta
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Ass1.asset b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass1.asset
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Ass1.asset
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass1.asset
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Ass1.asset.meta b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass1.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Ass1.asset.meta
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass1.asset.meta
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Ass2.asset b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass2.asset
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Ass2.asset
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass2.asset
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Ass2.asset.meta b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass2.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Ass2.asset.meta
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass2.asset.meta
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Ass3.asset b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass3.asset
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Ass3.asset
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass3.asset
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Ass3.asset.meta b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass3.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Ass3.asset.meta
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Ass3.asset.meta
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Head.asset b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Head.asset
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Head.asset
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Head.asset
diff --git a/Assets/Data/Puzzles/ExampleAssPuzzle/Head.asset.meta b/Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Head.asset.meta
similarity index 100%
rename from Assets/Data/Puzzles/ExampleAssPuzzle/Head.asset.meta
rename to Assets/Data/Puzzles/Testing/ExampleAssPuzzle/Head.asset.meta
diff --git a/Assets/Data/Puzzles/Testing/Testing.asset b/Assets/Data/Puzzles/Testing/Testing.asset
new file mode 100644
index 00000000..cf3ef9f7
--- /dev/null
+++ b/Assets/Data/Puzzles/Testing/Testing.asset
@@ -0,0 +1,24 @@
+%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: 0a79780a5a0d498084afd737d4515e3b, type: 3}
+ m_Name: Testing
+ m_EditorClassIdentifier: AppleHillsScripts::PuzzleS.PuzzleLevelDataSO
+ levelId: Testing
+ displayName: Testing
+ allSteps:
+ - {fileID: 11400000, guid: 0b13ff4f31443b74281b13e0eef865c2, type: 2}
+ - {fileID: 11400000, guid: a84cbe9804e13f74e857c55d90cc10d1, type: 2}
+ - {fileID: 11400000, guid: 13b0c411066f85a41ba40c3bbbc281ed, type: 2}
+ - {fileID: 11400000, guid: 9de0c57af6191384e96e2ba7c04a3d0d, type: 2}
+ initialSteps:
+ - {fileID: 11400000, guid: 0b13ff4f31443b74281b13e0eef865c2, type: 2}
+ - {fileID: 11400000, guid: a84cbe9804e13f74e857c55d90cc10d1, type: 2}
diff --git a/Assets/Data/Puzzles/Testing/Testing.asset.meta b/Assets/Data/Puzzles/Testing/Testing.asset.meta
new file mode 100644
index 00000000..70d7ef48
--- /dev/null
+++ b/Assets/Data/Puzzles/Testing/Testing.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1a9d24004b795e147b8544845a7a9ae3
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Editor/PuzzleAssetProcessor.cs b/Assets/Editor/PuzzleAssetProcessor.cs
new file mode 100644
index 00000000..a3fdb7d4
--- /dev/null
+++ b/Assets/Editor/PuzzleAssetProcessor.cs
@@ -0,0 +1,321 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using UnityEditor.AddressableAssets;
+using UnityEditor.AddressableAssets.Settings;
+
+namespace PuzzleS.Editor
+{
+ ///
+ /// Handles asset post-processing for puzzle step data.
+ /// Automatically builds level data from folder structure.
+ ///
+ public class PuzzleAssetProcessor : AssetPostprocessor
+ {
+ // Base path for puzzle data
+ private const string PuzzleDataBasePath = "Assets/Data/Puzzles";
+
+ ///
+ /// Called after assets have been imported, deleted, or moved.
+ ///
+ private static void OnPostprocessAllAssets(
+ string[] importedAssets,
+ string[] deletedAssets,
+ string[] movedAssets,
+ string[] movedFromAssetPaths)
+ {
+ bool puzzleAssetsChanged = false;
+
+ // Check for changes to puzzle step assets
+ foreach (string assetPath in importedAssets.Concat(movedAssets))
+ {
+ if (IsPuzzleAssetPath(assetPath) && Path.GetExtension(assetPath) == ".asset")
+ {
+ var asset = AssetDatabase.LoadAssetAtPath(assetPath);
+
+ if (asset is PuzzleStepSO)
+ {
+ // Find which level this step belongs to
+ string assetDir = Path.GetDirectoryName(assetPath);
+ string levelFolderPath = FindLevelFolder(assetDir);
+
+ if (!string.IsNullOrEmpty(levelFolderPath))
+ {
+ ProcessPuzzleLevelFolder(levelFolderPath);
+ puzzleAssetsChanged = true;
+ }
+ }
+ }
+ }
+
+ // Check for changes to puzzle folder structure
+ foreach (string assetPath in deletedAssets.Concat(movedFromAssetPaths))
+ {
+ if (IsPuzzleAssetPath(assetPath))
+ {
+ // Extract parent folders for processing
+ string assetDir = Path.GetDirectoryName(assetPath);
+ string levelFolderPath = FindLevelFolder(assetDir);
+
+ if (!string.IsNullOrEmpty(levelFolderPath) && Directory.Exists(levelFolderPath))
+ {
+ ProcessPuzzleLevelFolder(levelFolderPath);
+ puzzleAssetsChanged = true;
+ }
+ }
+ }
+
+ if (puzzleAssetsChanged)
+ {
+ // Make sure changes are saved
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+
+ Debug.Log("[PuzzleProcessor] Puzzle data processing complete");
+ }
+ }
+
+ ///
+ /// Checks if an asset path is within the puzzle data folder structure
+ ///
+ private static bool IsPuzzleAssetPath(string assetPath)
+ {
+ // Unity's AssetDatabase always uses forward slashes, so we need to normalize for comparison
+ string normalizedPath = assetPath.Replace('\\', '/');
+ string normalizedBasePath = PuzzleDataBasePath;
+
+ return normalizedPath.StartsWith(normalizedBasePath) &&
+ !normalizedPath.Contains("/.") && // Skip hidden folders
+ !Path.GetExtension(normalizedPath).Equals(".meta");
+ }
+
+ ///
+ /// Find the level folder that contains this asset path
+ /// Assumes level folders are direct children of PuzzleDataBasePath
+ ///
+ private static string FindLevelFolder(string assetPath)
+ {
+ if (string.IsNullOrEmpty(assetPath)) return null;
+
+ // Unity's AssetDatabase always uses forward slashes
+ string normalizedPath = assetPath.Replace('\\', '/');
+
+ if (!normalizedPath.StartsWith(PuzzleDataBasePath))
+ return null;
+
+ // Get the relative path from the base path
+ string relativePath = normalizedPath.Substring(PuzzleDataBasePath.Length);
+ if (relativePath.StartsWith("/"))
+ relativePath = relativePath.Substring(1);
+
+ // Split into path components and get the first folder (level name)
+ string[] pathComponents = relativePath.Split('/');
+
+ // First component after PuzzleDataBasePath should be the level name
+ if (pathComponents.Length > 0)
+ {
+ // Use proper path joining with Unity's forward slash convention
+ return PuzzleDataBasePath + "/" + pathComponents[0];
+ }
+
+ return null;
+ }
+
+ ///
+ /// Process a level folder to build/update a PuzzleLevelDataSO with all puzzle steps
+ ///
+ private static void ProcessPuzzleLevelFolder(string folderPath)
+ {
+ if (!Directory.Exists(folderPath)) return;
+
+ // Get level name from folder name
+ string levelName = Path.GetFileName(folderPath);
+ if (string.IsNullOrEmpty(levelName)) return;
+
+ // Find all puzzle step assets in this level (including subfolders)
+ var stepAssets = FindAssetsOfTypeRecursive(folderPath);
+ if (stepAssets.Count == 0) return;
+
+ // Get or create level data asset
+ var levelDataAsset = GetOrCreateLevelDataAsset(folderPath, levelName);
+ if (levelDataAsset == null) return;
+
+ // Update level data
+ levelDataAsset.levelId = levelName;
+ levelDataAsset.displayName = levelName; // Default display name matches folder name
+ levelDataAsset.allSteps = stepAssets;
+
+ // Pre-process initial steps (those with no dependencies)
+ levelDataAsset.initialSteps = FindInitialSteps(stepAssets);
+
+ // Pre-process dependencies (which steps are required by each step)
+ PrecomputeDependencies(levelDataAsset);
+
+ // Mark as dirty and save
+ EditorUtility.SetDirty(levelDataAsset);
+
+ // Setup addressables
+ SetupAddressableAsset(AssetDatabase.GetAssetPath(levelDataAsset), $"Puzzles/{levelName}");
+
+ Debug.Log($"[PuzzleProcessor] Processed level: {levelName} with {stepAssets.Count} steps");
+ }
+
+ ///
+ /// Find all assets of a specific type in a folder and its subfolders
+ ///
+ private static List FindAssetsOfTypeRecursive(string folderPath) where T : ScriptableObject
+ {
+ var result = new List();
+
+ if (!Directory.Exists(folderPath)) return result;
+
+ var guids = AssetDatabase.FindAssets("t:" + typeof(T).Name, new[] { folderPath });
+ foreach (var guid in guids)
+ {
+ string assetPath = AssetDatabase.GUIDToAssetPath(guid);
+ var asset = AssetDatabase.LoadAssetAtPath(assetPath);
+ if (asset != null)
+ {
+ result.Add(asset);
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Get existing level data asset or create a new one
+ ///
+ private static PuzzleLevelDataSO GetOrCreateLevelDataAsset(string folderPath, string levelName)
+ {
+ // Check for existing level data asset
+ string levelDataAssetPath = $"{folderPath}/{levelName}.asset";
+ var levelDataAsset = AssetDatabase.LoadAssetAtPath(levelDataAssetPath);
+
+ if (levelDataAsset == null)
+ {
+ // Create new level data asset
+ levelDataAsset = ScriptableObject.CreateInstance();
+ AssetDatabase.CreateAsset(levelDataAsset, levelDataAssetPath);
+ Debug.Log($"[PuzzleProcessor] Created new level data asset: {levelDataAssetPath}");
+ }
+
+ return levelDataAsset;
+ }
+
+ ///
+ /// Find steps that have no dependencies (initial steps)
+ ///
+ private static List FindInitialSteps(List steps)
+ {
+ var initialSteps = new List();
+ var dependentSteps = new HashSet();
+
+ // Find all steps that are dependencies of other steps
+ foreach (var step in steps)
+ {
+ if (step == null) continue;
+
+ foreach (var unlockStep in step.unlocks)
+ {
+ if (unlockStep != null && steps.Contains(unlockStep))
+ {
+ dependentSteps.Add(unlockStep);
+ }
+ }
+ }
+
+ // Initial steps are those not depended on by any other step
+ foreach (var step in steps)
+ {
+ if (step != null && !dependentSteps.Contains(step))
+ {
+ initialSteps.Add(step);
+ }
+ }
+
+ return initialSteps;
+ }
+
+ ///
+ /// Pre-compute dependency information for a puzzle level
+ ///
+ private static void PrecomputeDependencies(PuzzleLevelDataSO levelDataAsset)
+ {
+ levelDataAsset.stepDependencies.Clear();
+
+ // Build reverse dependency map (which steps are required by each step)
+ var reverseDependencies = new Dictionary>();
+
+ foreach (var step in levelDataAsset.allSteps)
+ {
+ if (step == null) continue;
+
+ foreach (var unlockStep in step.unlocks)
+ {
+ if (unlockStep == null) continue;
+
+ if (!reverseDependencies.ContainsKey(unlockStep.stepId))
+ {
+ reverseDependencies[unlockStep.stepId] = new List();
+ }
+
+ reverseDependencies[unlockStep.stepId].Add(step.stepId);
+ }
+ }
+
+ // Convert to serialized form
+ foreach (var entry in reverseDependencies)
+ {
+ levelDataAsset.stepDependencies[entry.Key] = entry.Value.ToArray();
+ }
+ }
+
+ ///
+ /// Set up an asset as an Addressable with the specified address
+ ///
+ private static void SetupAddressableAsset(string assetPath, string address)
+ {
+ if (!File.Exists(assetPath)) return;
+
+ // Get Addressable settings
+ var settings = AddressableAssetSettingsDefaultObject.Settings;
+ if (settings == null)
+ {
+ Debug.LogWarning("[PuzzleProcessor] Addressable Asset Settings not found. Make sure Addressables is set up in your project.");
+ return;
+ }
+
+ // Get default group
+ var defaultGroup = settings.DefaultGroup;
+ if (defaultGroup == null)
+ {
+ Debug.LogWarning("[PuzzleProcessor] Default Addressable Asset Group not found.");
+ return;
+ }
+
+ // Get asset GUID
+ var guid = AssetDatabase.AssetPathToGUID(assetPath);
+
+ // Check if entry exists
+ var existingEntry = settings.FindAssetEntry(guid);
+ if (existingEntry != null)
+ {
+ // Update existing entry
+ existingEntry.SetAddress(address);
+ }
+ else
+ {
+ // Create new entry
+ settings.CreateOrMoveEntry(guid, defaultGroup);
+ var newEntry = settings.FindAssetEntry(guid);
+ if (newEntry != null)
+ {
+ newEntry.SetAddress(address);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/Editor/PuzzleAssetProcessor.cs.meta b/Assets/Editor/PuzzleAssetProcessor.cs.meta
new file mode 100644
index 00000000..1e34c156
--- /dev/null
+++ b/Assets/Editor/PuzzleAssetProcessor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 5097566c60d341dbb6f2bf5175b048cb
+timeCreated: 1760532147
\ No newline at end of file
diff --git a/Assets/Editor/PuzzleChainEditorWindow.cs b/Assets/Editor/PuzzleChainEditorWindow.cs
deleted file mode 100644
index 27c140ac..00000000
--- a/Assets/Editor/PuzzleChainEditorWindow.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using UnityEditor;
-using UnityEngine;
-using System.Collections.Generic;
-
-public class PuzzleChainEditorWindow : EditorWindow
-{
- private List puzzleSteps = new List();
- private Dictionary> dependencyGraph;
- private Vector2 scrollPos;
- private const int INDENT_SIZE = 24;
-
- [MenuItem("AppleHills/Puzzle Chain Editor")]
- public static void ShowWindow()
- {
- var window = GetWindow("Puzzle Chain Editor");
- window.minSize = new Vector2(600, 400);
- window.maxSize = new Vector2(1200, 800);
- window.position = new Rect(100, 100, 700, 500); // Reasonable default size and position
- }
-
- private void OnEnable()
- {
- LoadPuzzleSteps();
- ProcessPuzzleChains();
- }
-
- private void LoadPuzzleSteps()
- {
- puzzleSteps.Clear();
- // Find all PuzzleStepSO assets in the project
- string[] guids = AssetDatabase.FindAssets("t:PuzzleStepSO");
- foreach (var guid in guids)
- {
- var path = AssetDatabase.GUIDToAssetPath(guid);
- // Only include those under Assets/Data/Puzzles (case-insensitive)
- if (path.Replace('\\', '/').StartsWith("Assets/Data/Puzzles", System.StringComparison.OrdinalIgnoreCase))
- {
- var step = AssetDatabase.LoadAssetAtPath(path);
- if (step != null)
- puzzleSteps.Add(step);
- }
- }
- // Remove any nulls just in case
- puzzleSteps.RemoveAll(s => s == null);
- // Remove nulls from unlocks lists
- foreach (var step in puzzleSteps)
- {
- if (step.unlocks != null)
- step.unlocks.RemoveAll(u => u == null);
- }
- }
-
- private void ProcessPuzzleChains()
- {
- // Defensive: ensure no nulls in puzzleSteps or unlocks
- puzzleSteps.RemoveAll(s => s == null);
- foreach (var step in puzzleSteps)
- {
- if (step.unlocks != null)
- step.unlocks.RemoveAll(u => u == null);
- }
- dependencyGraph = PuzzleGraphUtility.BuildDependencyGraph(puzzleSteps);
- }
-
- private void OnGUI()
- {
- EditorGUILayout.LabelField("Puzzle Chain Visualization", EditorStyles.boldLabel);
- if (puzzleSteps.Count == 0)
- {
- EditorGUILayout.HelpBox("No PuzzleStepSO assets found in Assets/Data/Puzzles.", MessageType.Warning);
- return;
- }
- scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
- var initialSteps = dependencyGraph != null ? PuzzleGraphUtility.FindInitialSteps(dependencyGraph) : new List();
- foreach (var step in initialSteps)
- {
- if (step == null) continue; // Defensive
- EditorGUILayout.BeginVertical("box");
- EditorGUILayout.LabelField($"Step Path: {step.displayName} ({step.stepId})", EditorStyles.largeLabel);
- GUILayout.Space(6);
- DrawStepTree(step, 0);
- EditorGUILayout.EndVertical();
- GUILayout.Space(12); // Space between step paths
- }
- EditorGUILayout.EndScrollView();
- }
-
- private void DrawStepTree(PuzzleStepSO step, int indent)
- {
- if (step == null) {
- EditorGUILayout.LabelField("[Missing Step]", EditorStyles.boldLabel);
- return;
- }
- EditorGUILayout.BeginHorizontal();
- GUILayout.Space(indent * INDENT_SIZE);
- EditorGUILayout.BeginVertical("box");
- EditorGUILayout.LabelField($"{step.displayName} ({step.stepId})", EditorStyles.boldLabel);
- EditorGUILayout.LabelField(step.description ?? "", EditorStyles.wordWrappedLabel);
- GUILayout.Space(4);
- if (GUILayout.Button("Open in Inspector", GUILayout.Width(150)))
- {
- Selection.activeObject = step;
- EditorGUIUtility.PingObject(step);
- }
- EditorGUILayout.EndVertical();
- EditorGUILayout.EndHorizontal();
- GUILayout.Space(6);
- if (step.unlocks != null)
- {
- foreach (var unlock in step.unlocks)
- {
- DrawStepTree(unlock, indent + 1);
- }
- }
- }
-}
diff --git a/Assets/Editor/PuzzleChainEditorWindow.cs.meta b/Assets/Editor/PuzzleChainEditorWindow.cs.meta
deleted file mode 100644
index 26053c53..00000000
--- a/Assets/Editor/PuzzleChainEditorWindow.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: f7bfaa69d42e45adaa4a44dd143efc2f
-timeCreated: 1756991142
\ No newline at end of file
diff --git a/Assets/Editor/PuzzleSystem.meta b/Assets/Editor/PuzzleSystem.meta
new file mode 100644
index 00000000..66996b2e
--- /dev/null
+++ b/Assets/Editor/PuzzleSystem.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 453e2745b86c424da42227fbc38ed6d7
+timeCreated: 1760539457
\ No newline at end of file
diff --git a/Assets/Editor/PuzzleSystem/PuzzleEditorWindow.cs b/Assets/Editor/PuzzleSystem/PuzzleEditorWindow.cs
new file mode 100644
index 00000000..cbbef1d6
--- /dev/null
+++ b/Assets/Editor/PuzzleSystem/PuzzleEditorWindow.cs
@@ -0,0 +1,875 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using PuzzleS;
+
+namespace AppleHills.Editor.PuzzleSystem
+{
+ ///
+ /// Editor utility for managing puzzle steps and debugging puzzle state at runtime.
+ /// Provides tabs for both editing puzzle data and runtime debugging.
+ ///
+ public class PuzzleEditorWindow : EditorWindow
+ {
+ // Paths
+ private const string PuzzleDataBasePath = "Assets/Data";
+ private const string MenuPath = "AppleHills/Puzzle Editor";
+
+ // Editor state
+ private List _allPuzzleSteps = new List();
+ private Dictionary> _puzzleStepsByFolder = new Dictionary>();
+ private PuzzleStepSO _selectedStep;
+ private Dictionary _folderExpanded = new Dictionary();
+ private Vector2 _puzzleListScrollPosition;
+ private Vector2 _puzzleEditScrollPosition;
+ private string _searchQuery = "";
+ private bool _isDirty = false;
+
+ // Tab management
+ private int _selectedTab = 0;
+ private readonly string[] _tabNames = { "Edit", "Debug" };
+
+ // Runtime debug state
+ private Dictionary _stepUnlockState = new Dictionary();
+ private Dictionary _stepCompletedState = new Dictionary();
+ private Vector2 _debugScrollPosition;
+ private bool _isPlaying = false;
+ private bool _hasRuntimeData = false;
+ private PuzzleLevelDataSO _runtimeLevelData;
+
+ // New step creation
+ private string _newStepName = "New Step";
+ private string _selectedFolder = "";
+ private bool _showCreateNewStepDialog = false;
+
+ [MenuItem(MenuPath)]
+ public static void ShowWindow()
+ {
+ var window = GetWindow("Puzzle Editor");
+ window.minSize = new Vector2(800, 600);
+ window.Show();
+ }
+
+ private void OnEnable()
+ {
+ // Load all puzzle steps
+ LoadAllPuzzleSteps();
+
+ // Register for undo/redo
+ Undo.undoRedoPerformed += OnUndoRedo;
+
+ // Set up update callbacks
+ EditorApplication.update += OnEditorUpdate;
+ EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
+ }
+
+ private void OnDisable()
+ {
+ // Unregister from undo/redo
+ Undo.undoRedoPerformed -= OnUndoRedo;
+
+ // Unregister from update
+ EditorApplication.update -= OnEditorUpdate;
+ EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
+ }
+
+ private void OnEditorUpdate()
+ {
+ // Check if we're in play mode
+ bool currentlyPlaying = EditorApplication.isPlaying && !EditorApplication.isPaused;
+ if (_isPlaying != currentlyPlaying)
+ {
+ _isPlaying = currentlyPlaying;
+ Repaint();
+ }
+
+ // In play mode, update runtime data periodically
+ if (_isPlaying)
+ {
+ UpdateRuntimeData();
+ Repaint();
+ }
+ }
+
+ private void OnUndoRedo()
+ {
+ _isDirty = true;
+ Repaint();
+ }
+
+ private void OnPlayModeStateChanged(PlayModeStateChange state)
+ {
+ if (state == PlayModeStateChange.EnteredPlayMode)
+ {
+ _isPlaying = true;
+ _hasRuntimeData = false;
+ _stepUnlockState.Clear();
+ _stepCompletedState.Clear();
+ }
+ else if (state == PlayModeStateChange.ExitingPlayMode)
+ {
+ _isPlaying = false;
+ _hasRuntimeData = false;
+ _runtimeLevelData = null;
+ }
+
+ Repaint();
+ }
+
+ private void OnGUI()
+ {
+ DrawHeader();
+
+ _selectedTab = GUILayout.Toolbar(_selectedTab, _tabNames);
+
+ EditorGUILayout.Space();
+
+ switch (_selectedTab)
+ {
+ case 0: // Edit tab
+ DrawEditTab();
+ break;
+ case 1: // Debug tab
+ DrawDebugTab();
+ break;
+ }
+
+ // Apply any pending changes
+ if (_isDirty)
+ {
+ SaveChanges();
+ _isDirty = false;
+ }
+ }
+
+ #region Header UI
+
+ private void DrawHeader()
+ {
+ EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
+
+ if (GUILayout.Button("Refresh", EditorStyles.toolbarButton, GUILayout.Width(60)))
+ {
+ LoadAllPuzzleSteps();
+ }
+
+ GUILayout.FlexibleSpace();
+
+ // Tab-specific toolbar options
+ if (_selectedTab == 0) // Edit tab
+ {
+ if (GUILayout.Button("Create New", EditorStyles.toolbarButton, GUILayout.Width(80)))
+ {
+ _showCreateNewStepDialog = true;
+ }
+ }
+ else if (_selectedTab == 1) // Debug tab
+ {
+ EditorGUILayout.LabelField(_isPlaying ? "Runtime Active" : "Editor Mode", EditorStyles.toolbarButton, GUILayout.Width(100));
+ }
+
+ EditorGUILayout.EndHorizontal();
+ }
+
+ #endregion
+
+ #region Edit Tab
+
+ private void DrawEditTab()
+ {
+ EditorGUILayout.BeginHorizontal();
+
+ // Left panel - puzzle step list
+ EditorGUILayout.BeginVertical(GUILayout.Width(250));
+ DrawStepListPanel();
+ EditorGUILayout.EndVertical();
+
+ // Separator
+ EditorGUILayout.Space();
+
+ // Right panel - puzzle step editor
+ EditorGUILayout.BeginVertical();
+ DrawStepEditorPanel();
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.EndHorizontal();
+
+ // Draw create new step dialog if needed
+ if (_showCreateNewStepDialog)
+ {
+ DrawCreateNewStepDialog();
+ }
+ }
+
+ private void DrawStepListPanel()
+ {
+ // Search field
+ EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
+ _searchQuery = EditorGUILayout.TextField(_searchQuery, EditorStyles.toolbarSearchField);
+ if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(20)) && !string.IsNullOrEmpty(_searchQuery))
+ {
+ _searchQuery = "";
+ GUI.FocusControl(null);
+ }
+ EditorGUILayout.EndHorizontal();
+
+ _puzzleListScrollPosition = EditorGUILayout.BeginScrollView(_puzzleListScrollPosition);
+
+ // If search query exists, show filtered results across all folders
+ if (!string.IsNullOrEmpty(_searchQuery))
+ {
+ var filteredSteps = _allPuzzleSteps.Where(step =>
+ step.displayName.ToLower().Contains(_searchQuery.ToLower()) ||
+ step.stepId.ToLower().Contains(_searchQuery.ToLower())).ToList();
+
+ if (filteredSteps.Any())
+ {
+ EditorGUILayout.LabelField($"Search Results ({filteredSteps.Count}):", EditorStyles.boldLabel);
+
+ foreach (var step in filteredSteps)
+ {
+ if (DrawStepListItem(step))
+ {
+ _selectedStep = step;
+ GUI.FocusControl(null);
+ }
+ }
+ }
+ else
+ {
+ EditorGUILayout.LabelField("No matching steps found");
+ }
+ }
+ // Otherwise show organized by folder
+ else
+ {
+ foreach (var folderEntry in _puzzleStepsByFolder)
+ {
+ string folderName = folderEntry.Key;
+ List steps = folderEntry.Value;
+
+ if (!_folderExpanded.ContainsKey(folderName))
+ {
+ _folderExpanded[folderName] = true; // Default to expanded
+ }
+
+ // Folder header with toggle
+ EditorGUILayout.BeginHorizontal();
+ _folderExpanded[folderName] = EditorGUILayout.Foldout(_folderExpanded[folderName], folderName, true);
+
+ // Show step count
+ EditorGUILayout.LabelField($"({steps.Count})", GUILayout.Width(40));
+ EditorGUILayout.EndHorizontal();
+
+ // Draw steps in folder if expanded
+ if (_folderExpanded[folderName])
+ {
+ EditorGUI.indentLevel++;
+
+ foreach (var step in steps)
+ {
+ if (DrawStepListItem(step))
+ {
+ _selectedStep = step;
+ GUI.FocusControl(null);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ }
+ }
+
+ EditorGUILayout.EndScrollView();
+ }
+
+ private bool DrawStepListItem(PuzzleStepSO step)
+ {
+ if (step == null) return false;
+
+ bool isSelected = step == _selectedStep;
+ EditorGUILayout.BeginHorizontal(isSelected ? EditorStyles.selectionRect : EditorStyles.helpBox);
+
+ // Icon if available
+ if (step.icon != null)
+ {
+ GUILayout.Label(new GUIContent(step.icon.texture), GUILayout.Width(20), GUILayout.Height(20));
+ }
+ else
+ {
+ GUILayout.Space(24);
+ }
+
+ // Name and ID
+ EditorGUILayout.BeginVertical();
+ EditorGUILayout.LabelField(step.displayName, EditorStyles.boldLabel);
+ EditorGUILayout.LabelField(step.stepId, EditorStyles.miniLabel);
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.EndHorizontal();
+
+ // Check if this item was clicked
+ Rect itemRect = GUILayoutUtility.GetLastRect();
+ bool wasClicked = Event.current.type == EventType.MouseDown && Event.current.button == 0
+ && itemRect.Contains(Event.current.mousePosition);
+
+ if (wasClicked)
+ {
+ Event.current.Use();
+ }
+
+ return wasClicked;
+ }
+
+ private void DrawStepEditorPanel()
+ {
+ if (_selectedStep == null)
+ {
+ EditorGUILayout.HelpBox("Select a puzzle step to edit", MessageType.Info);
+ return;
+ }
+
+ _puzzleEditScrollPosition = EditorGUILayout.BeginScrollView(_puzzleEditScrollPosition);
+
+ EditorGUI.BeginChangeCheck();
+
+ // Header with name and ID
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Editing:", EditorStyles.boldLabel, GUILayout.Width(60));
+ EditorGUILayout.LabelField(_selectedStep.displayName, EditorStyles.boldLabel);
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.Space();
+
+ // Basic properties
+ EditorGUILayout.LabelField("Basic Properties", EditorStyles.boldLabel);
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+
+ // Step ID
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Step ID:", GUILayout.Width(100));
+ string newStepId = EditorGUILayout.TextField(_selectedStep.stepId);
+ if (newStepId != _selectedStep.stepId)
+ {
+ Undo.RecordObject(_selectedStep, "Change Step ID");
+ _selectedStep.stepId = newStepId;
+ _isDirty = true;
+ }
+ EditorGUILayout.EndHorizontal();
+
+ // Display Name
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Display Name:", GUILayout.Width(100));
+ string newDisplayName = EditorGUILayout.TextField(_selectedStep.displayName);
+ if (newDisplayName != _selectedStep.displayName)
+ {
+ Undo.RecordObject(_selectedStep, "Change Display Name");
+ _selectedStep.displayName = newDisplayName;
+ _isDirty = true;
+ }
+ EditorGUILayout.EndHorizontal();
+
+ // Description
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Description:", GUILayout.Width(100));
+ string newDescription = EditorGUILayout.TextArea(_selectedStep.description, GUILayout.Height(60));
+ if (newDescription != _selectedStep.description)
+ {
+ Undo.RecordObject(_selectedStep, "Change Description");
+ _selectedStep.description = newDescription;
+ _isDirty = true;
+ }
+ EditorGUILayout.EndHorizontal();
+
+ // Icon
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Icon:", GUILayout.Width(100));
+ Sprite newIcon = (Sprite)EditorGUILayout.ObjectField(_selectedStep.icon, typeof(Sprite), false);
+ if (newIcon != _selectedStep.icon)
+ {
+ Undo.RecordObject(_selectedStep, "Change Icon");
+ _selectedStep.icon = newIcon;
+ _isDirty = true;
+ }
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.Space();
+
+ // Unlocks (dependencies)
+ EditorGUILayout.LabelField("Unlocks", EditorStyles.boldLabel);
+ EditorGUILayout.HelpBox("Steps that will be unlocked when this step is completed", MessageType.Info);
+
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+
+ // Show unlocked steps list
+ if (_selectedStep.unlocks.Count > 0)
+ {
+ for (int i = 0; i < _selectedStep.unlocks.Count; i++)
+ {
+ EditorGUILayout.BeginHorizontal();
+
+ // Draw step selector
+ PuzzleStepSO newUnlockedStep = (PuzzleStepSO)EditorGUILayout.ObjectField(
+ _selectedStep.unlocks[i], typeof(PuzzleStepSO), false);
+
+ if (newUnlockedStep != _selectedStep.unlocks[i])
+ {
+ Undo.RecordObject(_selectedStep, "Change Unlocked Step");
+ _selectedStep.unlocks[i] = newUnlockedStep;
+ _isDirty = true;
+ }
+
+ // Remove button
+ if (GUILayout.Button("-", GUILayout.Width(20)))
+ {
+ Undo.RecordObject(_selectedStep, "Remove Unlocked Step");
+ _selectedStep.unlocks.RemoveAt(i);
+ _isDirty = true;
+ i--;
+ }
+
+ EditorGUILayout.EndHorizontal();
+ }
+ }
+ else
+ {
+ EditorGUILayout.LabelField("No steps will be unlocked");
+ }
+
+ // Add new dependency
+ if (GUILayout.Button("Add Unlocked Step"))
+ {
+ Undo.RecordObject(_selectedStep, "Add Unlocked Step");
+ _selectedStep.unlocks.Add(null);
+ _isDirty = true;
+ }
+
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.Space();
+
+ // Asset path info
+ string assetPath = AssetDatabase.GetAssetPath(_selectedStep);
+ EditorGUILayout.LabelField("Asset Path:", EditorStyles.miniLabel);
+ EditorGUILayout.LabelField(assetPath, EditorStyles.miniLabel);
+
+ // Delete button
+ EditorGUILayout.Space();
+ if (GUILayout.Button("Delete Step", GUILayout.Width(100)))
+ {
+ if (EditorUtility.DisplayDialog("Delete Puzzle Step",
+ $"Are you sure you want to delete '{_selectedStep.displayName}'? This action cannot be undone.",
+ "Delete", "Cancel"))
+ {
+ DeletePuzzleStep(_selectedStep);
+ _selectedStep = null;
+ }
+ }
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ EditorUtility.SetDirty(_selectedStep);
+ }
+
+ EditorGUILayout.EndScrollView();
+ }
+
+ private void DrawCreateNewStepDialog()
+ {
+ // Create a centered window
+ Rect windowRect = new Rect(
+ (position.width - 400) / 2,
+ (position.height - 200) / 2,
+ 400, 200);
+
+ GUI.Box(windowRect, "Create New Puzzle Step", EditorStyles.helpBox);
+ GUILayout.BeginArea(new Rect(windowRect.x + 10, windowRect.y + 30, windowRect.width - 20, windowRect.height - 40));
+
+ // Name field
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Name:", GUILayout.Width(80));
+ _newStepName = EditorGUILayout.TextField(_newStepName);
+ EditorGUILayout.EndHorizontal();
+
+ // Folder selection
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Folder:", GUILayout.Width(80));
+
+ // Create folder dropdown
+ List folderNames = _puzzleStepsByFolder.Keys.ToList();
+ int selectedFolderIndex = folderNames.IndexOf(_selectedFolder);
+ int newSelectedFolderIndex = EditorGUILayout.Popup(selectedFolderIndex >= 0 ? selectedFolderIndex : 0, folderNames.ToArray());
+ if (newSelectedFolderIndex >= 0 && newSelectedFolderIndex < folderNames.Count)
+ {
+ _selectedFolder = folderNames[newSelectedFolderIndex];
+ }
+ else if (folderNames.Count > 0)
+ {
+ _selectedFolder = folderNames[0];
+ }
+
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.Space();
+
+ // Buttons
+ EditorGUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+
+ if (GUILayout.Button("Cancel", GUILayout.Width(100)))
+ {
+ _showCreateNewStepDialog = false;
+ }
+
+ if (GUILayout.Button("Create", GUILayout.Width(100)))
+ {
+ if (!string.IsNullOrEmpty(_newStepName) && !string.IsNullOrEmpty(_selectedFolder))
+ {
+ CreateNewPuzzleStep(_newStepName, _selectedFolder);
+ _showCreateNewStepDialog = false;
+ _newStepName = "New Step";
+ }
+ }
+
+ EditorGUILayout.EndHorizontal();
+
+ GUILayout.EndArea();
+ }
+
+ #endregion
+
+ #region Debug Tab
+
+ private void DrawDebugTab()
+ {
+ if (!_isPlaying)
+ {
+ EditorGUILayout.HelpBox("Enter Play Mode to debug puzzles at runtime", MessageType.Info);
+ return;
+ }
+
+ if (!_hasRuntimeData || _runtimeLevelData == null)
+ {
+ EditorGUILayout.HelpBox("Waiting for puzzle data to be loaded...", MessageType.Info);
+ return;
+ }
+
+ EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
+ EditorGUILayout.LabelField($"Current Level: {_runtimeLevelData.levelId}", EditorStyles.boldLabel);
+ EditorGUILayout.EndHorizontal();
+
+ _debugScrollPosition = EditorGUILayout.BeginScrollView(_debugScrollPosition);
+
+ // List all steps with their current state
+ EditorGUILayout.LabelField("Puzzle Steps", EditorStyles.boldLabel);
+
+ // Show steps directly from the level data in a flat list
+ foreach (var step in _runtimeLevelData.allSteps)
+ {
+ if (step == null) continue;
+
+ DrawRuntimeStepItem(step);
+ }
+
+ EditorGUILayout.EndScrollView();
+ }
+
+ private void DrawRuntimeStepItem(PuzzleStepSO step)
+ {
+ bool isUnlocked = _stepUnlockState.ContainsKey(step.stepId) && _stepUnlockState[step.stepId];
+ bool isCompleted = _stepCompletedState.ContainsKey(step.stepId) && _stepCompletedState[step.stepId];
+
+ // Set background color based on state
+ Color originalColor = GUI.backgroundColor;
+
+ if (isCompleted)
+ GUI.backgroundColor = new Color(0.5f, 1f, 0.5f); // Green for completed
+ else if (isUnlocked)
+ GUI.backgroundColor = new Color(1f, 1f, 0.5f); // Yellow for unlocked but not completed
+ else
+ GUI.backgroundColor = new Color(1f, 0.5f, 0.5f); // Red for locked
+
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+
+ // Reset color
+ GUI.backgroundColor = originalColor;
+
+ EditorGUILayout.BeginHorizontal();
+
+ // Step info
+ EditorGUILayout.BeginVertical(GUILayout.ExpandWidth(true));
+ EditorGUILayout.LabelField(step.displayName, EditorStyles.boldLabel);
+ EditorGUILayout.LabelField(step.stepId, EditorStyles.miniLabel);
+
+ // Status text
+ string statusText = isCompleted ? "Completed" : (isUnlocked ? "Unlocked" : "Locked");
+ EditorGUILayout.LabelField($"Status: {statusText}", EditorStyles.miniLabel);
+
+ EditorGUILayout.EndVertical();
+
+ // Action buttons
+ EditorGUILayout.BeginVertical(GUILayout.Width(100));
+
+ EditorGUI.BeginDisabledGroup(isCompleted);
+ if (GUILayout.Button(isUnlocked ? "Lock" : "Unlock"))
+ {
+ ToggleStepUnlocked(step);
+ }
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUI.BeginDisabledGroup(!isUnlocked || isCompleted);
+ if (GUILayout.Button("Complete"))
+ {
+ CompleteStep(step);
+ }
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUILayout.EndVertical();
+
+ EditorGUILayout.EndHorizontal();
+ EditorGUILayout.EndVertical();
+ }
+
+ #endregion
+
+ #region Data Management
+
+ private void LoadAllPuzzleSteps()
+ {
+ _allPuzzleSteps.Clear();
+ _puzzleStepsByFolder.Clear();
+
+ // Find all PuzzleStepSO assets in the project
+ string[] guids = AssetDatabase.FindAssets("t:PuzzleStepSO");
+
+ foreach (string guid in guids)
+ {
+ string assetPath = AssetDatabase.GUIDToAssetPath(guid);
+
+ // Only include assets from the Data folder
+ if (assetPath.StartsWith(PuzzleDataBasePath))
+ {
+ PuzzleStepSO step = AssetDatabase.LoadAssetAtPath(assetPath);
+
+ if (step != null)
+ {
+ _allPuzzleSteps.Add(step);
+
+ // Add to folder dictionary for organization
+ string folder = Path.GetDirectoryName(assetPath)?.Replace("\\", "/");
+
+ if (folder != null)
+ {
+ if (!_puzzleStepsByFolder.ContainsKey(folder))
+ {
+ _puzzleStepsByFolder[folder] = new List();
+ }
+
+ _puzzleStepsByFolder[folder].Add(step);
+ }
+ }
+ }
+ }
+
+ // Make sure each folder is sorted by name
+ foreach (var key in _puzzleStepsByFolder.Keys.ToList())
+ {
+ _puzzleStepsByFolder[key] = _puzzleStepsByFolder[key]
+ .OrderBy(step => step.displayName)
+ .ToList();
+ }
+
+ _isDirty = false;
+ }
+
+ private void SaveChanges()
+ {
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+ }
+
+ private void CreateNewPuzzleStep(string stepName, string folderPath)
+ {
+ // Create a new PuzzleStepSO
+ PuzzleStepSO newStep = CreateInstance();
+ newStep.stepId = GenerateUniqueStepId(stepName);
+ newStep.displayName = stepName;
+
+ // Create the path
+ string assetPath = Path.Combine(folderPath, $"{stepName}.asset").Replace("\\", "/");
+
+ // Make sure the directory exists
+ string directory = Path.GetDirectoryName(assetPath);
+ if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ // Create the asset
+ AssetDatabase.CreateAsset(newStep, assetPath);
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+
+ // Reload all steps and select the new one
+ LoadAllPuzzleSteps();
+ _selectedStep = newStep;
+ }
+
+ private void DeletePuzzleStep(PuzzleStepSO step)
+ {
+ if (step == null) return;
+
+ string assetPath = AssetDatabase.GetAssetPath(step);
+
+ if (!string.IsNullOrEmpty(assetPath))
+ {
+ // Also need to remove all references to this step from other steps' unlocks lists
+ foreach (var otherStep in _allPuzzleSteps)
+ {
+ if (otherStep != null && otherStep != step)
+ {
+ if (otherStep.unlocks.Contains(step))
+ {
+ Undo.RecordObject(otherStep, "Remove Deleted Step Reference");
+ otherStep.unlocks.Remove(step);
+ EditorUtility.SetDirty(otherStep);
+ }
+ }
+ }
+
+ AssetDatabase.DeleteAsset(assetPath);
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+
+ // Reload all steps
+ LoadAllPuzzleSteps();
+ }
+ }
+
+ private string GenerateUniqueStepId(string baseName)
+ {
+ // Convert to lowercase and replace spaces with underscores
+ string baseId = baseName.ToLower().Replace(" ", "_");
+
+ // Check if this ID already exists
+ bool idExists = _allPuzzleSteps.Any(step => step.stepId == baseId);
+
+ if (!idExists)
+ {
+ return baseId;
+ }
+
+ // Add a number suffix if ID already exists
+ int counter = 1;
+ while (_allPuzzleSteps.Any(step => step.stepId == $"{baseId}_{counter}"))
+ {
+ counter++;
+ }
+
+ return $"{baseId}_{counter}";
+ }
+
+ #endregion
+
+ #region Runtime Debug Helpers
+
+ private void UpdateRuntimeData()
+ {
+ if (!_isPlaying) return;
+
+ // Find PuzzleManager instance
+ PuzzleManager puzzleManager = Object.FindObjectOfType();
+
+ if (puzzleManager == null)
+ {
+ _hasRuntimeData = false;
+ return;
+ }
+
+ // Get current level data
+ var levelData = puzzleManager.GetCurrentLevelData();
+ if (levelData == null)
+ {
+ _hasRuntimeData = false;
+ return;
+ }
+
+ _hasRuntimeData = true;
+ _runtimeLevelData = levelData;
+
+ // Update step states
+ foreach (var step in _runtimeLevelData.allSteps)
+ {
+ if (step != null)
+ {
+ _stepUnlockState[step.stepId] = puzzleManager.IsStepUnlocked(step);
+ _stepCompletedState[step.stepId] = puzzleManager.IsPuzzleStepCompleted(step.stepId);
+ }
+ }
+ }
+
+ private void ToggleStepUnlocked(PuzzleStepSO step)
+ {
+ if (!_isPlaying || step == null) return;
+
+ PuzzleManager puzzleManager = Object.FindObjectOfType();
+ if (puzzleManager == null) return;
+
+ // Get current unlock state
+ bool isCurrentlyUnlocked = _stepUnlockState.ContainsKey(step.stepId) && _stepUnlockState[step.stepId];
+
+ // Call appropriate method using reflection since these might be private methods
+ System.Type managerType = puzzleManager.GetType();
+
+ if (isCurrentlyUnlocked)
+ {
+ // Find the LockStep method that takes a PuzzleStepSO parameter
+ System.Reflection.MethodInfo lockMethod = managerType.GetMethod("LockStep",
+ System.Reflection.BindingFlags.Instance |
+ System.Reflection.BindingFlags.Public |
+ System.Reflection.BindingFlags.NonPublic);
+
+ if (lockMethod != null)
+ {
+ lockMethod.Invoke(puzzleManager, new object[] { step });
+ }
+ }
+ else
+ {
+ // Find the UnlockStep method that takes a PuzzleStepSO parameter
+ System.Reflection.MethodInfo unlockMethod = managerType.GetMethod("UnlockStep",
+ System.Reflection.BindingFlags.Instance |
+ System.Reflection.BindingFlags.Public |
+ System.Reflection.BindingFlags.NonPublic);
+
+ if (unlockMethod != null)
+ {
+ unlockMethod.Invoke(puzzleManager, new object[] { step });
+ }
+ }
+
+ // Update state
+ UpdateRuntimeData();
+ }
+
+ private void CompleteStep(PuzzleStepSO step)
+ {
+ if (!_isPlaying || step == null) return;
+
+ PuzzleManager puzzleManager = Object.FindObjectOfType();
+ if (puzzleManager == null) return;
+
+ // Complete the step
+ puzzleManager.MarkPuzzleStepCompleted(step);
+
+ // Update state
+ UpdateRuntimeData();
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/Editor/PuzzleSystem/PuzzleEditorWindow.cs.meta b/Assets/Editor/PuzzleSystem/PuzzleEditorWindow.cs.meta
new file mode 100644
index 00000000..9eccad68
--- /dev/null
+++ b/Assets/Editor/PuzzleSystem/PuzzleEditorWindow.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: c733f3c6624e4e5486c07abfe4fab81e
+timeCreated: 1760539457
\ No newline at end of file
diff --git a/Assets/Prefabs/Environment/Placeholders/BallTree.prefab b/Assets/Prefabs/Environment/Placeholders/BallTree.prefab
index f49f6fc6..918b50fa 100644
--- a/Assets/Prefabs/Environment/Placeholders/BallTree.prefab
+++ b/Assets/Prefabs/Environment/Placeholders/BallTree.prefab
@@ -26,7 +26,7 @@ Transform:
m_GameObject: {fileID: 1646387898454772943}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
- m_LocalPosition: {x: -4.4615383, y: 0.61538696, z: 0}
+ m_LocalPosition: {x: -1.854, y: -0, z: 0}
m_LocalScale: {x: 0.7692308, y: 0.7692308, z: 0.7692308}
m_ConstrainProportionsScale: 0
m_Children: []
@@ -46,6 +46,38 @@ MonoBehaviour:
m_EditorClassIdentifier: AppleHillsScripts::Interactions.CharacterMoveToTarget
characterType: 2
positionOffset: {x: 0, y: 0, z: 1}
+--- !u!1 &2654542252039360806
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2084964592986606867}
+ m_Layer: 10
+ m_Name: AnimContainer
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2084964592986606867
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2654542252039360806}
+ serializedVersion: 2
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0.826, y: 1.333, z: 0}
+ m_LocalScale: {x: 0.1851852, y: 0.1851852, z: 0.1851852}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 305861995533261809}
+ m_Father: {fileID: 4937390562043858043}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &3591802784221671576
GameObject:
m_ObjectHideFlags: 0
@@ -72,7 +104,7 @@ Transform:
m_GameObject: {fileID: 3591802784221671576}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
- m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalPosition: {x: 0.9, y: -0.16, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children: []
@@ -124,10 +156,10 @@ PolygonCollider2D:
m_AutoTiling: 0
m_Points:
m_Paths:
- - - {x: 0.69036514, y: -0.039288044}
- - {x: -0.7429288, y: 0.004180908}
- - {x: -0.73904544, y: 2.4819212}
- - {x: 0.5286481, y: 2.5456142}
+ - - {x: 0.5431701, y: -0.027021673}
+ - {x: -0.62026507, y: -0.020351835}
+ - {x: -0.48145303, y: 1.4024833}
+ - {x: 0.36918524, y: 1.3803146}
m_UseDelaunayMesh: 0
--- !u!1 &7379304988657006554
GameObject:
@@ -144,7 +176,8 @@ GameObject:
- component: {fileID: 492578671844741631}
- component: {fileID: 8984729148657672365}
- component: {fileID: 1569498917964935965}
- - component: {fileID: 3871210969445384207}
+ - component: {fileID: 6417332830266550134}
+ - component: {fileID: 4544320034237251646}
m_Layer: 10
m_Name: BallTree
m_TagString: Untagged
@@ -162,9 +195,10 @@ Transform:
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
- m_LocalScale: {x: 1.3, y: 1.3, z: 1.3}
+ m_LocalScale: {x: 2.7, y: 2.7, z: 2.7}
m_ConstrainProportionsScale: 1
m_Children:
+ - {fileID: 2084964592986606867}
- {fileID: 6631072601870453588}
- {fileID: 7371967679236352629}
m_Father: {fileID: 0}
@@ -216,7 +250,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
- m_Sprite: {fileID: 4974115186881715698, guid: e1aa947fcf3609045ba89a6ddb609ae3, type: 3}
+ m_Sprite: {fileID: 768869974157336279, guid: ab1e8f5d45ee00247af9314b56f4af91, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
@@ -261,17 +295,17 @@ BoxCollider2D:
m_UsedByEffector: 0
m_CompositeOperation: 0
m_CompositeOrder: 0
- m_Offset: {x: -0.1844703, y: 2.8477936}
+ m_Offset: {x: 0.76, y: 2.88}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
- pivot: {x: 0.5, y: 0.08}
- oldSize: {x: 9.23, y: 11.48}
+ pivot: {x: 0.5, y: 0.2}
+ oldSize: {x: 14.969999, y: 9.060193}
newSize: {x: 9.23, y: 11.48}
adaptiveTilingThreshold: 0.5
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
- m_Size: {x: 3.437712, y: 7.532383}
+ m_Size: {x: 3.8, y: 7.34}
m_EdgeRadius: 0
--- !u!114 &8437452310832126615
MonoBehaviour:
@@ -313,7 +347,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
stepData: {fileID: 11400000, guid: 8ac614a698631554ab8ac39aed04a189, type: 2}
- puzzleIndicator: {fileID: 0}
+ puzzleIndicator: {fileID: 214996082823304376}
drawPromptRangeGizmo: 1
--- !u!114 &8984729148657672365
MonoBehaviour:
@@ -327,18 +361,20 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 42e77a0c97604b6eb7674e58726c831a, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::Interactions.InteractionTimelineAction
- respondToEvents: 04000000
+ respondToEvents: 02000000
pauseInteractionFlow: 1
- playableDirector: {fileID: 0}
+ playableDirector: {fileID: 1569498917964935965}
timelineMappings:
- - eventType: 4
- timelines: []
+ - eventType: 2
+ timelines:
+ - {fileID: 11400000, guid: 1791fd5a24a3142418ed441a2a25b374, type: 2}
+ - {fileID: 11400000, guid: ee609df51f47bd541a23d5425e289e30, type: 2}
bindPlayerCharacter: 0
bindPulverCharacter: 0
- playerTrackName:
- pulverTrackName:
+ playerTrackName: Player
+ pulverTrackName: Pulver
timeoutSeconds: 30
- loopLast: 0
+ loopLast: 1
loopAll: 0
--- !u!320 &1569498917964935965
PlayableDirector:
@@ -349,21 +385,43 @@ PlayableDirector:
m_GameObject: {fileID: 7379304988657006554}
m_Enabled: 1
serializedVersion: 3
- m_PlayableAsset: {fileID: 11400000, guid: dd9566026364e814a8dad109e6c365ca, type: 2}
+ m_PlayableAsset: {fileID: 11400000, guid: ee609df51f47bd541a23d5425e289e30, type: 2}
m_InitialState: 0
m_WrapMode: 2
m_DirectorUpdateMode: 1
m_InitialTime: 0
m_SceneBindings:
- key: {fileID: -7584736085941489071, guid: dd9566026364e814a8dad109e6c365ca, type: 2}
- value: {fileID: 3871210969445384207}
+ value: {fileID: 0}
- key: {fileID: -2395336864975438248, guid: dd9566026364e814a8dad109e6c365ca, type: 2}
value: {fileID: 0}
- key: {fileID: -7231857257271738743, guid: dd9566026364e814a8dad109e6c365ca, type: 2}
value: {fileID: 0}
+ - key: {fileID: -7584736085941489071, guid: 1791fd5a24a3142418ed441a2a25b374, type: 2}
+ value: {fileID: 4544320034237251646}
+ - key: {fileID: -2395336864975438248, guid: 1791fd5a24a3142418ed441a2a25b374, type: 2}
+ value: {fileID: 0}
+ - key: {fileID: 3942302933360259000, guid: 1791fd5a24a3142418ed441a2a25b374, type: 2}
+ value: {fileID: 0}
+ - key: {fileID: -2395336864975438248, guid: ee609df51f47bd541a23d5425e289e30, type: 2}
+ value: {fileID: 0}
+ - key: {fileID: -7584736085941489071, guid: ee609df51f47bd541a23d5425e289e30, type: 2}
+ value: {fileID: 4544320034237251646}
m_ExposedReferences:
m_References: []
---- !u!95 &3871210969445384207
+--- !u!114 &6417332830266550134
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7379304988657006554}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 833a4ccef651449e973e623d9107bef5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!95 &4544320034237251646
Animator:
serializedVersion: 7
m_ObjectHideFlags: 0
@@ -385,3 +443,90 @@ Animator:
m_AllowConstantClipSamplingOptimization: 1
m_KeepAnimatorStateOnDisable: 0
m_WriteDefaultValuesOnDisable: 0
+--- !u!1001 &5210361341888085501
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 2084964592986606867}
+ m_Modifications:
+ - target: {fileID: 5383276844808284485, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_Name
+ value: NextStepIndicator
+ objectReference: {fileID: 0}
+ - target: {fileID: 5383276844808284485, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_IsActive
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalScale.x
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalScale.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalScale.z
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalRotation.x
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalRotation.y
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalRotation.z
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ propertyPath: m_ConstrainProportionsScale
+ value: 1
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+--- !u!1 &214996082823304376 stripped
+GameObject:
+ m_CorrespondingSourceObject: {fileID: 5383276844808284485, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ m_PrefabInstance: {fileID: 5210361341888085501}
+ m_PrefabAsset: {fileID: 0}
+--- !u!4 &305861995533261809 stripped
+Transform:
+ m_CorrespondingSourceObject: {fileID: 5507990123417429516, guid: afbb486e5456a20479aee4cf8bc949b6, type: 3}
+ m_PrefabInstance: {fileID: 5210361341888085501}
+ m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Prefabs/Managers/SceneManager.prefab b/Assets/Prefabs/Managers/SceneManager.prefab
index d28c028c..7c1883c3 100644
--- a/Assets/Prefabs/Managers/SceneManager.prefab
+++ b/Assets/Prefabs/Managers/SceneManager.prefab
@@ -30,7 +30,7 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- - {fileID: 7090108953567368886}
+ - {fileID: 4689617562113187593}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5327225408302228741
@@ -45,8 +45,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 360f320f4d7a48e38f5fd7cdfa28144a, type: 3}
m_Name:
m_EditorClassIdentifier:
- loadingScreen: {fileID: 3391437592962192360}
---- !u!1001 &6967569849783118800
+--- !u!1001 &4881964705042195055
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
@@ -138,32 +137,13 @@ PrefabInstance:
propertyPath: m_Name
value: LoadingScreen
objectReference: {fileID: 0}
- - target: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
- propertyPath: m_IsActive
- value: 1
- objectReference: {fileID: 0}
- - target: {fileID: 5737877680156686392, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
- propertyPath: minimumDisplayTime
- value: 1
- objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
---- !u!114 &3391437592962192360 stripped
-MonoBehaviour:
- m_CorrespondingSourceObject: {fileID: 5737877680156686392, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
- m_PrefabInstance: {fileID: 6967569849783118800}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 0}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 1494b10574e74acd880f9101b4248239, type: 3}
- m_Name:
- m_EditorClassIdentifier: AppleHillsScripts::UI.LoadingScreenController
---- !u!224 &7090108953567368886 stripped
+--- !u!224 &4689617562113187593 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
- m_PrefabInstance: {fileID: 6967569849783118800}
+ m_PrefabInstance: {fileID: 4881964705042195055}
m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Prefabs/UI/LoadingScreen.prefab b/Assets/Prefabs/UI/LoadingScreen.prefab
index ddbfae6f..cfd531d9 100644
--- a/Assets/Prefabs/UI/LoadingScreen.prefab
+++ b/Assets/Prefabs/UI/LoadingScreen.prefab
@@ -229,9 +229,7 @@ MonoBehaviour:
m_EditorClassIdentifier: AppleHillsScripts::UI.LoadingScreenController
loadingScreenContainer: {fileID: 7270617579256400696}
progressBarImage: {fileID: 1674678211233966532}
- minimumDisplayTime: 2
- animateProgressBar: 1
- progressBarSmoothTime: 0.1
+ minimumDisplayTime: 1
progressUpdateInterval: 0.1
--- !u!1 &6888795583318782279
GameObject:
diff --git a/Assets/Scenes/StartingScene.unity b/Assets/Scenes/StartingScene.unity
new file mode 100644
index 00000000..fd4cd910
--- /dev/null
+++ b/Assets/Scenes/StartingScene.unity
@@ -0,0 +1,452 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 10
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 3
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 13
+ m_BakeOnSceneLoad: 0
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 0
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 12
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_AtlasSize: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_ExtractAmbientOcclusion: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 1
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 512
+ m_PVRBounces: 2
+ m_PVREnvironmentSampleCount: 256
+ m_PVREnvironmentReferencePointCount: 2048
+ m_PVRFilteringMode: 1
+ m_PVRDenoiserTypeDirect: 1
+ m_PVRDenoiserTypeIndirect: 1
+ m_PVRDenoiserTypeAO: 1
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVREnvironmentMIS: 1
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 1
+ m_PVRFilteringGaussRadiusAO: 1
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ExportTrainingData: 0
+ m_TrainingDataDestination: TrainingData
+ m_LightProbeSampleCountMultiplier: 4
+ m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 3
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ buildHeightMesh: 0
+ maxJobWorkers: 0
+ preserveTilesOutsideBounds: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1001 &180679694
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_Pivot.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_Pivot.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_AnchorMax.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_AnchorMax.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_AnchorMin.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_AnchorMin.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_SizeDelta.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_SizeDelta.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_AnchoredPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_AnchoredPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 204042265062571366, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ propertyPath: m_Name
+ value: LoadingScreen
+ objectReference: {fileID: 0}
+ m_RemovedComponents:
+ - {fileID: 5737877680156686392, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents:
+ - targetCorrespondingSourceObject: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ insertIndex: -1
+ addedObject: {fileID: 180679698}
+ m_SourcePrefab: {fileID: 100100000, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+--- !u!1 &180679695 stripped
+GameObject:
+ m_CorrespondingSourceObject: {fileID: 4869161796575291839, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ m_PrefabInstance: {fileID: 180679694}
+ m_PrefabAsset: {fileID: 0}
+--- !u!114 &180679696 stripped
+MonoBehaviour:
+ m_CorrespondingSourceObject: {fileID: 1674678211233966532, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ m_PrefabInstance: {fileID: 180679694}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
+--- !u!1 &180679697 stripped
+GameObject:
+ m_CorrespondingSourceObject: {fileID: 7270617579256400696, guid: 19fad826fce26d34ba304620216a7f47, type: 3}
+ m_PrefabInstance: {fileID: 180679694}
+ m_PrefabAsset: {fileID: 0}
+--- !u!114 &180679698
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 180679695}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 8968b564891a474baae157792b88e75f, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: AppleHillsScripts::Bootstrap.InitialLoadingScreen
+ loadingScreenContainer: {fileID: 180679697}
+ progressBarImage: {fileID: 180679696}
+ minimumDisplayTime: 1
+ progressUpdateInterval: 0.1
+--- !u!1 &400217123
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 400217126}
+ - component: {fileID: 400217125}
+ - component: {fileID: 400217124}
+ - component: {fileID: 400217127}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &400217124
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 400217123}
+ m_Enabled: 1
+--- !u!20 &400217125
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 400217123}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_Iso: 200
+ m_ShutterSpeed: 0.005
+ m_Aperture: 16
+ m_FocusDistance: 10
+ m_FocalLength: 50
+ m_BladeCount: 5
+ m_Curvature: {x: 2, y: 11}
+ m_BarrelClipping: 0.25
+ m_Anamorphism: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 1
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &400217126
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 400217123}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: -10}
+ 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 &400217127
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 400217123}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData
+ m_RenderShadows: 1
+ m_RequiresDepthTextureOption: 2
+ m_RequiresOpaqueTextureOption: 2
+ m_CameraType: 0
+ m_Cameras: []
+ m_RendererIndex: -1
+ m_VolumeLayerMask:
+ serializedVersion: 2
+ m_Bits: 1
+ m_VolumeTrigger: {fileID: 0}
+ m_VolumeFrameworkUpdateModeOption: 2
+ m_RenderPostProcessing: 0
+ m_Antialiasing: 0
+ m_AntialiasingQuality: 2
+ m_StopNaN: 0
+ m_Dithering: 0
+ m_ClearDepth: 1
+ m_AllowXRRendering: 1
+ m_AllowHDROutput: 1
+ m_UseScreenCoordOverride: 0
+ m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
+ m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
+ m_RequiresDepthTexture: 0
+ m_RequiresColorTexture: 0
+ m_TaaSettings:
+ m_Quality: 3
+ m_FrameInfluence: 0.1
+ m_JitterScale: 1
+ m_MipBias: 0
+ m_VarianceClampScale: 0.9
+ m_ContrastAdaptiveSharpening: 0
+ m_Version: 2
+--- !u!1 &1710655392
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1710655394}
+ - component: {fileID: 1710655393}
+ m_Layer: 0
+ m_Name: BootSceneController
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1710655393
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1710655392}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fdb797d6fcdc469bb9bfb9ad3c5f51b5, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: AppleHillsScripts::Bootstrap.BootSceneController
+ mainMenuSceneName: MainMenu
+ minDelayAfterBoot: 0.5
+ debugMode: 0
+ initialLoadingScreen: {fileID: 180679698}
+ bootProgressWeight: 0.5
+--- !u!4 &1710655394
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1710655392}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: -39.55614, y: 4.31573, 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!1660057539 &9223372036854775807
+SceneRoots:
+ m_ObjectHideFlags: 0
+ m_Roots:
+ - {fileID: 400217126}
+ - {fileID: 1710655394}
+ - {fileID: 180679694}
diff --git a/Assets/Scenes/StartingScene.unity.meta b/Assets/Scenes/StartingScene.unity.meta
new file mode 100644
index 00000000..7ba42526
--- /dev/null
+++ b/Assets/Scenes/StartingScene.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: cf01e2d0135b06c4486d00ef393d0274
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Bootstrap/BootCompletionService.cs b/Assets/Scripts/Bootstrap/BootCompletionService.cs
new file mode 100644
index 00000000..c74f6c4e
--- /dev/null
+++ b/Assets/Scripts/Bootstrap/BootCompletionService.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace Bootstrap
+{
+ ///
+ /// Service that provides notification and management of boot completion status.
+ /// Allows systems to subscribe to boot completion events, register initialization actions with priorities,
+ /// or await boot completion asynchronously.
+ ///
+ public static class BootCompletionService
+ {
+ ///
+ /// Indicates if the boot process has completed
+ ///
+ public static bool IsBootComplete { get; private set; } = false;
+
+ ///
+ /// Event triggered when boot completes
+ ///
+ public static event Action OnBootComplete;
+
+ ///
+ /// Represents an initialization action with priority
+ ///
+ private class InitializationAction
+ {
+ public Action Action { get; }
+ public int Priority { get; }
+ public string Name { get; }
+
+ public InitializationAction(Action action, int priority, string name)
+ {
+ Action = action;
+ Priority = priority;
+ Name = name;
+ }
+ }
+
+ // List of initialization actions to be executed once boot completes
+ private static List _initializationActions = new List();
+
+ // TaskCompletionSource for async await pattern
+ private static TaskCompletionSource _bootCompletionTask = new TaskCompletionSource();
+
+ ///
+ /// Called by CustomBoot when the boot process is complete
+ ///
+ internal static void HandleBootCompleted()
+ {
+ if (IsBootComplete)
+ return;
+
+ IsBootComplete = true;
+
+ Debug.Log("[BootCompletionService] Boot process completed, executing initialization actions");
+
+ // Execute initialization actions in priority order (lower number = higher priority)
+ ExecuteInitializationActions();
+
+ // Trigger the event
+ OnBootComplete?.Invoke();
+
+ // Complete the task for async waiters
+ _bootCompletionTask.TrySetResult(true);
+
+ Debug.Log("[BootCompletionService] All boot completion handlers executed");
+ }
+
+ ///
+ /// Register an action to be executed when boot completes.
+ /// Lower priority numbers run first.
+ ///
+ /// The action to execute
+ /// Priority (lower numbers run first)
+ /// Name for debugging
+ public static void RegisterInitAction(Action action, int priority = 100, string name = null)
+ {
+ if (action == null)
+ return;
+
+ if (string.IsNullOrEmpty(name))
+ name = $"Action_{_initializationActions.Count}";
+
+ var initAction = new InitializationAction(action, priority, name);
+
+ if (IsBootComplete)
+ {
+ // If boot is already complete, execute immediately
+ Debug.Log($"[BootCompletionService] Executing late registration: {name} (Priority: {priority})");
+ try
+ {
+ action();
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"[BootCompletionService] Error executing init action '{name}': {ex}");
+ }
+ }
+ else
+ {
+ // Otherwise add to the queue
+ _initializationActions.Add(initAction);
+ Debug.Log($"[BootCompletionService] Registered init action: {name} (Priority: {priority})");
+ }
+ }
+
+ ///
+ /// Wait asynchronously for boot completion
+ ///
+ /// Task that completes when boot is complete
+ public static Task WaitForBootCompletionAsync()
+ {
+ if (IsBootComplete)
+ return Task.CompletedTask;
+
+ return _bootCompletionTask.Task;
+ }
+
+ ///
+ /// Execute all registered initialization actions in priority order
+ ///
+ private static void ExecuteInitializationActions()
+ {
+ // Sort by priority (lowest first)
+ var sortedActions = _initializationActions
+ .OrderBy(a => a.Priority)
+ .ToList();
+
+ foreach (var action in sortedActions)
+ {
+ try
+ {
+ Debug.Log($"[BootCompletionService] Executing: {action.Name} (Priority: {action.Priority})");
+ action.Action();
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"[BootCompletionService] Error executing init action '{action.Name}': {ex}");
+ }
+ }
+
+ // Clear the list after execution
+ _initializationActions.Clear();
+ }
+ }
+}
diff --git a/Assets/Scripts/Bootstrap/BootCompletionService.cs.meta b/Assets/Scripts/Bootstrap/BootCompletionService.cs.meta
new file mode 100644
index 00000000..2f02bd4f
--- /dev/null
+++ b/Assets/Scripts/Bootstrap/BootCompletionService.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: aa0228cf33a64515bc166b7a9bc8c0b9
+timeCreated: 1760606319
\ No newline at end of file
diff --git a/Assets/Scripts/Bootstrap/BootSceneController.cs b/Assets/Scripts/Bootstrap/BootSceneController.cs
new file mode 100644
index 00000000..eba66b8b
--- /dev/null
+++ b/Assets/Scripts/Bootstrap/BootSceneController.cs
@@ -0,0 +1,269 @@
+using System;
+using UnityEngine;
+using UI;
+using Core;
+using UnityEngine.SceneManagement;
+using Cinematics;
+
+namespace Bootstrap
+{
+ ///
+ /// Controller for the boot scene that coordinates bootstrap initialization with loading screen
+ ///
+ public class BootSceneController : MonoBehaviour
+ {
+ [SerializeField] private string mainMenuSceneName = "MainMenu";
+ [SerializeField] private float minDelayAfterBoot = 0.5f; // Small delay after boot to ensure smooth transition
+ [SerializeField] private bool debugMode = false;
+ [SerializeField] private InitialLoadingScreen initialLoadingScreen; // Reference to our specialized loading screen
+
+ // Progress distribution between bootstrap and scene loading
+ [SerializeField, Range(0.1f, 0.9f)] private float bootProgressWeight = 0.5f; // Default 50/50 split
+
+ private enum LoadingPhase { Bootstrap, SceneLoading }
+ private LoadingPhase _currentPhase = LoadingPhase.Bootstrap;
+
+ private bool _bootComplete = false;
+ private bool _hasStartedLoading = false;
+ private float _sceneLoadingProgress = 0f;
+
+ private void Start()
+ {
+ Debug.Log("[BootSceneController] Boot scene started");
+
+ // Ensure the initial loading screen exists
+ if (initialLoadingScreen == null)
+ {
+ Debug.LogError("[BootSceneController] No InitialLoadingScreen assigned! Please assign it in the inspector.");
+ return;
+ }
+
+ // Subscribe to the loading screen completion event
+ initialLoadingScreen.OnLoadingScreenFullyHidden += OnInitialLoadingComplete;
+
+ // Show the loading screen immediately with our combined progress provider
+ initialLoadingScreen.ShowLoadingScreen(GetCombinedProgress);
+
+ // Subscribe to boot progress events
+ CustomBoot.OnBootProgressChanged += OnBootProgressChanged;
+
+ // Register our boot completion handler with the BootCompletionService
+ // This will execute either immediately if boot is already complete,
+ // or when the boot process completes
+ BootCompletionService.RegisterInitAction(
+ OnBootCompleted,
+ 50, // Higher priority (lower number)
+ "BootSceneController.OnBootCompleted"
+ );
+
+ // In debug mode, log additional information
+ if (debugMode)
+ {
+ InvokeRepeating(nameof(LogDebugInfo), 0.1f, 0.5f);
+ }
+ }
+
+ ///
+ /// Called when the initial loading screen is fully hidden
+ ///
+ private void OnInitialLoadingComplete()
+ {
+ Debug.Log("[BootSceneController] Initial loading screen fully hidden, boot sequence completed");
+
+ // Play the intro cinematic if available
+ if (CinematicsManager.Instance != null)
+ {
+ Debug.Log("[BootSceneController] Attempting to play intro cinematic");
+
+ // Use LoadAndPlayCinematic to play the intro sequence
+ CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence");
+
+ // Immediately unload the StartingScene - no need to wait for cinematic to finish
+ // since CinematicsManager is bootstrapped and won't be unloaded
+ UnloadStartingScene();
+ }
+ else
+ {
+ // If no cinematics manager, unload the StartingScene directly
+ UnloadStartingScene();
+ }
+ }
+
+ private void OnDestroy()
+ {
+ // Clean up event subscriptions
+ CustomBoot.OnBootCompleted -= OnBootCompleted;
+ CustomBoot.OnBootProgressChanged -= OnBootProgressChanged;
+
+ if (initialLoadingScreen != null)
+ {
+ initialLoadingScreen.OnLoadingScreenFullyHidden -= OnInitialLoadingComplete;
+ }
+
+ if (debugMode)
+ {
+ CancelInvoke(nameof(LogDebugInfo));
+ }
+ }
+
+ ///
+ /// Progress provider that combines bootstrap and scene loading progress
+ ///
+ private float GetCombinedProgress()
+ {
+ switch (_currentPhase)
+ {
+ case LoadingPhase.Bootstrap:
+ // Scale bootstrap progress from 0 to bootProgressWeight
+ return CustomBoot.CurrentProgress * bootProgressWeight;
+
+ case LoadingPhase.SceneLoading:
+ // Scale scene loading progress from bootProgressWeight to 1.0
+ return bootProgressWeight + (_sceneLoadingProgress * (1f - bootProgressWeight));
+
+ default:
+ return 0f;
+ }
+ }
+
+ private void OnBootProgressChanged(float progress)
+ {
+ if (debugMode)
+ {
+ Debug.Log($"[BootSceneController] Bootstrap progress: {progress:P0}, Combined: {GetCombinedProgress():P0}");
+ }
+ }
+
+ private void LogDebugInfo()
+ {
+ Debug.Log($"[BootSceneController] Debug - Phase: {_currentPhase}, Bootstrap: {CustomBoot.CurrentProgress:P0}, " +
+ $"Scene: {_sceneLoadingProgress:P0}, Combined: {GetCombinedProgress():P0}, Boot Complete: {_bootComplete}");
+ }
+
+ private void OnBootCompleted()
+ {
+ // Unsubscribe to prevent duplicate calls
+ CustomBoot.OnBootCompleted -= OnBootCompleted;
+
+ Debug.Log("[BootSceneController] Boot process completed");
+ _bootComplete = true;
+
+ // After a small delay, start loading the main menu
+ // This prevents jerky transitions if boot happens very quickly
+ Invoke(nameof(StartLoadingMainMenu), minDelayAfterBoot);
+ }
+
+ private void StartLoadingMainMenu()
+ {
+ if (_hasStartedLoading)
+ return;
+
+ _hasStartedLoading = true;
+ _currentPhase = LoadingPhase.SceneLoading;
+ LoadMainMenu();
+ }
+
+ private async void LoadMainMenu()
+ {
+ Debug.Log($"[BootSceneController] Loading main menu scene: {mainMenuSceneName}");
+
+ try
+ {
+ // Initialize scene loading progress to 0 to ensure proper remapping
+ _sceneLoadingProgress = 0f;
+
+ // Create a custom progress reporter using a custom class
+ var progressHandler = new ProgressHandler(value => {
+ // Store the raw scene loading progress (0-1)
+ _sceneLoadingProgress = value;
+
+ if (debugMode)
+ {
+ Debug.Log($"[BootSceneController] Scene loading raw: {value:P0}, Combined: {GetCombinedProgress():P0}");
+ }
+ });
+
+ // Step 1: Additively load the main menu scene - don't unload StartingScene yet
+ var op = SceneManager.LoadSceneAsync(mainMenuSceneName, LoadSceneMode.Additive);
+
+ // Disable scene activation until we're ready to show it
+ op.allowSceneActivation = true;
+
+ // Track progress while loading
+ while (!op.isDone)
+ {
+ progressHandler.ReportProgress(op.progress);
+ await System.Threading.Tasks.Task.Yield();
+ }
+
+ // Update the current gameplay scene in SceneManagerService
+ SceneManagerService.Instance.CurrentGameplayScene = mainMenuSceneName;
+
+ // Ensure progress is complete
+ _sceneLoadingProgress = 1f;
+
+ // Step 2: Scene is fully loaded, now hide the loading screen
+ // This will trigger OnInitialLoadingComplete via the event when animation completes
+ initialLoadingScreen.HideLoadingScreen();
+
+ // Step 3: The OnInitialLoadingComplete method will handle playing the intro cinematic
+ // Step 4: StartingScene will be unloaded after the cinematic completes in OnIntroCinematicFinished
+ }
+ catch (Exception e)
+ {
+ Debug.LogError($"[BootSceneController] Error loading main menu: {e.Message}");
+ // Still try to hide the loading screen even if there was an error
+ initialLoadingScreen.HideLoadingScreen();
+ }
+ }
+
+ ///
+ /// Unloads the StartingScene, completing the transition to the main menu
+ ///
+ private async void UnloadStartingScene()
+ {
+ try
+ {
+ // Get the current scene (StartingScene)
+ Scene currentScene = SceneManager.GetActiveScene();
+ string startingSceneName = currentScene.name;
+
+ Debug.Log($"[BootSceneController] Unloading StartingScene: {startingSceneName}");
+
+ // Unload the StartingScene
+ await SceneManager.UnloadSceneAsync(startingSceneName);
+
+ // Set the main menu scene as the active scene
+ Scene mainMenuScene = SceneManager.GetSceneByName(mainMenuSceneName);
+ SceneManager.SetActiveScene(mainMenuScene);
+
+ Debug.Log($"[BootSceneController] Transition complete: {startingSceneName} unloaded, {mainMenuSceneName} is now active");
+
+ // Destroy the boot scene controller since its job is done
+ Destroy(gameObject);
+ }
+ catch (Exception e)
+ {
+ Logging.Warning($"[BootSceneController] Error unloading StartingScene: {e.Message}");
+ }
+ }
+
+ ///
+ /// Helper class to handle progress reporting without running into explicit interface implementation issues
+ ///
+ private class ProgressHandler
+ {
+ private Action _progressAction;
+
+ public ProgressHandler(Action progressAction)
+ {
+ _progressAction = progressAction;
+ }
+
+ public void ReportProgress(float value)
+ {
+ _progressAction?.Invoke(value);
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/Bootstrap/BootSceneController.cs.meta b/Assets/Scripts/Bootstrap/BootSceneController.cs.meta
new file mode 100644
index 00000000..3ddffcb9
--- /dev/null
+++ b/Assets/Scripts/Bootstrap/BootSceneController.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: fdb797d6fcdc469bb9bfb9ad3c5f51b5
+timeCreated: 1760604860
\ No newline at end of file
diff --git a/Assets/Scripts/Bootstrap/CustomBoot.cs b/Assets/Scripts/Bootstrap/CustomBoot.cs
index 54b5eeb4..6ff31ccc 100644
--- a/Assets/Scripts/Bootstrap/CustomBoot.cs
+++ b/Assets/Scripts/Bootstrap/CustomBoot.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
@@ -14,9 +15,24 @@ namespace Bootstrap
/// Current initialisation status
///
public static bool Initialised { get; private set; }
+
+ ///
+ /// Event triggered when boot progress changes
+ ///
+ public static event Action OnBootProgressChanged;
+
+ ///
+ /// Event triggered when boot process completes
+ ///
+ public static event Action OnBootCompleted;
+
+ ///
+ /// Current progress of the boot process (0-1)
+ ///
+ public static float CurrentProgress { get; private set; }
///
- // Called as soon as the game begins
+ /// Called as soon as the game begins
///
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
private static void Initialise()
@@ -32,6 +48,9 @@ namespace Bootstrap
///
public static void PerformInitialisation()
{
+ //Reset progress
+ CurrentProgress = 0f;
+
//In editor, perform initialisation synchronously
if (Application.isEditor)
{
@@ -72,6 +91,17 @@ namespace Bootstrap
{
await LoadCustomBootSettings();
Initialised = true;
+ CurrentProgress = 1f;
+ OnBootProgressChanged?.Invoke(1f);
+ OnBootCompleted?.Invoke();
+
+ // Notify the BootCompletionService that boot is complete
+ if (Application.isPlaying)
+ {
+ // Direct call to boot completion service
+ Debug.Log("[CustomBoot] Calling BootCompletionService.HandleBootCompleted()");
+ BootCompletionService.HandleBootCompleted();
+ }
}
///
@@ -81,6 +111,17 @@ namespace Bootstrap
{
LoadCustomBootSettingsSync();
Initialised = true;
+ CurrentProgress = 1f;
+ OnBootProgressChanged?.Invoke(1f);
+ OnBootCompleted?.Invoke();
+
+ // Notify the BootCompletionService that boot is complete
+ if (Application.isPlaying)
+ {
+ // Direct call to boot completion service
+ Debug.Log("[CustomBoot] Calling BootCompletionService.HandleBootCompleted()");
+ BootCompletionService.HandleBootCompleted();
+ }
}
@@ -177,5 +218,16 @@ namespace Bootstrap
result.InitialiseSync();
return handle;
}
+
+ ///
+ /// Updates the current progress value and triggers the progress event
+ ///
+ /// Progress value between 0-1
+ internal static void UpdateProgress(float progress)
+ {
+ CurrentProgress = Mathf.Clamp01(progress);
+ OnBootProgressChanged?.Invoke(CurrentProgress);
+ Debug.Log($"[CustomBoot] Progress: {CurrentProgress:P0}");
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Bootstrap/CustomBootSettings.cs b/Assets/Scripts/Bootstrap/CustomBootSettings.cs
index ffd2293f..03531f86 100644
--- a/Assets/Scripts/Bootstrap/CustomBootSettings.cs
+++ b/Assets/Scripts/Bootstrap/CustomBootSettings.cs
@@ -31,15 +31,37 @@ namespace Bootstrap
RuntimeContainer = new GameObject($"{name}_Container");
DontDestroyOnLoad(RuntimeContainer);
Instances = new GameObject[BootPrefabs.Length];
+
+ // Calculate total prefabs for progress tracking
+ int totalPrefabs = BootPrefabs.Length;
+ float progressPerPrefab = totalPrefabs > 0 ? 1f / totalPrefabs : 1f;
+ float currentProgress = 0f;
+
for (var i = 0; i < BootPrefabs.Length; i++)
{
- if (!BootPrefabs[i]) continue;
+ if (!BootPrefabs[i])
+ {
+ // Report incremental progress even for null prefabs
+ currentProgress = (i + 1) * progressPerPrefab;
+ CustomBoot.UpdateProgress(currentProgress);
+ continue;
+ }
var instance = GameObject.InstantiateAsync(BootPrefabs[i], RuntimeContainer.transform);
while (!instance.isDone)
+ {
+ // Report partial progress while waiting
+ float progressInStep = instance.progress * progressPerPrefab;
+ float overallProgress = i * progressPerPrefab + progressInStep;
+ CustomBoot.UpdateProgress(overallProgress);
await Task.Yield();
+ }
Instances[i] = instance.Result[0];
+
+ // Report completion of this step
+ currentProgress = (i + 1) * progressPerPrefab;
+ CustomBoot.UpdateProgress(currentProgress);
}
}
@@ -55,12 +77,28 @@ namespace Bootstrap
}
Instances = new GameObject[BootPrefabs.Length];
+
+ // Calculate total prefabs for progress tracking
+ int totalPrefabs = BootPrefabs.Length;
+ float progressPerPrefab = totalPrefabs > 0 ? 1f / totalPrefabs : 1f;
+ float currentProgress = 0f;
+
for (var i = 0; i < BootPrefabs.Length; i++)
{
- if (!BootPrefabs[i]) continue;
+ if (!BootPrefabs[i])
+ {
+ // Report incremental progress even for null prefabs
+ currentProgress = (i + 1) * progressPerPrefab;
+ CustomBoot.UpdateProgress(currentProgress);
+ continue;
+ }
var instance = GameObject.Instantiate(BootPrefabs[i], RuntimeContainer.transform);
Instances[i] = instance;
+
+ // Report completion of this step
+ currentProgress = (i + 1) * progressPerPrefab;
+ CustomBoot.UpdateProgress(currentProgress);
}
}
diff --git a/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs
new file mode 100644
index 00000000..bebe4d9b
--- /dev/null
+++ b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs
@@ -0,0 +1,240 @@
+using System;
+using System.Collections;
+using UnityEngine;
+using UnityEngine.UI;
+using Core;
+
+namespace Bootstrap
+{
+ ///
+ /// Specialized loading screen controller specifically for the initial boot sequence.
+ /// This handles the combined progress of bootstrap initialization and main menu loading.
+ ///
+ public class InitialLoadingScreen : MonoBehaviour
+ {
+ [Header("UI References")]
+ [SerializeField] private GameObject loadingScreenContainer;
+ [SerializeField] private Image progressBarImage;
+
+ [Header("Settings")]
+ [SerializeField] private float minimumDisplayTime = 1.0f;
+ [SerializeField] private float progressUpdateInterval = 0.1f;
+
+ private float _displayStartTime;
+ private Coroutine _progressCoroutine;
+ private bool _loadingComplete = false;
+ private bool _animationComplete = false;
+ private Action _onLoadingScreenFullyHidden;
+
+ ///
+ /// Event that fires when the loading screen is fully hidden (both loading and animation completed)
+ ///
+ public event Action OnLoadingScreenFullyHidden;
+
+ ///
+ /// Delegate for providing progress values from different sources
+ ///
+ public delegate float ProgressProvider();
+
+ ///
+ /// Current progress provider being used for the loading screen
+ ///
+ private ProgressProvider _currentProgressProvider;
+
+ ///
+ /// Default progress provider that returns 0 (or 1 if loading is complete)
+ ///
+ private float DefaultProgressProvider() => _loadingComplete ? 1f : 0f;
+
+ ///
+ /// Check if the loading screen is currently active
+ ///
+ public bool IsActive => loadingScreenContainer != null && loadingScreenContainer.activeSelf;
+
+ private void Awake()
+ {
+ if (loadingScreenContainer == null)
+ loadingScreenContainer = gameObject;
+
+ // Ensure the loading screen is initially hidden
+ if (loadingScreenContainer != null)
+ {
+ loadingScreenContainer.SetActive(false);
+ }
+ }
+
+ ///
+ /// Shows the loading screen and resets the progress bar to zero
+ ///
+ /// Optional delegate to provide progress values (0-1). If null, uses default provider.
+ /// Optional callback when loading screen is fully hidden
+ public void ShowLoadingScreen(ProgressProvider progressProvider = null, Action onComplete = null)
+ {
+ // Store the completion callback
+ _onLoadingScreenFullyHidden = onComplete;
+
+ // Set the progress provider, use default if none provided
+ _currentProgressProvider = progressProvider ?? DefaultProgressProvider;
+
+ // Stop any existing progress coroutine
+ if (_progressCoroutine != null)
+ {
+ StopCoroutine(_progressCoroutine);
+ _progressCoroutine = null;
+ }
+
+ _displayStartTime = Time.time;
+ _loadingComplete = false;
+ _animationComplete = false;
+
+ if (progressBarImage != null)
+ {
+ progressBarImage.fillAmount = 0f;
+ }
+
+ if (loadingScreenContainer != null)
+ {
+ loadingScreenContainer.SetActive(true);
+ }
+
+ // Start the progress filling coroutine
+ _progressCoroutine = StartCoroutine(AnimateProgressBar());
+ }
+
+ ///
+ /// Animates the progress bar at a steady pace over the minimum display time,
+ /// while also checking actual loading progress from the current progress provider
+ ///
+ private IEnumerator AnimateProgressBar()
+ {
+ float startTime = Time.time;
+
+ // Continue until both animation and loading are complete
+ while (!_animationComplete)
+ {
+ // Calculate the steady progress based on elapsed time
+ float elapsedTime = Time.time - startTime;
+ float steadyProgress = Mathf.Clamp01(elapsedTime / minimumDisplayTime);
+
+ // Get the actual loading progress from the current provider
+ float actualProgress = _currentProgressProvider();
+
+ // If loading is complete, actualProgress should be 1.0
+ if (_loadingComplete)
+ {
+ actualProgress = 1.0f;
+ }
+
+ // Use the minimum of steady progress and actual progress
+ // This ensures we don't show more progress than actual loading
+ float displayProgress = Mathf.Min(steadyProgress, actualProgress);
+
+ // Log the progress values for debugging
+ Debug.Log($"[InitialLoadingScreen] Progress - Default: {steadyProgress:F2}, Actual: {actualProgress:F2}, Display: {displayProgress:F2}");
+
+ // Directly set the progress bar fill amount without smoothing
+ if (progressBarImage != null)
+ {
+ progressBarImage.fillAmount = displayProgress;
+ }
+
+ // Check if the animation has completed
+ // Animation is complete when we've reached the minimum display time AND we're at 100% progress
+ if (steadyProgress >= 1.0f && displayProgress >= 1.0f)
+ {
+ _animationComplete = true;
+ Debug.Log("[InitialLoadingScreen] Animation complete");
+ break;
+ }
+
+ // Wait for the configured interval before updating again
+ yield return new WaitForSeconds(progressUpdateInterval);
+ }
+
+ // Ensure we end at 100% progress
+ if (progressBarImage != null)
+ {
+ progressBarImage.fillAmount = 1.0f;
+ Debug.Log("[InitialLoadingScreen] Final progress set to 1.0");
+ }
+
+ // Hide the screen if loading is also complete
+ if (_loadingComplete)
+ {
+ if (loadingScreenContainer != null)
+ {
+ loadingScreenContainer.SetActive(false);
+ Debug.Log("[InitialLoadingScreen] Animation AND loading complete, hiding screen");
+
+ // Invoke the callback when fully hidden
+ _onLoadingScreenFullyHidden?.Invoke();
+ OnLoadingScreenFullyHidden?.Invoke();
+ _onLoadingScreenFullyHidden = null;
+ }
+ }
+
+ _progressCoroutine = null;
+ }
+
+ ///
+ /// Called when the actual loading process is complete
+ ///
+ public void HideLoadingScreen()
+ {
+ Debug.Log("[InitialLoadingScreen] Loading complete, marking loading as finished");
+
+ // Mark that loading is complete
+ _loadingComplete = true;
+
+ // If animation is already complete, we can hide the screen now
+ if (_animationComplete)
+ {
+ if (loadingScreenContainer != null)
+ {
+ loadingScreenContainer.SetActive(false);
+ Debug.Log("[InitialLoadingScreen] Animation already complete, hiding screen immediately");
+
+ // Invoke the callback when fully hidden
+ _onLoadingScreenFullyHidden?.Invoke();
+ OnLoadingScreenFullyHidden?.Invoke();
+ _onLoadingScreenFullyHidden = null;
+ }
+ }
+ else
+ {
+ Debug.Log("[InitialLoadingScreen] Animation still in progress, waiting for it to complete");
+ // The coroutine will handle hiding when animation completes
+ }
+ }
+
+ ///
+ /// Waits until the loading screen is fully hidden before continuing
+ ///
+ /// Task that completes when the loading screen is hidden
+ public System.Threading.Tasks.Task WaitForLoadingScreenToHideAsync()
+ {
+ var tcs = new System.Threading.Tasks.TaskCompletionSource();
+
+ // If the loading screen is not active, complete immediately
+ if (!IsActive)
+ {
+ tcs.SetResult(true);
+ return tcs.Task;
+ }
+
+ // Store existing callback to chain it
+ Action existingCallback = _onLoadingScreenFullyHidden;
+
+ // Set new callback
+ _onLoadingScreenFullyHidden = () => {
+ // Call existing callback if any
+ existingCallback?.Invoke();
+
+ // Complete the task
+ tcs.SetResult(true);
+ };
+
+ return tcs.Task;
+ }
+ }
+}
diff --git a/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs.meta b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs.meta
new file mode 100644
index 00000000..cf2bd6e7
--- /dev/null
+++ b/Assets/Scripts/Bootstrap/InitialLoadingScreen.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8968b564891a474baae157792b88e75f
+timeCreated: 1760613320
\ No newline at end of file
diff --git a/Assets/Scripts/Cinematics/CinematicsManager.cs b/Assets/Scripts/Cinematics/CinematicsManager.cs
index e8dbf1a5..66eafa0f 100644
--- a/Assets/Scripts/Cinematics/CinematicsManager.cs
+++ b/Assets/Scripts/Cinematics/CinematicsManager.cs
@@ -1,4 +1,6 @@
+using System;
using System.Collections.Generic;
+using Bootstrap;
using Core;
using UnityEngine;
using UnityEngine.AddressableAssets;
@@ -26,24 +28,27 @@ namespace Cinematics
private Dictionary> _addressableHandles
= new Dictionary>();
- public static CinematicsManager Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("CinematicsManager");
- _instance = go.AddComponent();
- // DontDestroyOnLoad(go);
- }
- }
- return _instance;
- }
- }
+ ///
+ /// Singleton instance of the CinematicsManager. No longer creates an instance if one doesn't exist.
+ ///
+ public static CinematicsManager Instance => _instance;
+
public PlayableDirector playableDirector;
+
+ private void Awake()
+ {
+ _instance = this;
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
+ private void InitializePostBoot()
+ {
+ // Initialize any dependencies that require other services to be ready
+ // For example, subscribe to SceneManagerService events if needed
+ Logging.Debug("[CinematicsManager] Post-boot initialization complete");
+ }
private void OnEnable()
{
@@ -62,15 +67,55 @@ namespace Cinematics
private void OnApplicationQuit()
{
+ _isQuitting = true;
ReleaseAllHandles();
}
+ ///
+ /// Initializes required components for the CinematicsManager
+ ///
+ private void InitializeComponents()
+ {
+ // Initialize PlayableDirector if not set
+ if (playableDirector == null)
+ {
+ playableDirector = GetComponent();
+
+ // If still null, try to add the component
+ if (playableDirector == null)
+ {
+ playableDirector = gameObject.AddComponent();
+ Debug.Log("[CinematicsManager] Added missing PlayableDirector component");
+ }
+ }
+
+ // Initialize _cinematicSprites if not set
+ if (_cinematicSprites == null)
+ {
+ // First try to find in children
+ _cinematicSprites = GetComponentInChildren(true);
+
+ // If still null, create a new UI Image for cinematics
+ if (_cinematicSprites == null)
+ {
+ Debug.LogWarning("[CinematicsManager] No Image found for cinematics display. Cinematics may not display correctly.");
+ }
+ }
+ }
+
///
/// Plays a cinematic from an object reference and returns the PlayableDirector playing the timeline
///
public PlayableDirector PlayCinematic(PlayableAsset assetToPlay)
{
- _cinematicSprites.enabled = true;
+ // Ensure components are initialized before playing
+ InitializeComponents();
+
+ if (_cinematicSprites != null)
+ {
+ _cinematicSprites.enabled = true;
+ }
+
playableDirector.stopped += OnPlayableDirectorStopped;
playableDirector.Play(assetToPlay);
Logging.Debug("Playing cinematic " + assetToPlay.name);
@@ -145,68 +190,5 @@ namespace Cinematics
}
_addressableHandles.Clear();
}
-
- private void Awake()
- {
- PlayStartCinematicOnGameLoad();
- }
-
- ///
- /// Loads a cinematic asynchronously while showing a loading screen, then plays it
- ///
- /// The addressable key of the cinematic to load
- /// The PlayableDirector playing the cinematic
- public async System.Threading.Tasks.Task PlayCinematicWithLoadingScreen(string key)
- {
- Logging.Debug($"[CinematicsManager] Preparing to load cinematic with loading screen: {key}");
-
- // First, show the loading screen BEFORE creating any async operations
- UI.LoadingScreenController.Instance.ShowLoadingScreen();
-
- // Give the loading screen a frame to render
- await System.Threading.Tasks.Task.Yield();
-
- // Now create the load handle and track its progress
- Logging.Debug($"[CinematicsManager] Starting cinematic asset load: {key}");
- AsyncOperationHandle handle = Addressables.LoadAssetAsync(key);
-
- // Update the loading screen with the progress provider after the handle is created
- UI.LoadingScreenController.Instance.ShowLoadingScreen(() => handle.PercentComplete);
-
- // Wait for the loading to complete
- var result = await handle.Task;
-
- // Store the handle for later release
- _addressableHandles[playableDirector] = handle;
-
- Logging.Debug($"[CinematicsManager] Cinematic loaded: {key}");
-
- // Hide the loading screen
- UI.LoadingScreenController.Instance.HideLoadingScreen();
-
- // Important: Wait for the loading screen to be fully hidden before playing the cinematic
- await UI.LoadingScreenController.Instance.WaitForLoadingScreenToHideAsync();
-
- Logging.Debug($"[CinematicsManager] Loading screen hidden, now playing cinematic: {key}");
-
- // Play the cinematic
- return PlayCinematic(result);
- }
-
- private async void PlayStartCinematicOnGameLoad()
- {
- if (!SceneManager.GetActiveScene().name.ToLower().Contains("mainmenu"))
- {
- return;
- }
-
- _instance = this;
-
- playableDirector = GetComponent();
- _cinematicSprites = GetComponentInChildren(true);
-
- // Use the new method with loading screen instead of direct load
- await PlayCinematicWithLoadingScreen("IntroSequence");
- }
}
}
diff --git a/Assets/Scripts/Cinematics/SkipCinematic.cs b/Assets/Scripts/Cinematics/SkipCinematic.cs
index 17e546a9..7db5ecf1 100644
--- a/Assets/Scripts/Cinematics/SkipCinematic.cs
+++ b/Assets/Scripts/Cinematics/SkipCinematic.cs
@@ -1,3 +1,4 @@
+using Bootstrap;
using Core;
using Input;
using UnityEngine;
@@ -14,7 +15,14 @@ namespace Cinematics
private float _holdStartTime;
private bool _isHolding;
private bool _skipPerformed;
+ private bool _initialized = false;
+ void Awake()
+ {
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
void Start()
{
// Reset the progress bar
@@ -23,32 +31,76 @@ namespace Cinematics
radialProgressBar.fillAmount = 0f;
}
}
-
- void OnEnable()
- {
- if (CinematicsManager.Instance.IsCinematicPlaying)
- HandleCinematicStarted();
-
- CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted;
- CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped;
- }
void OnDisable()
{
- CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted;
- CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped;
- // If still registered, unregister input override
- InputManager.Instance.UnregisterOverrideConsumer(this);
+ // Clean up subscriptions regardless of initialization state
+ UnsubscribeFromCinematicsEvents();
+ }
+
+ private void InitializePostBoot()
+ {
+ // Safe initialization of manager dependencies after boot is complete
+ if (_initialized)
+ return;
+
+ _initialized = true;
+
+ // Subscribe to CinematicsManager events now that boot is complete
+ SubscribeToCinematicsEvents();
+
+ Logging.Debug("[SkipCinematic] Post-boot initialization complete");
+ }
+
+ private void SubscribeToCinematicsEvents()
+ {
+ if (CinematicsManager.Instance == null) return;
+
+ // First unsubscribe to prevent duplicate subscriptions
+ UnsubscribeFromCinematicsEvents();
+
+ // Now subscribe
+ CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted;
+ CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped;
+
+ // Check if a cinematic is already playing
+ if (CinematicsManager.Instance.IsCinematicPlaying)
+ HandleCinematicStarted();
+
+ Logging.Debug("[SkipCinematic] Subscribed to cinematics events");
+ }
+
+ private void UnsubscribeFromCinematicsEvents()
+ {
+ if (CinematicsManager.Instance != null)
+ {
+ CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted;
+ CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped;
+ Logging.Debug("[SkipCinematic] Unsubscribed from cinematics events");
+ }
+
+ // If still registered as an input override consumer, unregister
+ if (InputManager.Instance != null)
+ {
+ InputManager.Instance.UnregisterOverrideConsumer(this);
+ Logging.Debug("[SkipCinematic] Unregistered as input override consumer");
+ }
}
private void HandleCinematicStarted()
{
- InputManager.Instance.RegisterOverrideConsumer(this);
+ if (InputManager.Instance != null)
+ {
+ InputManager.Instance.RegisterOverrideConsumer(this);
+ }
}
private void HandleCinematicStopped()
{
- InputManager.Instance.UnregisterOverrideConsumer(this);
+ if (InputManager.Instance != null)
+ {
+ InputManager.Instance.UnregisterOverrideConsumer(this);
+ }
}
void Update()
diff --git a/Assets/Scripts/Core/GameManager.cs b/Assets/Scripts/Core/GameManager.cs
index db282188..daf66e07 100644
--- a/Assets/Scripts/Core/GameManager.cs
+++ b/Assets/Scripts/Core/GameManager.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using AppleHills.Core.Interfaces;
using Core;
using UI;
+using Bootstrap;
///
/// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters.
@@ -15,25 +16,9 @@ public class GameManager : MonoBehaviour
private static bool _isQuitting = false;
///
- /// Singleton instance of the GameManager.
+ /// Singleton instance of the GameManager. No longer creates an instance if one doesn't exist.
///
- public static GameManager Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("GameManager");
- _instance = go.AddComponent();
- // DontDestroyOnLoad(go);
- }
- }
- return _instance;
- }
- }
+ public static GameManager Instance => _instance;
[Header("Settings Status")]
[SerializeField] private bool _settingsLoaded = false;
@@ -70,10 +55,13 @@ public class GameManager : MonoBehaviour
InitializeSettings();
InitializeDeveloperSettings();
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+
// DontDestroyOnLoad(gameObject);
}
- private void Start()
+ private void InitializePostBoot()
{
// Find and subscribe to PauseMenu events
PauseMenu pauseMenu = PauseMenu.Instance;
diff --git a/Assets/Scripts/Core/ItemManager.cs b/Assets/Scripts/Core/ItemManager.cs
index 43647028..fab7930b 100644
--- a/Assets/Scripts/Core/ItemManager.cs
+++ b/Assets/Scripts/Core/ItemManager.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using UnityEngine;
using Interactions;
+using Bootstrap;
namespace Core
{
@@ -14,23 +15,10 @@ namespace Core
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;
- }
- }
+ ///
+ /// Singleton instance of the ItemManager. No longer creates an instance if one doesn't exist.
+ ///
+ public static ItemManager Instance => _instance;
private readonly HashSet _pickups = new HashSet();
private readonly HashSet _itemSlots = new HashSet();
@@ -63,13 +51,16 @@ namespace Core
void Awake()
{
_instance = this;
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
}
-
- void Start()
+
+ private void InitializePostBoot()
{
// 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.SceneLoadStarted += OnSceneLoadStarted;
+ Logging.Debug("[ItemManager] Subscribed to SceneManagerService events");
}
void OnDestroy()
diff --git a/Assets/Scripts/Core/QuickAccess.cs b/Assets/Scripts/Core/QuickAccess.cs
index 954860c7..944e6217 100644
--- a/Assets/Scripts/Core/QuickAccess.cs
+++ b/Assets/Scripts/Core/QuickAccess.cs
@@ -18,23 +18,11 @@ namespace AppleHills.Core
private static QuickAccess _instance;
private static bool _isQuitting = false;
- public static QuickAccess Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("QuickAccess");
- _instance = go.AddComponent();
- }
- }
- return _instance;
- }
- }
-
+ ///
+ /// Singleton instance of QuickAccess. No longer creates an instance if one doesn't exist.
+ ///
+ public static QuickAccess Instance => _instance;
+
void OnApplicationQuit()
{
_isQuitting = true;
@@ -146,6 +134,8 @@ namespace AppleHills.Core
private void Awake()
{
+ _instance = this;
+
if (!_initialized)
{
// Subscribe to scene changes
diff --git a/Assets/Scripts/Core/SceneManagerService.cs b/Assets/Scripts/Core/SceneManagerService.cs
index 3ecc6a8f..0244d624 100644
--- a/Assets/Scripts/Core/SceneManagerService.cs
+++ b/Assets/Scripts/Core/SceneManagerService.cs
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using UI;
using UnityEngine;
using UnityEngine.SceneManagement;
+using Bootstrap;
namespace Core
{
@@ -15,26 +16,11 @@ namespace Core
private LoadingScreenController _loadingScreen;
private static SceneManagerService _instance;
private static bool _isQuitting = false;
+
///
- /// Singleton instance of the SceneManagerService.
+ /// Singleton instance of the SceneManagerService. No longer creates an instance if one doesn't exist.
///
- public static SceneManagerService Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("SceneManagerService");
- _instance = go.AddComponent();
- // DontDestroyOnLoad(go);
- }
- }
- return _instance;
- }
- }
+ public static SceneManagerService Instance => _instance;
// Events for scene lifecycle
public event Action SceneLoadStarted;
@@ -48,30 +34,17 @@ namespace Core
private readonly Dictionary _activeUnloads = new();
private const string BootstrapSceneName = "BootstrapScene";
- void Start()
- {
- _loadingScreen = LoadingScreenController.Instance;
-
- // Set up loading screen event handlers
- SetupLoadingScreenEvents();
- }
-
-
void Awake()
{
_instance = this;
// DontDestroyOnLoad(gameObject);
-#if UNITY_EDITOR
- // In Editor, set CurrentGameplayScene to the currently open scene at play start
- if (Application.isPlaying)
- {
- var activeScene = SceneManager.GetActiveScene();
- if (activeScene.IsValid())
- {
- CurrentGameplayScene = activeScene.name;
- }
- }
-#endif
+
+ // Initialize current scene tracking immediately in Awake
+ InitializeCurrentSceneTracking();
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+
// Ensure BootstrapScene is loaded at startup
var bootstrap = SceneManager.GetSceneByName(BootstrapSceneName);
if (!bootstrap.isLoaded)
@@ -79,6 +52,48 @@ namespace Core
SceneManager.LoadScene(BootstrapSceneName, LoadSceneMode.Additive);
}
}
+
+ ///
+ /// Initialize current scene tracking immediately in Awake
+ /// This ensures scene management works correctly regardless of boot timing
+ ///
+ private void InitializeCurrentSceneTracking()
+ {
+ // Get the active scene and use it as the current gameplay scene
+ Scene activeScene = SceneManager.GetActiveScene();
+
+ if (activeScene.IsValid())
+ {
+ // If this is the MainMenu or another gameplay scene, track it
+ if (activeScene.name != BootstrapSceneName)
+ {
+ CurrentGameplayScene = activeScene.name;
+ Logging.Debug($"[SceneManagerService] Initialized with current scene: {CurrentGameplayScene}");
+ }
+ // Otherwise default to MainMenu
+ else
+ {
+ CurrentGameplayScene = "MainMenu";
+ Logging.Debug($"[SceneManagerService] Initialized with default scene: {CurrentGameplayScene}");
+ }
+ }
+ else
+ {
+ CurrentGameplayScene = "MainMenu";
+ Logging.Debug($"[SceneManagerService] No valid active scene, defaulting to: {CurrentGameplayScene}");
+ }
+ }
+
+ private void InitializePostBoot()
+ {
+ // Set up loading screen reference and events after boot is complete
+ _loadingScreen = LoadingScreenController.Instance;
+
+ // Set up loading screen event handlers if available
+ SetupLoadingScreenEvents();
+
+ Logging.Debug($"[SceneManagerService] Post-boot initialization complete, current scene is: {CurrentGameplayScene}");
+ }
private void SetupLoadingScreenEvents()
{
@@ -262,16 +277,27 @@ namespace Core
}
// Tracks the currently loaded gameplay scene (not persistent/bootstrapper)
- public string CurrentGameplayScene { get; private set; } = "MainMenu";
+ public string CurrentGameplayScene { get; set; } = "MainMenu";
- public async Task ReloadCurrentScene(IProgress progress = null)
+ public async Task ReloadCurrentScene(IProgress progress = null, bool autoHideLoadingScreen = true)
{
- await SwitchSceneAsync(CurrentGameplayScene, progress);
+ await SwitchSceneAsync(CurrentGameplayScene, progress, autoHideLoadingScreen);
}
- // Switches from current gameplay scene to a new one
- public async Task SwitchSceneAsync(string newSceneName, IProgress progress = null)
+ ///
+ /// Switches from current gameplay scene to a new one
+ ///
+ /// Name of the scene to load
+ /// Optional progress reporter
+ /// Whether to automatically hide the loading screen when complete. If false, caller must hide it manually.
+ public async Task SwitchSceneAsync(string newSceneName, IProgress progress = null, bool autoHideLoadingScreen = true)
{
+ // Show loading screen at the start (whether using auto-hide or not)
+ if (_loadingScreen != null && !_loadingScreen.IsActive)
+ {
+ _loadingScreen.ShowLoadingScreen();
+ }
+
// Remove all AstarPath (A* Pathfinder) singletons before loading the new scene
var astarPaths = FindObjectsByType(FindObjectsSortMode.None);
foreach (var astar in astarPaths)
@@ -304,6 +330,12 @@ namespace Core
await LoadSceneAsync(newSceneName, progress);
// Update tracker
CurrentGameplayScene = newSceneName;
+
+ // Only hide the loading screen if autoHideLoadingScreen is true
+ if (autoHideLoadingScreen && _loadingScreen != null)
+ {
+ _loadingScreen.HideLoadingScreen();
+ }
}
}
}
diff --git a/Assets/Scripts/Core/SceneOrientationEnforcer.cs b/Assets/Scripts/Core/SceneOrientationEnforcer.cs
index ca5c8b0c..7ab784ff 100644
--- a/Assets/Scripts/Core/SceneOrientationEnforcer.cs
+++ b/Assets/Scripts/Core/SceneOrientationEnforcer.cs
@@ -5,6 +5,8 @@ using Input;
using Settings;
using System.Collections;
using Minigames.DivingForPictures;
+using Bootstrap;
+using Core;
namespace Utility
{
@@ -12,23 +14,11 @@ namespace Utility
{
private static SceneOrientationEnforcer _instance;
private static bool _isQuitting;
- public static SceneOrientationEnforcer Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("SceneOrientationEnforcer");
- _instance = go.AddComponent();
- // DontDestroyOnLoad(go); // Uncomment if you want persistence
- }
- }
- return _instance;
- }
- }
+
+ ///
+ /// Singleton instance of the SceneOrientationEnforcer. No longer creates an instance if one doesn't exist.
+ ///
+ public static SceneOrientationEnforcer Instance => _instance;
[Header("Config")]
public SceneOrientationConfig orientationConfig;
@@ -48,10 +38,16 @@ namespace Utility
{
_instance = this;
OnOrientationCorrect += HandleOrientationCorrect;
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
}
-
- void Start()
+
+ private void InitializePostBoot()
{
+ // Initialize any dependencies that require other services to be ready
+ Logging.Debug("[SceneOrientationEnforcer] Post-boot initialization complete");
+
// Subscribe to sceneLoaded event
SceneManager.sceneLoaded += OnSceneLoaded;
// Manually invoke for the first scene (unless it's Main Menu)
@@ -74,6 +70,8 @@ namespace Utility
return Screen.orientation == ScreenOrientation.Portrait || Screen.orientation == ScreenOrientation.PortraitUpsideDown;
case ScreenOrientationRequirement.Landscape:
return Screen.orientation == ScreenOrientation.LandscapeLeft || Screen.orientation == ScreenOrientation.LandscapeRight;
+ case ScreenOrientationRequirement.NotApplicable:
+ return true;
default:
return true;
}
@@ -98,7 +96,7 @@ namespace Utility
_isDivingMinigame = IsDivingMinigameScene(scene);
- _requiredOrientation = orientationConfig != null ? orientationConfig.GetRequirementForScene(scene.name) : ScreenOrientationRequirement.Portrait;
+ _requiredOrientation = orientationConfig != null ? orientationConfig.GetRequirementForScene(scene.name) : ScreenOrientationRequirement.NotApplicable;
_orientationCorrect = IsOrientationCorrect();
if (!_orientationCorrect)
@@ -186,7 +184,7 @@ namespace Utility
_orientationCheckCoroutine = null;
}
- InputManager.Instance.SetInputMode(InputMode.Game);
+ InputManager.Instance.SetInputMode(InputMode.GameAndUI);
}
private void CleanupPromptAndCoroutine()
diff --git a/Assets/Scripts/Core/Settings/Developer/DeveloperSettingsProvider.cs b/Assets/Scripts/Core/Settings/Developer/DeveloperSettingsProvider.cs
index 7b491909..2db54849 100644
--- a/Assets/Scripts/Core/Settings/Developer/DeveloperSettingsProvider.cs
+++ b/Assets/Scripts/Core/Settings/Developer/DeveloperSettingsProvider.cs
@@ -24,7 +24,7 @@ namespace AppleHills.Core.Settings
if (_instance == null && Application.isPlaying)
{
_instance = FindFirstObjectByType();
-
+
if (_instance == null)
{
GameObject go = new GameObject("DeveloperSettingsProvider");
@@ -33,7 +33,7 @@ namespace AppleHills.Core.Settings
DontDestroyOnLoad(go);
}
}
-
+
return _instance;
}
}
diff --git a/Assets/Scripts/Data/CardSystem/CardSystemManager.cs b/Assets/Scripts/Data/CardSystem/CardSystemManager.cs
index f2027b21..b8ec90e6 100644
--- a/Assets/Scripts/Data/CardSystem/CardSystemManager.cs
+++ b/Assets/Scripts/Data/CardSystem/CardSystemManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using AppleHills.Data.CardSystem;
+using Bootstrap;
using Core;
using UnityEngine;
@@ -61,6 +62,15 @@ namespace Data.CardSystem
// Build lookup dictionary
BuildDefinitionLookup();
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
+ private void InitializePostBoot()
+ {
+ // Initialize any dependencies that require other services to be ready
+ Logging.Debug("[CardSystemManager] Post-boot initialization complete");
}
private void OnApplicationQuit()
diff --git a/Assets/Scripts/Input/InputManager.cs b/Assets/Scripts/Input/InputManager.cs
index ada4a561..1cd117d3 100644
--- a/Assets/Scripts/Input/InputManager.cs
+++ b/Assets/Scripts/Input/InputManager.cs
@@ -5,6 +5,7 @@ using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using AppleHills.Core.Settings;
+using Bootstrap;
using Core; // Added for IInteractionSettings
namespace Input
@@ -35,22 +36,10 @@ namespace Input
// Track which consumer is handling the current hold operation
private ITouchInputConsumer _activeHoldConsumer;
- public static InputManager Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("InputManager");
- _instance = go.AddComponent();
- }
- }
- return _instance;
- }
- }
+ ///
+ /// Singleton instance of the InputManager. No longer creates an instance if one doesn't exist.
+ ///
+ public static InputManager Instance => _instance;
// Settings reference
private IInteractionSettings _interactionSettings;
@@ -66,6 +55,15 @@ namespace Input
{
_instance = this;
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
+ private void InitializePostBoot()
+ {
+ // Subscribe to scene load completed events now that boot is complete
+ SceneManagerService.Instance.SceneLoadCompleted += SwitchInputOnSceneLoaded;
+
// Initialize settings reference
_interactionSettings = GameManager.GetSettingsObject();
@@ -78,12 +76,25 @@ namespace Input
tapMoveAction = playerInput.actions.FindAction("TapMove", false);
holdMoveAction = playerInput.actions.FindAction("HoldMove", false);
positionAction = playerInput.actions.FindAction("TouchPosition", false);
- }
-
- private void Start()
- {
- // SceneManagerService.Instance.SceneLoadCompleted += SwitchInputOnSceneLoaded;
+
+ if (tapMoveAction != null)
+ tapMoveAction.performed += OnTapMovePerformed;
+ if (holdMoveAction != null)
+ {
+ holdMoveAction.performed += OnHoldMoveStarted;
+ holdMoveAction.canceled += OnHoldMoveCanceled;
+ }
+
SwitchInputOnSceneLoaded(SceneManager.GetActiveScene().name);
+
+ Logging.Debug("[InputManager] Subscribed to SceneManagerService events");
+ }
+
+ private void OnDestroy()
+ {
+ // Unsubscribe from SceneManagerService
+ if (SceneManagerService.Instance != null)
+ SceneManagerService.Instance.SceneLoadCompleted -= SwitchInputOnSceneLoaded;
}
private void SwitchInputOnSceneLoaded(string sceneName)
@@ -122,17 +133,6 @@ namespace Input
break;
}
}
-
- void OnEnable()
- {
- if (tapMoveAction != null)
- tapMoveAction.performed += OnTapMovePerformed;
- if (holdMoveAction != null)
- {
- holdMoveAction.performed += OnHoldMoveStarted;
- holdMoveAction.canceled += OnHoldMoveCanceled;
- }
- }
void OnDisable()
{
@@ -149,7 +149,7 @@ namespace Input
{
_isQuitting = true;
}
-
+
///
/// Sets the default ITouchInputConsumer to receive input events.
///
diff --git a/Assets/Scripts/Interactions/Interactable.cs b/Assets/Scripts/Interactions/Interactable.cs
index 521d3c1b..e21cec6a 100644
--- a/Assets/Scripts/Interactions/Interactable.cs
+++ b/Assets/Scripts/Interactions/Interactable.cs
@@ -418,7 +418,8 @@ namespace Interactions
{
// Check for ObjectiveStepBehaviour and lock state
var step = GetComponent();
- if (step != null && !step.IsStepUnlocked())
+ var slot = GetComponent();
+ if (step != null && !step.IsStepUnlocked() && slot == null)
{
DebugUIMessage.Show("This step is locked!", Color.yellow);
BroadcastInteractionComplete(false);
diff --git a/Assets/Scripts/PuzzleS/ObjectiveStepBehaviour.cs b/Assets/Scripts/PuzzleS/ObjectiveStepBehaviour.cs
index e11e168d..91937df3 100644
--- a/Assets/Scripts/PuzzleS/ObjectiveStepBehaviour.cs
+++ b/Assets/Scripts/PuzzleS/ObjectiveStepBehaviour.cs
@@ -1,10 +1,7 @@
using Input;
using Interactions;
using UnityEngine;
-using System;
-using AppleHills.Core.Settings;
using Core;
-using UnityEngine.Serialization;
namespace PuzzleS
{
@@ -37,22 +34,52 @@ namespace PuzzleS
void Awake()
{
_interactable = GetComponent();
+
+ // Initialize the indicator if it exists, but ensure it's hidden initially
+ if (puzzleIndicator != null)
+ {
+ // The indicator should start inactive until we determine its proper state
+ puzzleIndicator.SetActive(false);
+
+ // Get the IPuzzlePrompt component
+ _indicator = puzzleIndicator.GetComponent();
+
+ if (_indicator == null)
+ {
+ // Try to find it in children if not on the root
+ _indicator = puzzleIndicator.GetComponentInChildren();
+ }
+
+ if (_indicator == null)
+ {
+ Logging.Warning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt");
+ }
+ }
}
void OnEnable()
{
if (_interactable == null)
_interactable = GetComponent();
+
if (_interactable != null)
{
_interactable.interactionStarted.AddListener(OnInteractionStarted);
_interactable.interactionComplete.AddListener(OnInteractionComplete);
}
- PuzzleManager.Instance?.RegisterStepBehaviour(this);
- // Check if this step was already unlocked
- if (stepData != null && PuzzleManager.Instance != null && PuzzleManager.Instance.IsStepUnlocked(stepData))
+ }
+
+ void Start()
+ {
+ // Simply register with the PuzzleManager
+ // The manager will handle state updates appropriately based on whether data is loaded
+ if (stepData != null && PuzzleManager.Instance != null)
{
- UnlockStep();
+ PuzzleManager.Instance.RegisterStepBehaviour(this);
+ }
+ else if (stepData == null)
+ {
+ Logging.Warning($"[Puzzles] Cannot register step on {gameObject.name}: stepData is null");
}
}
@@ -63,7 +90,11 @@ namespace PuzzleS
_interactable.interactionStarted.RemoveListener(OnInteractionStarted);
_interactable.interactionComplete.RemoveListener(OnInteractionComplete);
}
- PuzzleManager.Instance?.UnregisterStepBehaviour(this);
+
+ if (PuzzleManager.Instance != null && stepData != null)
+ {
+ PuzzleManager.Instance.UnregisterStepBehaviour(this);
+ }
}
///
@@ -73,7 +104,7 @@ namespace PuzzleS
public void UpdateProximityState(ProximityState newState)
{
if (_currentProximityState == newState) return;
- if (_indicator == null) return;
+ if (!_isUnlocked) return; // Don't process state changes if locked
// Determine state changes and call appropriate methods
if (newState == ProximityState.Close)
@@ -97,6 +128,9 @@ namespace PuzzleS
///
public virtual void OnShow()
{
+ if (puzzleIndicator != null)
+ puzzleIndicator.SetActive(true);
+
// Delegate to indicator if available
if (IsIndicatorValid())
{
@@ -104,7 +138,6 @@ namespace PuzzleS
return;
}
- // Default fallback behavior
Logging.Debug($"[Puzzles] Prompt shown for {stepData?.stepId} on {gameObject.name}");
}
@@ -113,14 +146,15 @@ namespace PuzzleS
///
public virtual void OnHide()
{
+ if (puzzleIndicator != null)
+ puzzleIndicator.SetActive(false);
+
// Delegate to indicator if available
if (IsIndicatorValid())
{
_indicator.OnHide();
- return;
}
- // Default fallback behavior
Logging.Debug($"[Puzzles] Prompt hidden for {stepData?.stepId} on {gameObject.name}");
}
@@ -138,9 +172,6 @@ namespace PuzzleS
_indicator.ShowFar();
return;
}
-
- // Default fallback behavior
- Logging.Debug($"[Puzzles] Player entered far range of {stepData?.stepId} on {gameObject.name}");
}
///
@@ -157,9 +188,6 @@ namespace PuzzleS
_indicator.ShowClose();
return;
}
-
- // Default fallback behavior
- Logging.Debug($"[Puzzles] Player entered close range of {stepData?.stepId} on {gameObject.name}");
}
///
@@ -176,9 +204,6 @@ namespace PuzzleS
_indicator.HideClose();
return;
}
-
- // Default fallback behavior
- Logging.Debug($"[Puzzles] Player exited close range of {stepData?.stepId} on {gameObject.name}");
}
///
@@ -195,9 +220,6 @@ namespace PuzzleS
_indicator.HideFar();
return;
}
-
- // Default fallback behavior
- Logging.Debug($"[Puzzles] Player exited far range of {stepData?.stepId} on {gameObject.name}");
}
///
@@ -205,57 +227,42 @@ namespace PuzzleS
///
public void UnlockStep()
{
+ if (_isUnlocked) return;
+
_isUnlocked = true;
Logging.Debug($"[Puzzles] Step unlocked: {stepData?.stepId} on {gameObject.name}");
- // Show indicator if enabled in settings
- if (puzzleIndicator != null)
+ // Make the indicator visible since this step is now unlocked
+ OnShow();
+
+ if (IsIndicatorValid())
{
- // Try to get the IPuzzlePrompt component from the spawned indicator
- _indicator = puzzleIndicator.GetComponent();
-
- if (_indicator == null)
+ // Set the correct state based on current player distance
+ Transform playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
+ if (playerTransform != null)
{
- // Try to find it in children if not on the root
- _indicator = puzzleIndicator.GetComponentInChildren();
- }
-
- if (_indicator == null)
- {
- Logging.Warning($"[Puzzles] Indicator prefab for {stepData?.stepId} does not implement IPuzzlePrompt");
- }
- else
- {
- // First show the indicator
- _indicator.OnShow();
+ float distance = Vector3.Distance(transform.position, playerTransform.position);
+ float promptRange = AppleHills.SettingsAccess.GetPuzzlePromptRange();
- // Then set the correct state based on current player distance
- Transform playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
- if (playerTransform != null)
+ if (distance <= promptRange)
{
- float distance = Vector3.Distance(transform.position, playerTransform.position);
- float promptRange = AppleHills.SettingsAccess.GetPuzzlePromptRange();
-
- if (distance <= promptRange)
- {
- // Player is in close range
- _currentProximityState = ProximityState.Close;
- _indicator.ShowClose();
- }
- else
- {
- // Player is in far range
- _currentProximityState = ProximityState.Far;
- _indicator.ShowFar();
- }
+ // Player is in close range
+ _currentProximityState = ProximityState.Close;
+ _indicator.ShowClose();
}
else
{
- // Default to far if player not found
+ // Player is in far range
_currentProximityState = ProximityState.Far;
_indicator.ShowFar();
}
}
+ else
+ {
+ // Default to far if player not found
+ _currentProximityState = ProximityState.Far;
+ _indicator.ShowFar();
+ }
}
}
@@ -264,14 +271,18 @@ namespace PuzzleS
///
public void LockStep()
{
+ if (!_isUnlocked && puzzleIndicator != null)
+ {
+ // Make sure indicator is hidden if we're already locked
+ puzzleIndicator.SetActive(false);
+ return;
+ }
+
_isUnlocked = false;
Logging.Debug($"[Puzzles] Step locked: {stepData?.stepId} on {gameObject.name}");
- // Hide indicator
- if (IsIndicatorValid())
- {
- _indicator.OnHide();
- }
+ // Hide the indicator
+ OnHide();
}
///
@@ -287,7 +298,7 @@ namespace PuzzleS
///
private void OnInteractionStarted(PlayerTouchController playerRef, FollowerController followerRef)
{
- // Optionally handle started interaction (e.g. visual feedback)
+ // Empty - handled by Interactable
}
///
@@ -297,12 +308,17 @@ namespace PuzzleS
private void OnInteractionComplete(bool success)
{
if (!_isUnlocked) return;
- if (success)
+ if (success && !_isCompleted)
{
Logging.Debug($"[Puzzles] Step interacted: {stepData?.stepId} on {gameObject.name}");
_isCompleted = true;
PuzzleManager.Instance?.MarkPuzzleStepCompleted(stepData);
- Destroy(puzzleIndicator);
+
+ if (puzzleIndicator != null)
+ {
+ Destroy(puzzleIndicator);
+ _indicator = null;
+ }
}
}
@@ -323,7 +339,7 @@ namespace PuzzleS
// Draw threshold circle
Gizmos.color = Color.cyan;
- Gizmos.DrawWireSphere(transform.position, promptRange / 2f);
+ Gizmos.DrawWireSphere(transform.position, promptRange);
}
}
}
diff --git a/Assets/Scripts/PuzzleS/PuzzleChainSO.cs b/Assets/Scripts/PuzzleS/PuzzleChainSO.cs
new file mode 100644
index 00000000..3340dfa7
--- /dev/null
+++ b/Assets/Scripts/PuzzleS/PuzzleChainSO.cs
@@ -0,0 +1,103 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.AddressableAssets;
+
+namespace PuzzleS
+{
+ ///
+ /// Represents a complete chain of puzzle steps that form a logical sequence.
+ /// This is automatically generated from folder structure during asset import.
+ ///
+ [CreateAssetMenu(fileName = "PuzzleChain", menuName = "AppleHills/Items & Puzzles/PuzzleChain")]
+ public class PuzzleChainSO : ScriptableObject
+ {
+ ///
+ /// Unique identifier for this puzzle chain, automatically set to match folder name
+ ///
+ public string chainId;
+
+ ///
+ /// Display name for this chain
+ ///
+ public string displayName;
+
+ ///
+ /// Description of this puzzle chain
+ ///
+ [TextArea]
+ public string description;
+
+ ///
+ /// All steps that belong to this puzzle chain
+ ///
+ public List allSteps = new List();
+
+ ///
+ /// Initial steps that should be unlocked when the puzzle chain starts
+ /// (steps with no dependencies)
+ ///
+ public List initialSteps = new List();
+
+ ///
+ /// Optional requirement for this entire chain to be activated
+ /// If not null, this chain requires the specified chain to be completed first
+ ///
+ public PuzzleChainSO requiredChain;
+
+ ///
+ /// Pre-processed dependency data built at edit time.
+ /// Maps step IDs to arrays of dependency step IDs
+ ///
+ [HideInInspector]
+ public Dictionary stepDependencies = new Dictionary();
+
+ ///
+ /// Gets all steps that will be unlocked by completing the given step
+ ///
+ public List GetUnlockedSteps(string stepId)
+ {
+ var result = new List();
+ foreach (var step in allSteps)
+ {
+ if (step.stepId == stepId && step != null)
+ {
+ return step.unlocks;
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// Gets all steps that will be unlocked by completing the given step
+ ///
+ public List GetUnlockedSteps(PuzzleStepSO completedStep)
+ {
+ return completedStep != null ? completedStep.unlocks : new List();
+ }
+
+ ///
+ /// Check if this step is an initial step (no dependencies)
+ ///
+ public bool IsInitialStep(PuzzleStepSO step)
+ {
+ return step != null && initialSteps.Contains(step);
+ }
+
+ ///
+ /// Check if all steps in this chain are completed
+ ///
+ public bool IsChainComplete(HashSet completedSteps)
+ {
+ if (completedSteps == null) return false;
+
+ foreach (var step in allSteps)
+ {
+ if (step != null && !completedSteps.Contains(step))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/Assets/Scripts/PuzzleS/PuzzleChainSO.cs.meta b/Assets/Scripts/PuzzleS/PuzzleChainSO.cs.meta
new file mode 100644
index 00000000..62518a80
--- /dev/null
+++ b/Assets/Scripts/PuzzleS/PuzzleChainSO.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 58109a40325e47f2a8a3b9264d8938dd
+timeCreated: 1760532067
\ No newline at end of file
diff --git a/Assets/Scripts/PuzzleS/PuzzleLevelDataSO.cs b/Assets/Scripts/PuzzleS/PuzzleLevelDataSO.cs
new file mode 100644
index 00000000..086a8a13
--- /dev/null
+++ b/Assets/Scripts/PuzzleS/PuzzleLevelDataSO.cs
@@ -0,0 +1,73 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.AddressableAssets;
+
+namespace PuzzleS
+{
+ ///
+ /// Represents all puzzle steps in a level.
+ /// This is automatically generated from folder structure during asset import.
+ ///
+ [CreateAssetMenu(fileName = "LevelPuzzleData", menuName = "AppleHills/Items & Puzzles/LevelPuzzleData")]
+ public class PuzzleLevelDataSO : ScriptableObject
+ {
+ ///
+ /// Unique identifier for this level, automatically set to match folder name
+ ///
+ public string levelId;
+
+ ///
+ /// Display name for this level
+ ///
+ public string displayName;
+
+ ///
+ /// All puzzle steps in this level
+ ///
+ public List allSteps = new List();
+
+ ///
+ /// Steps that should be unlocked at level start (no dependencies)
+ ///
+ public List initialSteps = new List();
+
+ ///
+ /// Pre-processed dependency data built at edit time.
+ /// Maps step IDs to arrays of dependency step IDs (which steps are required by each step)
+ ///
+ public Dictionary stepDependencies = new Dictionary();
+
+ ///
+ /// Check if all steps in the level are complete
+ ///
+ public bool IsLevelComplete(HashSet completedSteps)
+ {
+ if (completedSteps == null) return false;
+
+ foreach (var step in allSteps)
+ {
+ if (step != null && !completedSteps.Contains(step))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Gets all steps that will be unlocked by completing the given step
+ ///
+ public List GetUnlockedSteps(PuzzleStepSO completedStep)
+ {
+ return completedStep != null ? completedStep.unlocks : new List();
+ }
+
+ ///
+ /// Check if this step is an initial step (no dependencies)
+ ///
+ public bool IsInitialStep(PuzzleStepSO step)
+ {
+ return step != null && initialSteps.Contains(step);
+ }
+ }
+}
diff --git a/Assets/Scripts/PuzzleS/PuzzleLevelDataSO.cs.meta b/Assets/Scripts/PuzzleS/PuzzleLevelDataSO.cs.meta
new file mode 100644
index 00000000..0fde9380
--- /dev/null
+++ b/Assets/Scripts/PuzzleS/PuzzleLevelDataSO.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0a79780a5a0d498084afd737d4515e3b
+timeCreated: 1760532084
\ No newline at end of file
diff --git a/Assets/Scripts/PuzzleS/PuzzleManager.cs b/Assets/Scripts/PuzzleS/PuzzleManager.cs
index 61e1a2b3..1c8f522f 100644
--- a/Assets/Scripts/PuzzleS/PuzzleManager.cs
+++ b/Assets/Scripts/PuzzleS/PuzzleManager.cs
@@ -5,7 +5,11 @@ using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using AppleHills.Core.Settings;
-using Core; // Added for IInteractionSettings
+using Bootstrap;
+using Core;
+using UnityEngine.AddressableAssets;
+using UnityEngine.ResourceManagement.AsyncOperations;
+using Utils;
namespace PuzzleS
{
@@ -26,71 +30,103 @@ namespace PuzzleS
// Settings reference
private IInteractionSettings _interactionSettings;
+ // Current level puzzle data
+ private PuzzleLevelDataSO _currentLevelData;
+ private AsyncOperationHandle _levelDataLoadOperation;
+ private bool _isDataLoaded = false;
+
+ // Store registered behaviors that are waiting for data to be loaded
+ private List _registeredBehaviours = new List();
+
///
/// Singleton instance of the PuzzleManager.
///
- public static PuzzleManager Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("PuzzleManager");
- _instance = go.AddComponent();
- // DontDestroyOnLoad(go);
- }
- }
- return _instance;
- }
- }
+ public static PuzzleManager Instance => _instance;
// Events to notify about step lifecycle
public event Action OnStepCompleted;
public event Action OnStepUnlocked;
+ public event Action OnLevelDataLoaded;
+ public event Action OnAllPuzzlesComplete;
private HashSet _completedSteps = new HashSet();
private HashSet _unlockedSteps = new HashSet();
// Registration for ObjectiveStepBehaviour
private Dictionary _stepBehaviours = new Dictionary();
- // Runtime dependency graph
- private Dictionary> _runtimeDependencies = new Dictionary>();
void Awake()
{
_instance = this;
- // DontDestroyOnLoad(gameObject);
- SceneManager.sceneLoaded += OnSceneLoaded;
-
+
// Initialize settings reference
_interactionSettings = GameManager.GetSettingsObject();
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
}
-
- void Start()
+
+ private void InitializePostBoot()
{
+ // Subscribe to SceneManagerService events after boot is complete
+ SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
+ SceneManagerService.Instance.SceneLoadStarted += OnSceneLoadStarted;
+
// Find player transform
_playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
// Start proximity check coroutine
StartProximityChecks();
+
+ // Load puzzle data for the current scene if not already loading
+ if (_currentLevelData == null && !_isDataLoaded)
+ {
+ LoadPuzzleDataForCurrentScene();
+ }
+
+ Logging.Debug("[PuzzleManager] Subscribed to SceneManagerService events");
}
-
+
void OnDestroy()
{
- SceneManager.sceneLoaded -= OnSceneLoaded;
StopProximityChecks();
+
+ // Unsubscribe from scene manager events
+ if (SceneManagerService.Instance != null)
+ {
+ SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
+ SceneManagerService.Instance.SceneLoadStarted -= OnSceneLoadStarted;
+ }
+
+ // Release addressable handle if needed
+ if (_levelDataLoadOperation.IsValid())
+ {
+ Addressables.Release(_levelDataLoadOperation);
+ }
}
- private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
+ ///
+ /// Called when a scene is starting to load
+ ///
+ public void OnSceneLoadStarted(string sceneName)
{
- SceneManager.sceneLoaded -= OnSceneLoaded;
+ // Reset data loaded state when changing scenes to avoid using stale data
+ _isDataLoaded = false;
+ Logging.Debug($"[Puzzles] Scene load started: {sceneName}, marked puzzle data as not loaded");
+ }
+
+ ///
+ /// Called when a scene is loaded
+ ///
+ public void OnSceneLoadCompleted(string sceneName)
+ {
+ // Skip for non-gameplay scenes
+ if (sceneName == "BootstrapScene" || string.IsNullOrEmpty(sceneName))
+ {
+ return;
+ }
- Logging.Debug("[MDPI] OnSceneLoaded");
- _runtimeDependencies.Clear();
- BuildRuntimeDependencies();
- UnlockInitialSteps();
+ Logging.Debug($"[Puzzles] Scene loaded: {sceneName}, loading puzzle data");
+ LoadPuzzleDataForCurrentScene(sceneName);
// Find player transform again in case it changed with scene load
_playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
@@ -99,6 +135,72 @@ namespace PuzzleS
StartProximityChecks();
}
+ ///
+ /// Load puzzle data for the current scene
+ ///
+ private void LoadPuzzleDataForCurrentScene(string sceneName = null)
+ {
+ string currentScene = sceneName ?? SceneManagerService.Instance.CurrentGameplayScene;
+ if (string.IsNullOrEmpty(currentScene))
+ {
+ Logging.Warning("[Puzzles] Cannot load puzzle data: Current scene name is empty");
+ return;
+ }
+
+ _isDataLoaded = false;
+ string addressablePath = $"Puzzles/{currentScene}";
+
+ Logging.Debug($"[Puzzles] Loading puzzle data from addressable: {addressablePath}");
+
+ // Release previous handle if needed
+ if (_levelDataLoadOperation.IsValid())
+ {
+ Addressables.Release(_levelDataLoadOperation);
+ }
+
+ // Check if the addressable exists before trying to load it
+ if (!AppleHillsUtils.AddressableKeyExists(addressablePath))
+ {
+ Logging.Warning($"[Puzzles] Puzzle data does not exist for scene: {currentScene}");
+ _isDataLoaded = true; // Mark as loaded but with no data
+ _currentLevelData = null;
+ return;
+ }
+
+ // Load the level data asset
+ _levelDataLoadOperation = Addressables.LoadAssetAsync(addressablePath);
+ _levelDataLoadOperation.Completed += handle =>
+ {
+ if (handle.Status == AsyncOperationStatus.Succeeded)
+ {
+ _currentLevelData = handle.Result;
+ Logging.Debug($"[Puzzles] Loaded level data: {_currentLevelData.levelId} with {_currentLevelData.allSteps.Count} steps");
+
+ // Reset state
+ _completedSteps.Clear();
+ _unlockedSteps.Clear();
+
+ // Unlock initial steps
+ UnlockInitialSteps();
+
+ // Update all registered behaviors now that data is loaded
+ UpdateAllRegisteredBehaviors();
+
+ // Mark data as loaded
+ _isDataLoaded = true;
+
+ // Notify listeners
+ OnLevelDataLoaded?.Invoke(_currentLevelData);
+ }
+ else
+ {
+ Logging.Warning($"[Puzzles] Failed to load puzzle data for {currentScene}: {handle.OperationException?.Message}");
+ _isDataLoaded = true; // Mark as loaded but with error
+ _currentLevelData = null;
+ }
+ };
+ }
+
///
/// Start the proximity check coroutine.
///
@@ -138,7 +240,7 @@ namespace PuzzleS
foreach (var kvp in _stepBehaviours)
{
if (kvp.Value == null) continue;
- if (IsPuzzleStepCompleted(kvp.Value.stepData.stepId)) continue;
+ if (IsPuzzleStepCompleted(kvp.Key.stepId)) continue;
float distance = Vector3.Distance(_playerTransform.position, kvp.Value.transform.position);
@@ -157,6 +259,24 @@ namespace PuzzleS
}
}
+ ///
+ /// Update all registered behaviors with their current state
+ ///
+ private void UpdateAllRegisteredBehaviors()
+ {
+ foreach (var behaviour in _registeredBehaviours)
+ {
+ if (behaviour == null) continue;
+
+ // Only update if the step is in our dictionary
+ bool stepUnlocked = IsStepUnlocked(behaviour.stepData);
+ if (stepUnlocked)
+ {
+ UpdateStepState(behaviour);
+ }
+ }
+ }
+
///
/// Registers a step behaviour with the manager.
///
@@ -164,21 +284,51 @@ namespace PuzzleS
public void RegisterStepBehaviour(ObjectiveStepBehaviour behaviour)
{
if (behaviour?.stepData == null) return;
+
+ // Always add to our registered behaviors list
+ if (!_registeredBehaviours.Contains(behaviour))
+ {
+ _registeredBehaviours.Add(behaviour);
+ }
+
+ // Add to the step behaviours dictionary if not already there
if (!_stepBehaviours.ContainsKey(behaviour.stepData))
{
_stepBehaviours.Add(behaviour.stepData, behaviour);
- _runtimeDependencies.Clear();
- foreach (var step in _stepBehaviours.Values)
- {
- step.LockStep();
- }
- _unlockedSteps.Clear();
- BuildRuntimeDependencies();
- UnlockInitialSteps();
Logging.Debug($"[Puzzles] Registered step: {behaviour.stepData.stepId} on {behaviour.gameObject.name}");
+
+ // Only update state if data is already loaded
+ if (_isDataLoaded && _currentLevelData != null)
+ {
+ UpdateStepState(behaviour);
+ }
+ // Otherwise, the state will be updated when data loads in UpdateAllRegisteredBehaviors
}
}
-
+
+ ///
+ /// Updates a step's state based on the current puzzle state.
+ ///
+ private void UpdateStepState(ObjectiveStepBehaviour behaviour)
+ {
+ if (behaviour?.stepData == null) return;
+
+ // If step is already completed, ignore
+ if (_completedSteps.Contains(behaviour.stepData))
+ return;
+
+ // If step is already unlocked, update the behaviour
+ if (_unlockedSteps.Contains(behaviour.stepData))
+ {
+ behaviour.UnlockStep();
+ }
+ else
+ {
+ // Make sure it's locked
+ behaviour.LockStep();
+ }
+ }
+
///
/// Unregisters a step behaviour from the manager.
///
@@ -186,57 +336,27 @@ namespace PuzzleS
public void UnregisterStepBehaviour(ObjectiveStepBehaviour behaviour)
{
if (behaviour?.stepData == null) return;
+
_stepBehaviours.Remove(behaviour.stepData);
+ _registeredBehaviours.Remove(behaviour);
+
Logging.Debug($"[Puzzles] Unregistered step: {behaviour.stepData.stepId} on {behaviour.gameObject.name}");
}
///
- /// Builds the runtime dependency graph for all registered steps.
- ///
- private void BuildRuntimeDependencies()
- {
- _runtimeDependencies = PuzzleGraphUtility.BuildDependencyGraph(_stepBehaviours.Keys);
- foreach (var step in _runtimeDependencies.Keys)
- {
- foreach (var dep in _runtimeDependencies[step])
- {
- Logging.Debug($"[Puzzles] Step {step.stepId} depends on {dep.stepId}");
- }
- }
- Logging.Debug($"[Puzzles] Runtime dependencies built. Total steps: {_stepBehaviours.Count}");
- }
-
- ///
- /// Unlocks all initial steps (those with no dependencies) and any steps whose dependencies are already met.
+ /// Unlocks all initial steps (those with no dependencies)
///
private void UnlockInitialSteps()
{
- // First, unlock all steps with no dependencies (initial steps)
- var initialSteps = PuzzleGraphUtility.FindInitialSteps(_runtimeDependencies);
- foreach (var step in initialSteps)
+ if (_currentLevelData == null) return;
+
+ // Unlock initial steps
+ foreach (var step in _currentLevelData.initialSteps)
{
- Logging.Debug($"[Puzzles] Initial step unlocked: {step.stepId}");
UnlockStep(step);
}
- // Keep trying to unlock steps as long as we're making progress
- bool madeProgress;
- do
- {
- madeProgress = false;
-
- // Check all steps that haven't been unlocked yet
- foreach (var step in _runtimeDependencies.Keys.Where(s => !_unlockedSteps.Contains(s)))
- {
- // Check if all dependencies have been completed
- if (AreRuntimeDependenciesMet(step))
- {
- Logging.Debug($"[Puzzles] Chain step unlocked: {step.stepId}");
- UnlockStep(step);
- madeProgress = true;
- }
- }
- } while (madeProgress);
+ Logging.Debug($"[Puzzles] Unlocked {_unlockedSteps.Count} initial steps");
}
///
@@ -246,24 +366,29 @@ namespace PuzzleS
public void MarkPuzzleStepCompleted(PuzzleStepSO step)
{
if (_completedSteps.Contains(step)) return;
+ if (_currentLevelData == null) return;
+
_completedSteps.Add(step);
Logging.Debug($"[Puzzles] Step completed: {step.stepId}");
// Broadcast completion
OnStepCompleted?.Invoke(step);
- foreach (var unlock in step.unlocks)
+ // Unlock steps that are unlocked by this step
+ foreach (var unlockStep in _currentLevelData.GetUnlockedSteps(step))
{
- if (AreRuntimeDependenciesMet(unlock))
+ if (AreStepDependenciesMet(unlockStep))
{
- Logging.Debug($"[Puzzles] Unlocking step {unlock.stepId} after completing {step.stepId}");
- UnlockStep(unlock);
+ Logging.Debug($"[Puzzles] Unlocking step {unlockStep.stepId} after completing {step.stepId}");
+ UnlockStep(unlockStep);
}
else
{
- Logging.Debug($"[Puzzles] Step {unlock.stepId} not unlocked yet, waiting for other dependencies");
+ Logging.Debug($"[Puzzles] Step {unlockStep.stepId} not unlocked yet, waiting for other dependencies");
}
}
+
+ // Check if all puzzle steps are now complete
CheckPuzzleCompletion();
}
@@ -272,13 +397,33 @@ namespace PuzzleS
///
/// The step to check.
/// True if all dependencies are met, false otherwise.
- private bool AreRuntimeDependenciesMet(PuzzleStepSO step)
+ private bool AreStepDependenciesMet(PuzzleStepSO step)
{
- if (!_runtimeDependencies.ContainsKey(step) || _runtimeDependencies[step].Count == 0) return true;
- foreach (var dep in _runtimeDependencies[step])
+ if (_currentLevelData == null || step == null) return false;
+
+ // If it's an initial step, it has no dependencies
+ if (_currentLevelData.IsInitialStep(step)) return true;
+
+ // Check if dependencies are met using pre-processed data
+ if (_currentLevelData.stepDependencies.TryGetValue(step.stepId, out string[] dependencies))
{
- if (!_completedSteps.Contains(dep)) return false;
+ foreach (var depId in dependencies)
+ {
+ // Find the dependency step
+ bool dependencyMet = false;
+ foreach (var completedStep in _completedSteps)
+ {
+ if (completedStep.stepId == depId)
+ {
+ dependencyMet = true;
+ break;
+ }
+ }
+
+ if (!dependencyMet) return false;
+ }
}
+
return true;
}
@@ -290,6 +435,7 @@ namespace PuzzleS
{
if (_unlockedSteps.Contains(step)) return;
_unlockedSteps.Add(step);
+
if (_stepBehaviours.TryGetValue(step, out var behaviour))
{
behaviour.UnlockStep();
@@ -301,14 +447,18 @@ namespace PuzzleS
}
///
- /// Checks if the puzzle is complete (all steps finished).
+ /// Checks if the puzzle is complete (all steps in level finished).
///
private void CheckPuzzleCompletion()
{
- if (_completedSteps.Count == _stepBehaviours.Count)
+ if (_currentLevelData == null) return;
+
+ if (_currentLevelData.IsLevelComplete(_completedSteps))
{
- Logging.Debug("[Puzzles] Puzzle complete! All steps finished.");
- // TODO: Fire puzzle complete event or trigger outcome logic
+ Logging.Debug("[Puzzles] All puzzles complete! Level finished.");
+
+ // Fire level complete event
+ OnAllPuzzlesComplete?.Invoke(_currentLevelData);
}
}
@@ -317,9 +467,6 @@ namespace PuzzleS
///
public bool IsStepUnlocked(PuzzleStepSO step)
{
- // _runtimeDependencies.Clear();
- // BuildRuntimeDependencies();
- // UnlockInitialSteps();
return _unlockedSteps.Contains(step);
}
@@ -332,6 +479,23 @@ namespace PuzzleS
{
return _completedSteps.Any(step => step.stepId == stepId);
}
+
+ ///
+ /// Get the current level puzzle data
+ ///
+ public PuzzleLevelDataSO GetCurrentLevelData()
+ {
+ return _currentLevelData;
+ }
+
+ ///
+ /// Checks if puzzle data is loaded
+ ///
+ /// True if data loading has completed (whether successful or not)
+ public bool IsDataLoaded()
+ {
+ return _isDataLoaded;
+ }
void OnApplicationQuit()
{
diff --git a/Assets/Scripts/PuzzleS/PuzzleStepSO.cs b/Assets/Scripts/PuzzleS/PuzzleStepSO.cs
index 7a2bd299..8de0850c 100644
--- a/Assets/Scripts/PuzzleS/PuzzleStepSO.cs
+++ b/Assets/Scripts/PuzzleS/PuzzleStepSO.cs
@@ -1,5 +1,6 @@
using UnityEngine;
using System.Collections.Generic;
+using System;
///
/// ScriptableObject representing a single puzzle step, its display info, and which steps it unlocks.
@@ -29,4 +30,58 @@ public class PuzzleStepSO : ScriptableObject
///
[Header("Unlocks")]
public List unlocks = new List();
+
+ ///
+ /// Override Equals to compare by stepId rather than reference equality.
+ /// This ensures consistent behavior across different platforms (Editor vs Mobile).
+ ///
+ /// Object to compare to
+ /// True if the objects represent the same puzzle step
+ public override bool Equals(object obj)
+ {
+ if (obj == null) return false;
+
+ // Check if the object is actually a PuzzleStepSO
+ PuzzleStepSO other = obj as PuzzleStepSO;
+ if (other == null) return false;
+
+ // Compare by stepId instead of reference
+ return string.Equals(stepId, other.stepId, StringComparison.Ordinal);
+ }
+
+ ///
+ /// Override GetHashCode to be consistent with the Equals method.
+ /// This is crucial for HashSet and Dictionary to work properly.
+ ///
+ /// Hash code based on stepId
+ public override int GetHashCode()
+ {
+ // Generate hash code from stepId to ensure consistent hashing
+ return stepId != null ? stepId.GetHashCode() : 0;
+ }
+
+ ///
+ /// Override == operator to use our custom equality logic
+ ///
+ public static bool operator ==(PuzzleStepSO a, PuzzleStepSO b)
+ {
+ // Check if both are null or if they're the same instance
+ if (ReferenceEquals(a, b))
+ return true;
+
+ // Check if either is null (but not both, as that's handled above)
+ if (((object)a == null) || ((object)b == null))
+ return false;
+
+ // Use our custom Equals method
+ return a.Equals(b);
+ }
+
+ ///
+ /// Override != operator to be consistent with == operator
+ ///
+ public static bool operator !=(PuzzleStepSO a, PuzzleStepSO b)
+ {
+ return !(a == b);
+ }
}
diff --git a/Assets/Scripts/Settings/SceneOrientationConfig.cs b/Assets/Scripts/Settings/SceneOrientationConfig.cs
index fe92171c..34c76e11 100644
--- a/Assets/Scripts/Settings/SceneOrientationConfig.cs
+++ b/Assets/Scripts/Settings/SceneOrientationConfig.cs
@@ -23,7 +23,7 @@ namespace Settings
return entry.requiredOrientation;
}
// Default to Portrait if not found
- return ScreenOrientationRequirement.Portrait;
+ return ScreenOrientationRequirement.NotApplicable;
}
}
}
diff --git a/Assets/Scripts/Settings/ScreenOrientationRequirement.cs b/Assets/Scripts/Settings/ScreenOrientationRequirement.cs
index d91adc13..60a52f01 100644
--- a/Assets/Scripts/Settings/ScreenOrientationRequirement.cs
+++ b/Assets/Scripts/Settings/ScreenOrientationRequirement.cs
@@ -3,7 +3,8 @@
public enum ScreenOrientationRequirement
{
Portrait,
- Landscape
+ Landscape,
+ NotApplicable
}
}
diff --git a/Assets/Scripts/UI/CardSystem/UIPageController.cs b/Assets/Scripts/UI/CardSystem/UIPageController.cs
index 0d6c96ff..b9f4a220 100644
--- a/Assets/Scripts/UI/CardSystem/UIPageController.cs
+++ b/Assets/Scripts/UI/CardSystem/UIPageController.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Bootstrap;
using Core;
using UnityEngine;
@@ -29,6 +30,15 @@ namespace AppleHills.UI.CardSystem
}
_instance = this;
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
+ private void InitializePostBoot()
+ {
+ // Initialize any dependencies that require other services to be ready
+ Logging.Debug("[UIPageController] Post-boot initialization complete");
}
///
diff --git a/Assets/Scripts/UI/LoadingScreenController.cs b/Assets/Scripts/UI/LoadingScreenController.cs
index e902b7f7..045c9c23 100644
--- a/Assets/Scripts/UI/LoadingScreenController.cs
+++ b/Assets/Scripts/UI/LoadingScreenController.cs
@@ -1,5 +1,6 @@
using System.Collections;
using System;
+using Bootstrap;
using UnityEngine;
using UnityEngine.UI;
using Core;
@@ -48,22 +49,10 @@ namespace UI
///
public bool IsActive => loadingScreenContainer != null && loadingScreenContainer.activeSelf;
- public static LoadingScreenController Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("LoadingScreenController");
- _instance = go.AddComponent();
- }
- }
- return _instance;
- }
- }
+ ///
+ /// Singleton instance of the LoadingScreenController. No longer creates an instance if one doesn't exist.
+ ///
+ public static LoadingScreenController Instance => _instance;
private void Awake()
{
@@ -77,6 +66,15 @@ namespace UI
{
loadingScreenContainer.SetActive(false);
}
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
+ private void InitializePostBoot()
+ {
+ // Initialize any dependencies that require other services to be ready
+ Logging.Debug("[LoadingScreenController] Post-boot initialization complete");
}
///
diff --git a/Assets/Scripts/UI/PauseMenu.cs b/Assets/Scripts/UI/PauseMenu.cs
index 54933f19..55f132e0 100644
--- a/Assets/Scripts/UI/PauseMenu.cs
+++ b/Assets/Scripts/UI/PauseMenu.cs
@@ -3,6 +3,7 @@ using Core;
using UnityEngine;
using UnityEngine.SceneManagement;
using Input;
+using Bootstrap;
namespace UI
{
@@ -11,23 +12,10 @@ namespace UI
private static PauseMenu _instance;
private static bool _isQuitting;
- public static PauseMenu Instance
- {
- get
- {
- if (_instance == null && Application.isPlaying && !_isQuitting)
- {
- _instance = FindAnyObjectByType();
- if (_instance == null)
- {
- var go = new GameObject("PauseMenu");
- _instance = go.AddComponent();
- // DontDestroyOnLoad(go);
- }
- }
- return _instance;
- }
- }
+ ///
+ /// Singleton instance of the PauseMenu. No longer creates an instance if one doesn't exist.
+ ///
+ public static PauseMenu Instance => _instance;
[Header("UI References")]
[SerializeField] private GameObject pauseMenuPanel;
@@ -43,18 +31,30 @@ namespace UI
///
public bool IsPaused => _isPaused;
- private void Start()
+ private void Awake()
+ {
+ _instance = this;
+
+ // Register for post-boot initialization
+ BootCompletionService.RegisterInitAction(InitializePostBoot);
+ }
+
+ private void InitializePostBoot()
{
// Subscribe to scene loaded events
SceneManagerService.Instance.SceneLoadCompleted += SetPauseMenuByLevel;
+ // SceneManagerService subscription moved to InitializePostBoot
+
// Set initial state based on current scene
SetPauseMenuByLevel(SceneManager.GetActiveScene().name);
- #if UNITY_EDITOR
+#if UNITY_EDITOR
// Initialize pause menu state
HidePauseMenu(false);
- #endif
+#endif
+
+ Logging.Debug("[PauseMenu] Subscribed to SceneManagerService events");
}
private void OnDestroy()
@@ -81,9 +81,11 @@ namespace UI
return;
bool isMainMenu = levelName.ToLower().Contains("mainmenu");
- gameObject.SetActive(!isMainMenu);
+ bool isStartingLevel = levelName.ToLower().Contains("startingscene");
- if(!isMainMenu)
+ gameObject.SetActive(!(isMainMenu || isStartingLevel));
+
+ if(!isMainMenu && !isStartingLevel)
HidePauseMenu(false); // Ensure menu is hidden when switching to a game level
Logging.Debug($"[PauseMenu] Setting pause menu active: {!isMainMenu} for scene: {levelName}");
diff --git a/Assets/Scripts/UI/Tutorial/DivingTutorial.cs b/Assets/Scripts/UI/Tutorial/DivingTutorial.cs
index a7368ff4..16e9a8e6 100644
--- a/Assets/Scripts/UI/Tutorial/DivingTutorial.cs
+++ b/Assets/Scripts/UI/Tutorial/DivingTutorial.cs
@@ -42,22 +42,21 @@ public class DivingTutorial : MonoBehaviour, ITouchInputConsumer
public void OnTap(Vector2 position)
{
-
stateMachine.Next(true);
}
public void OnHoldStart(Vector2 position)
{
- throw new System.NotImplementedException();
+ return;
}
public void OnHoldMove(Vector2 position)
{
- throw new System.NotImplementedException();
+ return;
}
public void OnHoldEnd(Vector2 position)
{
- throw new System.NotImplementedException();
+ return;
}
}
diff --git a/Assets/Scripts/Utils/AppleHillsUtils.cs b/Assets/Scripts/Utils/AppleHillsUtils.cs
index 62df8b17..7a90a65f 100644
--- a/Assets/Scripts/Utils/AppleHillsUtils.cs
+++ b/Assets/Scripts/Utils/AppleHillsUtils.cs
@@ -1,6 +1,9 @@
-using UnityEngine;
+using System.Collections.Generic;
+using UnityEngine;
using AppleHills.Core.Settings;
using Core;
+using UnityEngine.AddressableAssets;
+using UnityEngine.ResourceManagement.ResourceLocations;
namespace Utils
{
@@ -68,5 +71,11 @@ namespace Utils
// Apply screen normalization
return frameAdjustedSpeed * screenNormalizationFactor;
}
+
+ public static bool AddressableKeyExists(object key)
+ {
+ IList locations;
+ return Addressables.LoadResourceLocationsAsync(key).WaitForCompletion()?.Count > 0;
+ }
}
}
diff --git a/Assets/Scripts/found_instances b/Assets/Scripts/found_instances
new file mode 100644
index 00000000..8dca3309
--- /dev/null
+++ b/Assets/Scripts/found_instances
@@ -0,0 +1,207 @@
+Targets
+ Occurrences of '.Instance' in Directory C:\Users\info\Desktop\repos\AppleHillsProduction\Assets\Scripts
+Found usages (163 usages found)
+ (163 usages found)
+ Assets (163 usages found)
+ Scripts (163 usages found)
+ Bootstrap (3 usages found)
+ BootSceneController.cs (3 usages found)
+ 79 if (CinematicsManager.Instance != null)
+ 84 CinematicsManager.Instance.LoadAndPlayCinematic("IntroSequence");
+ 205 SceneManagerService.Instance.CurrentGameplayScene = mainMenuSceneName;
+ Cinematics (16 usages found)
+ SkipCinematic.cs (16 usages found)
+ 57 if (CinematicsManager.Instance == null) return;
+ 63 CinematicsManager.Instance.OnCinematicStarted += HandleCinematicStarted;
+ 64 CinematicsManager.Instance.OnCinematicStopped += HandleCinematicStopped;
+ 67 if (CinematicsManager.Instance.IsCinematicPlaying)
+ 75 if (CinematicsManager.Instance != null)
+ 77 CinematicsManager.Instance.OnCinematicStarted -= HandleCinematicStarted;
+ 78 CinematicsManager.Instance.OnCinematicStopped -= HandleCinematicStopped;
+ 83 if (InputManager.Instance != null)
+ 85 InputManager.Instance.UnregisterOverrideConsumer(this);
+ 92 if (InputManager.Instance != null)
+ 94 InputManager.Instance.RegisterOverrideConsumer(this);
+ 100 if (InputManager.Instance != null)
+ 102 InputManager.Instance.UnregisterOverrideConsumer(this);
+ 109 if (_isHolding && CinematicsManager.Instance.IsCinematicPlaying)
+ 131 CinematicsManager.Instance.SkipCurrentCinematic();
+ 141 InputManager.Instance.UnregisterOverrideConsumer(this);
+ Core (25 usages found)
+ GameManager.cs (8 usages found)
+ 65 SettingsProvider.Instance.gameObject.name = "Settings Provider";
+ 68 DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider";
+ 88 PauseMenu pauseMenu = PauseMenu.Instance;
+ 220 var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous();
+ 221 var interactionSettings = SettingsProvider.Instance.LoadSettingsSynchronous();
+ 222 var minigameSettings = SettingsProvider.Instance.LoadSettingsSynchronous();
+ 275 var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings();
+ 318 return DeveloperSettingsProvider.Instance?.GetSettings();
+ ItemManager.cs (3 usages found)
+ 80 SceneManagerService.Instance.SceneLoadStarted += OnSceneLoadStarted;
+ 87 if (SceneManagerService.Instance != null)
+ 88 SceneManagerService.Instance.SceneLoadStarted -= OnSceneLoadStarted;
+ QuickAccess.cs (7 usages found)
+ 47 public GameManager GameManager => GameManager.Instance;
+ 48 public ItemManager ItemManager => ItemManager.Instance;
+ 49 public SceneManagerService SceneManager => SceneManagerService.Instance;
+ 52 public InputManager InputManager => InputManager.Instance;
+ 53 public PuzzleManager PuzzleManager => PuzzleManager.Instance;
+ 54 public CinematicsManager CinematicsManager => CinematicsManager.Instance;
+ 55 public CardSystemManager CardSystemManager => CardSystemManager.Instance;
+ SceneManagerService.cs (1 usage found)
+ 110 _loadingScreen = LoadingScreenController.Instance;
+ SceneOrientationEnforcer.cs (3 usages found)
+ 106 InputManager.Instance.SetInputMode(InputMode.UI);
+ 159 InputManager.Instance.SetInputMode(InputMode.UI);
+ 189 InputManager.Instance.SetInputMode(InputMode.Game);
+ SettingsAccess.cs (3 usages found)
+ 43 return GameManager.Instance.PlayerStopDistance;
+ 55 return GameManager.Instance.PlayerStopDistanceDirectInteraction;
+ 67 return GameManager.Instance.DefaultPuzzlePromptRange;
+ Dialogue (26 usages found)
+ DialogueComponent.cs (26 usages found)
+ 39 if (PuzzleManager.Instance != null)
+ 40 PuzzleManager.Instance.OnStepCompleted += OnAnyPuzzleStepCompleted;
+ 42 if (ItemManager.Instance != null)
+ 44 ItemManager.Instance.OnItemPickedUp += OnAnyItemPickedUp;
+ 45 ItemManager.Instance.OnCorrectItemSlotted += OnAnyItemSlotted;
+ 46 ItemManager.Instance.OnIncorrectItemSlotted += OnAnyIncorrectItemSlotted;
+ 47 ItemManager.Instance.OnForbiddenItemSlotted += OnAnyForbiddenItemSlotted;
+ 48 ItemManager.Instance.OnItemSlotCleared += OnAnyItemSlotCleared;
+ 49 ItemManager.Instance.OnItemsCombined += OnAnyItemsCombined;
+ 170 if (PuzzleManager.Instance != null)
+ 171 PuzzleManager.Instance.OnStepCompleted -= OnAnyPuzzleStepCompleted;
+ 173 if (ItemManager.Instance != null)
+ 175 ItemManager.Instance.OnItemPickedUp -= OnAnyItemPickedUp;
+ 176 ItemManager.Instance.OnCorrectItemSlotted -= OnAnyItemSlotted;
+ 177 ItemManager.Instance.OnIncorrectItemSlotted -= OnAnyIncorrectItemSlotted;
+ 178 ItemManager.Instance.OnForbiddenItemSlotted -= OnAnyForbiddenItemSlotted;
+ 179 ItemManager.Instance.OnItemSlotCleared -= OnAnyItemSlotCleared;
+ 180 ItemManager.Instance.OnItemsCombined -= OnAnyItemsCombined;
+ 600 return PuzzleManager.Instance != null &&
+ 601 PuzzleManager.Instance.IsPuzzleStepCompleted(stepID);
+ 606 if (ItemManager.Instance == null) return false;
+ 609 foreach (var pickup in ItemManager.Instance.Pickups)
+ 622 if (ItemManager.Instance == null) return false;
+ 625 foreach (var slot in ItemManager.Instance.ItemSlots)
+ 638 if (ItemManager.Instance == null) return false;
+ 641 return ItemManager.Instance.WasItemCreatedThroughCombination(resultItemId);
+ Input (4 usages found)
+ InputManager.cs (3 usages found)
+ 96 SceneManagerService.Instance.SceneLoadCompleted += SwitchInputOnSceneLoaded;
+ 103 if (SceneManagerService.Instance != null)
+ 104 SceneManagerService.Instance.SceneLoadCompleted -= SwitchInputOnSceneLoaded;
+ PlayerTouchController.cs (1 usage found)
+ 77 InputManager.Instance?.SetDefaultConsumer(this);
+ Interactions (6 usages found)
+ Interactable.cs (2 usages found)
+ 213 ? GameManager.Instance.PlayerStopDistance
+ 214 : GameManager.Instance.PlayerStopDistanceDirectInteraction;
+ ItemSlot.cs (2 usages found)
+ 222 ItemManager.Instance?.RegisterItemSlot(this);
+ 227 ItemManager.Instance?.UnregisterItemSlot(this);
+ Pickup.cs (2 usages found)
+ 49 ItemManager.Instance?.RegisterPickup(this);
+ 64 ItemManager.Instance?.UnregisterPickup(this);
+ LevelS (3 usages found)
+ LevelSwitch.cs (3 usages found)
+ 114 InputManager.Instance.SetInputMode(InputMode.UI);
+ 120 await SceneManagerService.Instance.SwitchSceneAsync(switchData.targetLevelSceneName, progress);
+ 126 InputManager.Instance.SetInputMode(InputMode.GameAndUI);
+ Minigames (43 usages found)
+ DivingForPictures (43 usages found)
+ Bubbles (2 usages found)
+ BubbleSpawner.cs (2 usages found)
+ 70 DivingGameManager.Instance.RegisterPausableComponent(this);
+ 78 DivingGameManager.Instance.UnregisterPausableComponent(this);
+ Obstacles (5 usages found)
+ ObstacleSpawner.cs (5 usages found)
+ 88 DivingGameManager.Instance.OnGameInitialized += Initialize;
+ 91 DivingGameManager.Instance.RegisterPausableComponent(this);
+ 94 if (DivingGameManager.Instance.GetType().GetField("_isGameInitialized",
+ 96 System.Reflection.BindingFlags.Instance)?.GetValue(DivingGameManager.Instance) is bool isInitialized && isInitialized)
+ 104 DivingGameManager.Instance.UnregisterPausableComponent(this);
+ PictureCamera (2 usages found)
+ Viewfinder.cs (2 usages found)
+ 89 InputManager.Instance.RegisterOverrideConsumer(this);
+ 94 InputManager.Instance.UnregisterOverrideConsumer(this);
+ Player (6 usages found)
+ PlayerCollisionBehavior.cs (1 usage found)
+ 262 .GetField("_targetFingerX", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ PlayerController.cs (5 usages found)
+ 83 DivingGameManager.Instance.OnGameInitialized += Initialize;
+ 86 if (DivingGameManager.Instance.GetType().GetField("_isGameInitialized",
+ 88 System.Reflection.BindingFlags.Instance)?.GetValue(DivingGameManager.Instance) is bool isInitialized && isInitialized)
+ 102 InputManager.Instance?.SetDefaultConsumer(this);
+ 110 DivingGameManager.Instance.OnGameInitialized -= Initialize;
+ Tiles (5 usages found)
+ TrenchTileSpawner.cs (5 usages found)
+ 171 DivingGameManager.Instance.OnGameInitialized += Initialize;
+ 174 DivingGameManager.Instance.RegisterPausableComponent(this);
+ 177 if (DivingGameManager.Instance.GetType().GetField("_isGameInitialized",
+ 179 System.Reflection.BindingFlags.Instance)?.GetValue(DivingGameManager.Instance) is bool isInitialized && isInitialized)
+ 187 DivingGameManager.Instance.UnregisterPausableComponent(this);
+ Utilities (2 usages found)
+ BottlePauser.cs (1 usage found)
+ 14 DivingGameManager.Instance.RegisterPausableComponent(this);
+ RockPauser.cs (1 usage found)
+ 15 DivingGameManager.Instance.RegisterPausableComponent(this);
+ DivingGameManager.cs (16 usages found)
+ 142 PauseMenu pauseMenu = PauseMenu.Instance;
+ 156 if (GameManager.Instance != null)
+ 158 GameManager.Instance.RegisterPausableComponent(this);
+ 162 if (SceneOrientationEnforcer.Instance != null)
+ 164 SceneOrientationEnforcer.Instance.OnOrientationCorrect += InitializeGame;
+ 165 SceneOrientationEnforcer.Instance.OnOrientationIncorrect += Pause;
+ 169 if (SceneOrientationEnforcer.Instance.IsOrientationCorrect())
+ 186 viewfinderManager = CameraViewfinderManager.Instance;
+ 212 if (SceneOrientationEnforcer.Instance != null)
+ 214 SceneOrientationEnforcer.Instance.OnOrientationCorrect -= InitializeGame;
+ 215 SceneOrientationEnforcer.Instance.OnOrientationIncorrect -= Pause;
+ 219 PauseMenu pauseMenu = PauseMenu.Instance;
+ 227 if (GameManager.Instance != null)
+ 229 GameManager.Instance.UnregisterPausableComponent(this);
+ 801 InputManager.Instance.SetInputMode(InputMode.GameAndUI);
+ 822 InputManager.Instance.SetInputMode(InputMode.GameAndUI);
+ DivingScoreUI.cs (5 usages found)
+ 15 DivingGameManager.Instance.OnScoreChanged += UpdateScoreDisplay;
+ 16 DivingGameManager.Instance.OnPictureTaken += ShowScorePopup;
+ 19 UpdateScoreDisplay(DivingGameManager.Instance.PlayerScore);
+ 32 DivingGameManager.Instance.OnScoreChanged -= UpdateScoreDisplay;
+ 33 DivingGameManager.Instance.OnPictureTaken -= ShowScorePopup;
+ PuzzleS (7 usages found)
+ ObjectiveStepBehaviour.cs (3 usages found)
+ 76 PuzzleManager.Instance?.RegisterStepBehaviour(this);
+ 86 PuzzleManager.Instance?.UnregisterStepBehaviour(this);
+ 304 PuzzleManager.Instance?.MarkPuzzleStepCompleted(stepData);
+ PuzzleManager.cs (4 usages found)
+ 102 SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
+ 111 if (SceneManagerService.Instance != null)
+ 113 SceneManagerService.Instance.SceneLoadCompleted -= OnSceneLoadCompleted;
+ 149 string currentScene = sceneName ?? SceneManagerService.Instance.CurrentGameplayScene;
+ StateMachines (2 usages found)
+ Quarry (2 usages found)
+ AnneLise (2 usages found)
+ TakePhotoState.cs (2 usages found)
+ 39 InputManager.Instance.SetInputMode(InputMode.InputDisabled);
+ 47 InputManager.Instance.SetInputMode(InputMode.Game);
+ UI (21 usages found)
+ Tutorial (2 usages found)
+ DivingTutorial.cs (2 usages found)
+ 30 InputManager.Instance.RegisterOverrideConsumer(this);
+ 39 InputManager.Instance.UnregisterOverrideConsumer(this);
+ MainMenu.cs (1 usage found)
+ 12 await SceneManagerService.Instance.SwitchSceneAsync("AppleHillsOverworld", progress);
+ PauseMenu.cs (11 usages found)
+ 71 SceneManagerService.Instance.SceneLoadCompleted += SetPauseMenuByLevel;
+ 78 if (SceneManagerService.Instance != null)
+ 80 SceneManagerService.Instance.SceneLoadCompleted -= SetPauseMenuByLevel;
+ 122 InputManager.Instance.SetInputMode(InputMode.UI);
+ 141 InputManager.Instance.SetInputMode(InputMode.GameAndUI);
+ 162 await SceneManagerService.Instance.SwitchSceneAsync("MainMenu", progress);
+ 180 await SceneManagerService.Instance.ReloadCurrentScene(progress);
+ 198 await SceneManagerService.Instance.SwitchSceneAsync("MainMenu", progress);
+ 201 await SceneManagerService.Instance.SwitchSceneAsync("AppleHillsOverworld", progress);
+ 204 await SceneManagerService.Instance.SwitchSceneAsync("Quarry", progress);
+ 207 await SceneManagerService.Instance.SwitchSceneAsync("DivingForPictures", progress);
diff --git a/Assets/Scripts/found_instances.meta b/Assets/Scripts/found_instances.meta
new file mode 100644
index 00000000..3fcffc60
--- /dev/null
+++ b/Assets/Scripts/found_instances.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: faea13914b0c70740accf1eeb03f11aa
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset
index cf1d829f..9ac34527 100644
--- a/ProjectSettings/EditorBuildSettings.asset
+++ b/ProjectSettings/EditorBuildSettings.asset
@@ -5,6 +5,9 @@ EditorBuildSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes:
+ - enabled: 1
+ path: Assets/Scenes/StartingScene.unity
+ guid: cf01e2d0135b06c4486d00ef393d0274
- enabled: 1
path: Assets/Scenes/MainMenu.unity
guid: b93f2f3b39a62684c8474ba79c8f698d
diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset
index 5f084eba..1e425d08 100644
--- a/ProjectSettings/ProjectSettings.asset
+++ b/ProjectSettings/ProjectSettings.asset
@@ -329,42 +329,6 @@ PlayerSettings:
m_Height: 36
m_Kind: 0
m_SubKind:
- - m_Textures:
- - {fileID: 2800000, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
- - {fileID: 2800000, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
- m_Width: 432
- m_Height: 432
- m_Kind: 2
- m_SubKind:
- - m_Textures:
- - {fileID: 2800000, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
- - {fileID: 2800000, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
- m_Width: 324
- m_Height: 324
- m_Kind: 2
- m_SubKind:
- - m_Textures:
- - {fileID: 2800000, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
- - {fileID: 2800000, guid: 00354ded9d8f8d643acc14837a229544, type: 3}
- m_Width: 216
- m_Height: 216
- m_Kind: 2
- m_SubKind:
- - m_Textures: []
- m_Width: 162
- m_Height: 162
- m_Kind: 2
- m_SubKind:
- - m_Textures: []
- m_Width: 108
- m_Height: 108
- m_Kind: 2
- m_SubKind:
- - m_Textures: []
- m_Width: 81
- m_Height: 81
- m_Kind: 2
- m_SubKind:
- m_Textures: []
m_Width: 192
m_Height: 192
@@ -395,6 +359,42 @@ PlayerSettings:
m_Height: 36
m_Kind: 1
m_SubKind:
+ - m_Textures:
+ - {fileID: 2800000, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3}
+ - {fileID: 2800000, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3}
+ m_Width: 432
+ m_Height: 432
+ m_Kind: 2
+ m_SubKind:
+ - m_Textures:
+ - {fileID: 2800000, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3}
+ - {fileID: 2800000, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3}
+ m_Width: 324
+ m_Height: 324
+ m_Kind: 2
+ m_SubKind:
+ - m_Textures:
+ - {fileID: 2800000, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3}
+ - {fileID: 2800000, guid: 6767e1e5c0a16f14e926a72a81bf95cb, type: 3}
+ m_Width: 216
+ m_Height: 216
+ m_Kind: 2
+ m_SubKind:
+ - m_Textures: []
+ m_Width: 162
+ m_Height: 162
+ m_Kind: 2
+ m_SubKind:
+ - m_Textures: []
+ m_Width: 108
+ m_Height: 108
+ m_Kind: 2
+ m_SubKind:
+ - m_Textures: []
+ m_Width: 81
+ m_Height: 81
+ m_Kind: 2
+ m_SubKind:
- m_BuildTarget: iPhone
m_Icons:
- m_Textures: []