diff --git a/Assets/Prefabs/UI/Cards/CardSystem.prefab b/Assets/Prefabs/UI/Cards/CardSystem.prefab index da638723..6059321a 100644 --- a/Assets/Prefabs/UI/Cards/CardSystem.prefab +++ b/Assets/Prefabs/UI/Cards/CardSystem.prefab @@ -299,7 +299,7 @@ GameObject: m_Component: - component: {fileID: 8855931927481658112} - component: {fileID: 5244731468946939147} - - component: {fileID: 2860077094930428643} + - component: {fileID: 7848685733999064403} m_Layer: 5 m_Name: Backback m_TagString: Untagged @@ -372,7 +372,7 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] ---- !u!114 &2860077094930428643 +--- !u!114 &7848685733999064403 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -381,9 +381,9 @@ MonoBehaviour: m_GameObject: {fileID: 7564017895147059150} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e7ea50a695c58944799b4f27a9014301, type: 3} + m_Script: {fileID: 11500000, guid: 494d0aedce9744308499355006071138, type: 3} m_Name: - m_EditorClassIdentifier: '::' + m_EditorClassIdentifier: AppleHillsScripts::UI.DummyInput --- !u!1 &8561999612656273135 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/UI/Tutorial.prefab b/Assets/Prefabs/UI/Tutorial.prefab index 7d60f71d..c395a637 100644 --- a/Assets/Prefabs/UI/Tutorial.prefab +++ b/Assets/Prefabs/UI/Tutorial.prefab @@ -75,6 +75,135 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &1188952073410115051 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4893439260580271593} + - component: {fileID: 2903185199836080833} + m_Layer: 0 + m_Name: TutorialAudio + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4893439260580271593 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1188952073410115051} + serializedVersion: 2 + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -80.867836, y: 74.80073, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3183207532655435649} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!82 &2903185199836080833 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1188952073410115051} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 3533147658878909314, guid: 727a7e4b6df4b0d47897f7d8ee7fa323, type: 2} + m_audioClip: {fileID: 0} + m_Resource: {fileID: 8300000, guid: fca641cdc8dcd074483fad3db1cbe24c, type: 3} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 --- !u!1 &1590200553513530823 GameObject: m_ObjectHideFlags: 0 @@ -242,7 +371,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!4 &1831065641766120066 Transform: m_ObjectHideFlags: 0 @@ -263,7 +392,7 @@ Transform: - {fileID: 8442487091540635686} - {fileID: 8879908034887781514} - {fileID: 2211814522314670326} - m_Father: {fileID: 4592376486413311028} + m_Father: {fileID: 9007881587001685567} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!111 &1617577586792587006 Animation: @@ -312,7 +441,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &4249054603034392046 Transform: m_ObjectHideFlags: 0 @@ -330,7 +459,7 @@ Transform: - {fileID: 3606233682966098341} - {fileID: 9050619600904836405} - {fileID: 4757850631734005462} - m_Father: {fileID: 4592376486413311028} + m_Father: {fileID: 9007881587001685567} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!111 &6215866005480392352 Animation: @@ -393,6 +522,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 4592376486413311028} + - {fileID: 4893439260580271593} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &4267886887244421663 @@ -407,8 +537,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: c0f21d12fb6a50242b483dbdc3a6a1df, type: 3} m_Name: m_EditorClassIdentifier: '::' - divingGameManager: {fileID: 0} - playTutorial: 0 + playTutorial: 1 + tapPrompt: {fileID: 6751368003081662277} --- !u!1 &3503751741805984614 GameObject: m_ObjectHideFlags: 0 @@ -443,8 +573,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 13.456177, y: -2.627655} - m_SizeDelta: {x: 1204.5154, y: 802.1875} + m_AnchoredPosition: {x: 22.853088, y: -89.07968} + m_SizeDelta: {x: 1185.7214, y: 975.0914} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &3845799704385814770 CanvasRenderer: @@ -860,6 +990,43 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &5541992152058962912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9007881587001685567} + m_Layer: 5 + m_Name: TutorialScreens + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9007881587001685567 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5541992152058962912} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4249054603034392046} + - {fileID: 1831065641766120066} + m_Father: {fileID: 4592376486413311028} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &6118149484066388440 GameObject: m_ObjectHideFlags: 0 @@ -894,8 +1061,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 13.456177, y: -2.627655} - m_SizeDelta: {x: 1204.5154, y: 802.1875} + m_AnchoredPosition: {x: 22.853088, y: -89.07971} + m_SizeDelta: {x: 1185.721, y: 975.0914} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &4855560808002867609 CanvasRenderer: @@ -1010,6 +1177,142 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &6751368003081662277 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5554143010115146741} + - component: {fileID: 3254928157304415694} + - component: {fileID: 490204079952288596} + m_Layer: 5 + m_Name: Text (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5554143010115146741 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6751368003081662277} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4592376486413311028} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.000015258789, y: -51.398} + m_SizeDelta: {x: 658.8605, y: 102.796875} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3254928157304415694 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6751368003081662277} + m_CullTransparentMesh: 1 +--- !u!114 &490204079952288596 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6751368003081662277} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: TAP TO SKIP + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 75 + m_fontSizeBase: 75 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 1 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} --- !u!1 &7059429923783536925 GameObject: m_ObjectHideFlags: 0 @@ -1043,8 +1346,8 @@ RectTransform: m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 4249054603034392046} - - {fileID: 1831065641766120066} + - {fileID: 9007881587001685567} + - {fileID: 5554143010115146741} m_Father: {fileID: 3183207532655435649} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -1070,7 +1373,7 @@ Canvas: m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 0 - m_AdditionalShaderChannelsFlag: 0 + m_AdditionalShaderChannelsFlag: 25 m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 diff --git a/Assets/Scenes/MiniGames/DivingForPictures.unity b/Assets/Scenes/MiniGames/DivingForPictures.unity index 9189303a..356154fc 100644 --- a/Assets/Scenes/MiniGames/DivingForPictures.unity +++ b/Assets/Scenes/MiniGames/DivingForPictures.unity @@ -342,16 +342,16 @@ LineRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_Positions: - - {x: -0.15602553, y: 4.0749445, z: 0} - - {x: -0.1566351, y: 3.9736378, z: 0} - - {x: -0.1572447, y: 3.8729858, z: 0} + - {x: -0.15602553, y: 4.074945, z: 0} + - {x: -0.1566351, y: 3.973638, z: 0} + - {x: -0.1572447, y: 3.8729856, z: 0} - {x: -0.15785426, y: 3.7729874, z: 0} - - {x: -0.15846384, y: 3.6736438, z: 0} - - {x: -0.15907341, y: 3.5749545, z: 0} - - {x: -0.15968299, y: 3.4769197, z: 0} - - {x: -0.16029257, y: 3.379539, z: 0} - - {x: -0.16090216, y: 3.2828128, z: 0} - - {x: -0.16151173, y: 3.186741, z: 0} + - {x: -0.15846384, y: 3.6736436, z: 0} + - {x: -0.15907341, y: 3.574954, z: 0} + - {x: -0.15968299, y: 3.4769192, z: 0} + - {x: -0.16029257, y: 3.3795385, z: 0} + - {x: -0.16090216, y: 3.2828126, z: 0} + - {x: -0.16151173, y: 3.1867409, z: 0} - {x: -0.16212131, y: 3.0913236, z: 0} m_Parameters: serializedVersion: 3 @@ -1149,7 +1149,7 @@ Transform: m_GameObject: {fileID: 747976396} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 3.1975174, z: 0} + m_LocalPosition: {x: 0, y: 3.197517, z: 0} m_LocalScale: {x: 0.57574, y: 0.57574, z: 0.57574} m_ConstrainProportionsScale: 0 m_Children: @@ -1544,6 +1544,59 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &999630160 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 5541992152058962912, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + m_PrefabInstance: {fileID: 8347532583749285323} + m_PrefabAsset: {fileID: 0} +--- !u!114 &999630162 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 999630160} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9e0b24e2f2ad54cc09940c320ed3cf4b, type: 3} + m_Name: + m_EditorClassIdentifier: PixelplacementAssembly::Pixelplacement.StateMachine + defaultState: {fileID: 1211973842} + currentState: {fileID: 0} + _unityEventsFolded: 0 + verbose: 1 + allowReentry: 0 + returnToDefaultOnDisable: 1 + OnStateExited: + m_PersistentCalls: + m_Calls: [] + OnStateEntered: + m_PersistentCalls: + m_Calls: [] + OnFirstStateEntered: + m_PersistentCalls: + m_Calls: [] + OnFirstStateExited: + m_PersistentCalls: + m_Calls: [] + OnLastStateEntered: + m_PersistentCalls: + m_Calls: [] + OnLastStateExited: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &999630163 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 999630160} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 55938fb1577dd4ad3af7e994048c86f6, type: 3} + m_Name: + m_EditorClassIdentifier: PixelplacementAssembly::Pixelplacement.Initialization --- !u!1 &1003335103 GameObject: m_ObjectHideFlags: 0 @@ -1701,7 +1754,7 @@ LineRenderer: m_SortingOrder: 0 m_Positions: - {x: -0.15602553, y: 4.0749445, z: 0} - - {x: -0.11662118, y: 3.8796222, z: 0} + - {x: -0.11662118, y: 3.879622, z: 0} - {x: -0.07721684, y: 3.7057445, z: 0} - {x: -0.03781248, y: 3.5533106, z: 0} - {x: 0.0015918687, y: 3.4223216, z: 0} @@ -1709,7 +1762,7 @@ LineRenderer: - {x: 0.08040057, y: 3.2246761, z: 0} - {x: 0.11980491, y: 3.15802, z: 0} - {x: 0.15920927, y: 3.1128082, z: 0} - - {x: 0.1986136, y: 3.0890408, z: 0} + - {x: 0.1986136, y: 3.0890405, z: 0} - {x: 0.23801796, y: 3.0867176, z: 0} m_Parameters: serializedVersion: 3 @@ -1971,6 +2024,51 @@ MonoBehaviour: m_VarianceClampScale: 0.9 m_ContrastAdaptiveSharpening: 0 m_Version: 2 +--- !u!1 &1173818904 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 6751368003081662277, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + m_PrefabInstance: {fileID: 8347532583749285323} + m_PrefabAsset: {fileID: 0} +--- !u!114 &1173818908 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1173818904} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b75de64fb2fd4ff0b869bf469e2becea, type: 3} + m_Name: + m_EditorClassIdentifier: AppleHillsScripts::UI.BlinkingCanvasGroup + minAlpha: 0 + maxAlpha: 1 + legDuration: 0.5 + startDelay: 0 + obeyTimescale: 0 + easeCurve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!225 &1173818909 +CanvasGroup: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1173818904} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!1 &1211973842 stripped +GameObject: + m_CorrespondingSourceObject: {fileID: 2271162218602898139, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + m_PrefabInstance: {fileID: 8347532583749285323} + m_PrefabAsset: {fileID: 0} --- !u!1 &1224833348 GameObject: m_ObjectHideFlags: 0 @@ -2138,135 +2236,6 @@ Transform: m_Children: [] m_Father: {fileID: 2106431002} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1363116017 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1363116018} - - component: {fileID: 1363116019} - m_Layer: 0 - m_Name: TutorialAudio - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &1363116018 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1363116017} - serializedVersion: 2 - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -80.867836, y: 74.80073, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 1849171394} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!82 &1363116019 -AudioSource: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1363116017} - m_Enabled: 1 - serializedVersion: 4 - OutputAudioMixerGroup: {fileID: 3533147658878909314, guid: 727a7e4b6df4b0d47897f7d8ee7fa323, type: 2} - m_audioClip: {fileID: 0} - m_Resource: {fileID: 8300000, guid: fca641cdc8dcd074483fad3db1cbe24c, type: 3} - m_PlayOnAwake: 1 - m_Volume: 1 - m_Pitch: 1 - Loop: 0 - Mute: 0 - Spatialize: 0 - SpatializePostEffects: 0 - Priority: 128 - DopplerLevel: 1 - MinDistance: 1 - MaxDistance: 500 - Pan2D: 0 - rolloffMode: 0 - BypassEffects: 0 - BypassListenerEffects: 0 - BypassReverbZones: 0 - rolloffCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 1 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - panLevelCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - spreadCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - reverbZoneMixCustomCurve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 0 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 --- !u!1 &1376741227 GameObject: m_ObjectHideFlags: 0 @@ -2549,10 +2518,10 @@ LineRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_Positions: - - {x: -0.15602553, y: 4.074945, z: 0} - - {x: -0.18956745, y: 3.8764977, z: 0} - - {x: -0.22310936, y: 3.7000234, z: 0} - - {x: -0.25665125, y: 3.5455208, z: 0} + - {x: -0.15602553, y: 4.0749445, z: 0} + - {x: -0.18956745, y: 3.8764973, z: 0} + - {x: -0.22310936, y: 3.7000232, z: 0} + - {x: -0.25665125, y: 3.5455203, z: 0} - {x: -0.29019317, y: 3.412991, z: 0} - {x: -0.32373506, y: 3.3024335, z: 0} - {x: -0.35727698, y: 3.2138484, z: 0} @@ -2926,11 +2895,6 @@ SpriteRenderer: m_WasSpriteAssigned: 1 m_MaskInteraction: 0 m_SpriteSortPoint: 0 ---- !u!4 &1849171394 stripped -Transform: - m_CorrespondingSourceObject: {fileID: 3183207532655435649, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} - m_PrefabInstance: {fileID: 8347532583749285323} - m_PrefabAsset: {fileID: 0} --- !u!1 &1916796529 GameObject: m_ObjectHideFlags: 0 @@ -3440,25 +3404,24 @@ PrefabInstance: propertyPath: m_Name value: Tutorial objectReference: {fileID: 0} - - target: {fileID: 3332446944854334578, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} - propertyPath: m_IsActive - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 4267886887244421663, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} - propertyPath: playTutorial - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 4267886887244421663, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} - propertyPath: divingGameManager - value: - objectReference: {fileID: 424805725} - m_RemovedComponents: [] + m_RemovedComponents: + - {fileID: 8086909437439386099, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + - {fileID: 2002496630232568573, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} m_RemovedGameObjects: [] - m_AddedGameObjects: - - targetCorrespondingSourceObject: {fileID: 3183207532655435649, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 5541992152058962912, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} insertIndex: -1 - addedObject: {fileID: 1363116018} - m_AddedComponents: [] + addedObject: {fileID: 999630163} + - targetCorrespondingSourceObject: {fileID: 5541992152058962912, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + insertIndex: -1 + addedObject: {fileID: 999630162} + - targetCorrespondingSourceObject: {fileID: 6751368003081662277, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + insertIndex: -1 + addedObject: {fileID: 1173818909} + - targetCorrespondingSourceObject: {fileID: 6751368003081662277, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} + insertIndex: -1 + addedObject: {fileID: 1173818908} m_SourcePrefab: {fileID: 100100000, guid: a4dd78ff48942854ebb4c65025a8dc36, type: 3} --- !u!1660057539 &9223372036854775807 SceneRoots: diff --git a/Assets/Scripts/Cinematics/CinematicsManager.cs b/Assets/Scripts/Cinematics/CinematicsManager.cs index ad4b1c34..9bd85c00 100644 --- a/Assets/Scripts/Cinematics/CinematicsManager.cs +++ b/Assets/Scripts/Cinematics/CinematicsManager.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Bootstrap; using Core; @@ -7,7 +6,6 @@ using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.Playables; -using UnityEngine.SceneManagement; using UnityEngine.UI; using UI.Core; diff --git a/Assets/Scripts/Core/GameManager.cs b/Assets/Scripts/Core/GameManager.cs index daf66e07..a81b7940 100644 --- a/Assets/Scripts/Core/GameManager.cs +++ b/Assets/Scripts/Core/GameManager.cs @@ -1,304 +1,261 @@ -using UnityEngine; -using AppleHills.Core.Settings; -using System; +using System; using System.Collections.Generic; using AppleHills.Core.Interfaces; -using Core; -using UI; +using AppleHills.Core.Settings; using Bootstrap; +using Input; +using UnityEngine; -/// -/// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters. -/// -public class GameManager : MonoBehaviour +namespace Core { - private static GameManager _instance; - private static bool _isQuitting = false; - /// - /// Singleton instance of the GameManager. No longer creates an instance if one doesn't exist. + /// Singleton manager for global game state and settings. Provides accessors for various gameplay parameters. /// - public static GameManager Instance => _instance; + public class GameManager : MonoBehaviour + { + // Singleton implementation + private static GameManager _instance; + public static GameManager Instance => _instance; - [Header("Settings Status")] - [SerializeField] private bool _settingsLoaded = false; - [SerializeField] private bool _developerSettingsLoaded = false; - public bool SettingsLoaded => _settingsLoaded; - public bool DeveloperSettingsLoaded => _developerSettingsLoaded; + private bool _settingsLoaded; + private bool _developerSettingsLoaded; + + // Pausable implementation (counter-based) + private int _pauseCount; + public bool IsPaused => _pauseCount > 0; - [Header("Game State")] - [SerializeField] private bool _isPaused = false; + // List of pausable components that have registered with the GameManager + private List _pausableComponents = new List(); - /// - /// Current pause state of the game - /// - public bool IsPaused => _isPaused; - - // List of pausable components that have registered with the GameManager - private List _pausableComponents = new List(); - - // Events for pause state changes - public event Action OnGamePaused; - public event Action OnGameResumed; + // Events for pause state changes + public event Action OnGamePaused; + public event Action OnGameResumed; - void Awake() - { - _instance = this; + void Awake() + { + _instance = this; - // Create settings provider if it doesn't exist - SettingsProvider.Instance.gameObject.name = "Settings Provider"; + // Create settings providers if it doesn't exist + SettingsProvider.Instance.gameObject.name = "Settings Provider"; + DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider"; - // Create developer settings provider if it doesn't exist - DeveloperSettingsProvider.Instance.gameObject.name = "Developer Settings Provider"; + // Load all settings synchronously during Awake + InitializeSettings(); + InitializeDeveloperSettings(); - // Load all settings synchronously during Awake - InitializeSettings(); - InitializeDeveloperSettings(); + // Register for post-boot initialization + BootCompletionService.RegisterInitAction(InitializePostBoot); - // Register for post-boot initialization - BootCompletionService.RegisterInitAction(InitializePostBoot); - - // DontDestroyOnLoad(gameObject); - } + // DontDestroyOnLoad(gameObject); + } - private void InitializePostBoot() - { - // Find and subscribe to PauseMenu events - PauseMenu pauseMenu = PauseMenu.Instance; - if (pauseMenu != null) + private void InitializePostBoot() { - pauseMenu.OnGamePaused += OnPauseMenuPaused; - pauseMenu.OnGameResumed += OnPauseMenuResumed; - - Logging.Debug("[GameManager] Subscribed to PauseMenu events"); + // For post-boot correct initialization order } - else - { - Logging.Warning("[GameManager] PauseMenu not found. Pause functionality won't work properly."); - } - } - - private void OnDestroy() - { - // Unsubscribe from PauseMenu events - PauseMenu pauseMenu = FindFirstObjectByType(); - if (pauseMenu != null) - { - pauseMenu.OnGamePaused -= OnPauseMenuPaused; - pauseMenu.OnGameResumed -= OnPauseMenuResumed; - } - } - /// - /// Register a component as pausable, so it receives pause/resume notifications - /// - /// The pausable component to register - public void RegisterPausableComponent(IPausable component) - { - if (component != null && !_pausableComponents.Contains(component)) + /// + /// Register a component as pausable, so it receives pause/resume notifications + /// + /// The pausable component to register + public void RegisterPausableComponent(IPausable component) { - _pausableComponents.Add(component); - - // If the game is already paused, pause the component immediately - if (_isPaused) + if (component != null && !_pausableComponents.Contains(component)) { - component.Pause(); + _pausableComponents.Add(component); + + // If the game is already paused, pause the component immediately + if (IsPaused) + { + component.Pause(); + } + + Logging.Debug($"[GameManager] Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); + } + } + + /// + /// Unregister a pausable component + /// + /// The pausable component to unregister + public void UnregisterPausableComponent(IPausable component) + { + if (component != null && _pausableComponents.Contains(component)) + { + _pausableComponents.Remove(component); + Logging.Debug($"[GameManager] Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); + } + } + + /// + /// Request a pause. Multiple systems can request pause; the game only resumes when all requests are released. + /// + /// Optional object requesting the pause (for logging) + public void RequestPause(object requester = null) + { + _pauseCount++; + if (_pauseCount == 1) + { + ApplyPause(true); + } + + Logging.Debug($"[GameManager] Pause requested by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}"); + } + + /// + /// Release a previously requested pause. If this brings the pause count to zero the game resumes. + /// + /// Optional object releasing the pause (for logging) + public void ReleasePause(object requester = null) + { + _pauseCount = Mathf.Max(0, _pauseCount - 1); + if (_pauseCount == 0) + { + ApplyPause(false); + } + + Logging.Debug($"[GameManager] Pause released by {requester?.ToString() ?? "Unknown"}. pauseCount = {_pauseCount}"); + } + + /// + /// Apply pause/resume state to registered components and global systems. + /// + /// True to pause; false to resume + private void ApplyPause(bool shouldPause) + { + // TODO: Do we want to stop time? + // Time.timeScale = shouldPause ? 0f : 1f; + + // Notify registered components + foreach (var component in _pausableComponents) + { + if (shouldPause) + component.Pause(); + else + component.DoResume(); + } + + // Fire events + if (shouldPause) + { + // TODO: Stop input here? + InputManager.Instance.SetInputMode(InputMode.UI); + OnGamePaused?.Invoke(); + } + else + { + // TODO: Release input here? + InputManager.Instance.SetInputMode(InputMode.GameAndUI); + OnGameResumed?.Invoke(); } - Logging.Debug($"[GameManager] Registered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); + Logging.Debug($"[GameManager] Game {(shouldPause ? "paused" : "resumed")}. Paused {_pausableComponents.Count} components."); } - } - - /// - /// Unregister a pausable component - /// - /// The pausable component to unregister - public void UnregisterPausableComponent(IPausable component) - { - if (component != null && _pausableComponents.Contains(component)) - { - _pausableComponents.Remove(component); - Logging.Debug($"[GameManager] Unregistered pausable component: {(component as MonoBehaviour)?.name ?? "Unknown"}"); - } - } - - /// - /// Called when the PauseMenu broadcasts a pause event - /// - private void OnPauseMenuPaused() - { - PauseGame(); - } - - /// - /// Called when the PauseMenu broadcasts a resume event - /// - private void OnPauseMenuResumed() - { - ResumeGame(); - } - - /// - /// Pause the game and notify all registered pausable components - /// - public void PauseGame() - { - if (_isPaused) return; // Already paused - - _isPaused = true; - - // Pause all registered components - foreach (var component in _pausableComponents) - { - component.Pause(); - } - - // Broadcast pause event - OnGamePaused?.Invoke(); - - Logging.Debug($"[GameManager] Game paused. Paused {_pausableComponents.Count} components."); - } - - /// - /// Resume the game and notify all registered pausable components - /// - public void ResumeGame() - { - if (!_isPaused) return; // Already running - - _isPaused = false; - - // Resume all registered components - foreach (var component in _pausableComponents) - { - component.DoResume(); - } - - // Broadcast resume event - OnGameResumed?.Invoke(); - - Logging.Debug($"[GameManager] Game resumed. Resumed {_pausableComponents.Count} components."); - } - - /// - /// Toggle the pause state of the game - /// - public void TogglePause() - { - if (_isPaused) - ResumeGame(); - else - PauseGame(); - } - private void InitializeSettings() - { - Logging.Debug("Starting settings initialization..."); - - // Load settings synchronously - var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); - var interactionSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); - var minigameSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); - - // Register settings with service locator - if (playerSettings != null) + private void InitializeSettings() { - ServiceLocator.Register(playerSettings); - Logging.Debug("PlayerFollowerSettings registered successfully"); - } - else - { - Debug.LogError("Failed to load PlayerFollowerSettings"); - } + Logging.Debug("Starting settings initialization..."); + + // Load settings synchronously + var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); + var interactionSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); + var minigameSettings = SettingsProvider.Instance.LoadSettingsSynchronous(); + + // Register settings with service locator + if (playerSettings != null) + { + ServiceLocator.Register(playerSettings); + Logging.Debug("PlayerFollowerSettings registered successfully"); + } + else + { + Debug.LogError("Failed to load PlayerFollowerSettings"); + } - if (interactionSettings != null) - { - ServiceLocator.Register(interactionSettings); - Logging.Debug("InteractionSettings registered successfully"); - } - else - { - Debug.LogError("Failed to load InteractionSettings"); - } + if (interactionSettings != null) + { + ServiceLocator.Register(interactionSettings); + Logging.Debug("InteractionSettings registered successfully"); + } + else + { + Debug.LogError("Failed to load InteractionSettings"); + } - if (minigameSettings != null) - { - ServiceLocator.Register(minigameSettings); - Logging.Debug("MinigameSettings registered successfully"); - } - else - { - Debug.LogError("Failed to load MinigameSettings"); + if (minigameSettings != null) + { + ServiceLocator.Register(minigameSettings); + Logging.Debug("MinigameSettings registered successfully"); + } + else + { + Debug.LogError("Failed to load MinigameSettings"); + } + + // Log success + _settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null; + if (_settingsLoaded) + { + Logging.Debug("All settings loaded and registered with ServiceLocator"); + } + else + { + Logging.Warning("Some settings failed to load - check that all settings assets exist and are marked as Addressables"); + } } - // Log success - _settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null; - if (_settingsLoaded) + /// + /// Check for and initialize developer settings. + /// + private void InitializeDeveloperSettings() { - Logging.Debug("All settings loaded and registered with ServiceLocator"); - } - else - { - Logging.Warning("Some settings failed to load - check that all settings assets exist and are marked as Addressables"); - } - } - - /// - /// Check for and initialize developer settings. - /// - private void InitializeDeveloperSettings() - { - Logging.Debug("Starting developer settings initialization..."); + Logging.Debug("Starting developer settings initialization..."); - // Load developer settings - var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings(); + // Load developer settings + var divingDevSettings = DeveloperSettingsProvider.Instance.GetSettings(); - _developerSettingsLoaded = divingDevSettings != null; + _developerSettingsLoaded = divingDevSettings != null; - if (_developerSettingsLoaded) - { - Logging.Debug("All developer settings loaded successfully"); + if (_developerSettingsLoaded) + { + Logging.Debug("All developer settings loaded successfully"); + } + else + { + Logging.Warning("Some developer settings failed to load"); + } } - else + + + // Helper method to get settings + private T GetSettings() where T : class { - Logging.Warning("Some developer settings failed to load"); + return ServiceLocator.Get(); } - } - void OnApplicationQuit() - { - _isQuitting = true; - ServiceLocator.Clear(); - } - - // Helper method to get settings - private T GetSettings() where T : class - { - return ServiceLocator.Get(); - } - - /// - /// Returns the entire settings object of specified type. - /// - /// Type of settings to retrieve - /// The settings object or null if not found - public static T GetSettingsObject() where T : class - { - return Instance?.GetSettings(); - } + /// + /// Returns the entire settings object of specified type. + /// + /// Type of settings to retrieve + /// The settings object or null if not found + public static T GetSettingsObject() where T : class + { + return Instance?.GetSettings(); + } - /// - /// Returns the developer settings object of specified type. - /// - /// Type of developer settings to retrieve - /// The developer settings object or null if not found - public static T GetDeveloperSettings() where T : BaseDeveloperSettings - { - return DeveloperSettingsProvider.Instance?.GetSettings(); - } + /// + /// Returns the developer settings object of specified type. + /// + /// Type of developer settings to retrieve + /// The developer settings object or null if not found + public static T GetDeveloperSettings() where T : BaseDeveloperSettings + { + return DeveloperSettingsProvider.Instance?.GetSettings(); + } - // LEFTOVER LEGACY SETTINGS - public float PlayerStopDistance => GetSettings()?.PlayerStopDistance ?? 6.0f; - public float PlayerStopDistanceDirectInteraction => GetSettings()?.PlayerStopDistanceDirectInteraction ?? 2.0f; - public float DefaultPuzzlePromptRange => GetSettings()?.DefaultPuzzlePromptRange ?? 3.0f; + // LEFTOVER LEGACY SETTINGS + public float PlayerStopDistance => GetSettings()?.PlayerStopDistance ?? 6.0f; + public float PlayerStopDistanceDirectInteraction => GetSettings()?.PlayerStopDistanceDirectInteraction ?? 2.0f; + public float DefaultPuzzlePromptRange => GetSettings()?.DefaultPuzzlePromptRange ?? 3.0f; + } } diff --git a/Assets/Scripts/Core/Interfaces/IPausable.cs b/Assets/Scripts/Core/Interfaces/IPausable.cs index 9347e157..90eec549 100644 --- a/Assets/Scripts/Core/Interfaces/IPausable.cs +++ b/Assets/Scripts/Core/Interfaces/IPausable.cs @@ -16,10 +16,5 @@ namespace AppleHills.Core.Interfaces /// Resumes the component's functionality /// void DoResume(); - - /// - /// Gets whether the component is currently paused - /// - bool IsPaused { get; } } } diff --git a/Assets/Scripts/Core/SettingsAccess.cs b/Assets/Scripts/Core/SettingsAccess.cs index 502ecb9b..f5d12c18 100644 --- a/Assets/Scripts/Core/SettingsAccess.cs +++ b/Assets/Scripts/Core/SettingsAccess.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using Core; +using UnityEngine; namespace AppleHills { diff --git a/Assets/Scripts/Minigames/DivingForPictures/Bubbles/Bubble.cs b/Assets/Scripts/Minigames/DivingForPictures/Bubbles/Bubble.cs index a54d911e..1a32f241 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Bubbles/Bubble.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Bubbles/Bubble.cs @@ -29,12 +29,6 @@ namespace Minigames.DivingForPictures private Coroutine _wobbleCoroutine; private Coroutine _offScreenCheckCoroutine; - // Pause state tracking - private bool _isPaused = false; - - // IPausable implementation - public bool IsPaused => _isPaused; - void Awake() { // Cache references and randomize time offset for wobble @@ -67,9 +61,6 @@ namespace Minigames.DivingForPictures /// public void Pause() { - if (_isPaused) return; // Already paused - - _isPaused = true; StopBubbleBehavior(); // Debug log for troubleshooting @@ -81,9 +72,6 @@ namespace Minigames.DivingForPictures /// public void DoResume() { - if (!_isPaused) return; // Already running - - _isPaused = false; StartBubbleBehavior(); // Debug log for troubleshooting @@ -95,7 +83,7 @@ namespace Minigames.DivingForPictures /// private void StartBubbleBehavior() { - if (_isPaused || !isActiveAndEnabled) return; // Don't start if paused + if (GameManager.Instance.IsPaused || !isActiveAndEnabled) return; // Don't start if paused _movementCoroutine = StartCoroutine(MovementCoroutine()); _wobbleCoroutine = StartCoroutine(WobbleCoroutine()); diff --git a/Assets/Scripts/Minigames/DivingForPictures/Bubbles/BubbleSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/Bubbles/BubbleSpawner.cs index 4f485b20..8d2b42c1 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Bubbles/BubbleSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Bubbles/BubbleSpawner.cs @@ -25,15 +25,9 @@ namespace Minigames.DivingForPictures private UnityEngine.Camera _mainCamera; // Cache camera reference private bool _isSurfacing = false; - // Pause state - private bool _isPaused = false; - // Coroutines for pause/resume private Coroutine _spawnCoroutine; - // IPausable implementation - public bool IsPaused => _isPaused; - void Awake() { _mainCamera = UnityEngine.Camera.main; @@ -87,10 +81,6 @@ namespace Minigames.DivingForPictures /// public void Pause() { - if (_isPaused) return; // Already paused - - _isPaused = true; - // Stop spawning coroutine if (_spawnCoroutine != null) { @@ -116,10 +106,6 @@ namespace Minigames.DivingForPictures /// public void DoResume() { - if (!_isPaused) return; // Already running - - _isPaused = false; - // Restart spawning coroutine StartSpawningCoroutine(); @@ -141,7 +127,7 @@ namespace Minigames.DivingForPictures /// private void StartSpawningCoroutine() { - if (_spawnCoroutine == null && !_isPaused) + if (_spawnCoroutine == null && !GameManager.Instance.IsPaused) { _spawnCoroutine = StartCoroutine(SpawnBubblesRoutine()); } @@ -219,7 +205,7 @@ namespace Minigames.DivingForPictures bubble.SetWobbleScaleLimits(_devSettings.BubbleWobbleMinScale, _devSettings.BubbleWobbleMaxScale); // If the game is already paused, pause this bubble immediately - if (_isPaused) + if (GameManager.Instance.IsPaused) { bubble.Pause(); } @@ -262,7 +248,7 @@ namespace Minigames.DivingForPictures { Logging.Debug("[BubbleSpawner] Started bubble spawning coroutine"); - while (enabled && gameObject.activeInHierarchy && !_isPaused) + while (enabled && gameObject.activeInHierarchy && !GameManager.Instance.IsPaused) { SpawnBubble(); SetNextSpawnInterval(); // Set interval for next spawn diff --git a/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs b/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs index ce4e31da..01c2fcfa 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs @@ -7,8 +7,9 @@ using Minigames.DivingForPictures.PictureCamera; using System; using System.Collections; using System.Collections.Generic; -using System.Linq; +using Bootstrap; using UI; +using UI.Core; using UnityEngine; using UnityEngine.Events; using UnityEngine.Playables; @@ -38,24 +39,24 @@ namespace Minigames.DivingForPictures public CameraViewfinderManager viewfinderManager; // Settings reference - private IDivingMinigameSettings settings; + private IDivingMinigameSettings _settings; // Private state variables - private int playerScore = 0; - private float currentSpawnProbability; - private float lastSpawnTime = -100f; - private float timeSinceLastSpawn = 0f; - private List activeMonsters = new List(); + private int _playerScore = 0; + private float _currentSpawnProbability; + private float _lastSpawnTime = -100f; + private float _timeSinceLastSpawn = 0f; + private List _activeMonsters = new List(); // Velocity management // Velocity state tracking - private float currentVelocityFactor = 1.0f; // 1.0 = normal descent speed, -1.0 * surfacingSpeedFactor = full surfacing speed - private Coroutine velocityTransitionCoroutine; - private Coroutine surfacingSequenceCoroutine; + private float _currentVelocityFactor = 1.0f; // 1.0 = normal descent speed, -1.0 * surfacingSpeedFactor = full surfacing speed + private Coroutine _velocityTransitionCoroutine; + private Coroutine _surfacingSequenceCoroutine; // Public properties - public int PlayerScore => playerScore; - public float CurrentVelocityFactor => currentVelocityFactor; + public int PlayerScore => _playerScore; + public float CurrentVelocityFactor => _currentVelocityFactor; // Events public event Action OnScoreChanged; @@ -67,8 +68,8 @@ namespace Minigames.DivingForPictures public event Action OnVelocityFactorChanged; // Private state variables for rope system - private int currentRopeIndex = 0; - private bool isGameOver = false; + private int _currentRopeIndex = 0; + private bool _isGameOver = false; private bool _isSurfacing = false; // Initialization state @@ -77,23 +78,17 @@ namespace Minigames.DivingForPictures // Used to track if we're currently surfacing public bool IsSurfacing => _isSurfacing; - // Event for game components to subscribe to + // TODO: Get rid of this in favor of proper game pausing? public event Action OnGameInitialized; - // Pause state - private bool _isPaused = false; // List of pausable components controlled by this manager private List _pausableComponents = new List(); - - // IPausable implementation - public bool IsPaused => _isPaused; // Photo sequence state private bool _isPhotoSequenceActive = false; private Monster _currentPhotoTarget = null; - private Dictionary _pauseStateBackup = new Dictionary(); - private float _capturedProximity = 0f; // New: tracks how close to target the photo was taken (0-1) + private float _capturedProximity = 0f; // List of components to exempt from pausing during photo sequence private List _exemptFromPhotoSequencePausing = new List(); @@ -101,7 +96,6 @@ namespace Minigames.DivingForPictures // Photo sequence events public event Action OnPhotoSequenceStarted; public event Action OnPhotoSequenceCompleted; // Now includes proximity score - public event Action OnPhotoSequenceProgressUpdated; private static DivingGameManager _instance = null; private static bool _isQuitting = false; @@ -113,8 +107,8 @@ namespace Minigames.DivingForPictures private void Awake() { - settings = GameManager.GetSettingsObject(); - currentSpawnProbability = settings?.BaseSpawnProbability ?? 0.2f; + _settings = GameManager.GetSettingsObject(); + _currentSpawnProbability = _settings?.BaseSpawnProbability ?? 0.2f; if (_instance == null) { @@ -124,6 +118,9 @@ namespace Minigames.DivingForPictures { Destroy(gameObject); } + + // Ensure any previous run state is reset when this manager awakes + _isGameOver = false; } private void OnApplicationQuit() @@ -133,44 +130,8 @@ namespace Minigames.DivingForPictures private void Start() { - // Find PauseMenu and subscribe to its events - PauseMenu pauseMenu = PauseMenu.Instance; - if (pauseMenu != null) - { - pauseMenu.OnGamePaused += Pause; - pauseMenu.OnGameResumed += DoResume; - - Logging.Debug("[DivingGameManager] Subscribed to PauseMenu events"); - } - else - { - Logging.Warning("[DivingGameManager] PauseMenu not found. Pause functionality won't work properly."); - } - - // Register this manager with the global GameManager - if (GameManager.Instance != null) - { - GameManager.Instance.RegisterPausableComponent(this); - } - - // Subscribe to SceneOrientationEnforcer's event - if (SceneOrientationEnforcer.Instance != null) - { - SceneOrientationEnforcer.Instance.OnOrientationCorrect += InitializeGame; - SceneOrientationEnforcer.Instance.OnOrientationIncorrect += Pause; - - // If orientation is already correct, initialize right away - // This prevents issues if the orientation was already correct before subscription - if (SceneOrientationEnforcer.Instance.IsOrientationCorrect()) - { - InitializeGame(); - } - } - else - { - Logging.Warning("[DivingGameManager] SceneOrientationEnforcer not found. Initializing game immediately."); - InitializeGame(); - } + // Register for post-boot initialization + BootCompletionService.RegisterInitAction(InitializePostBoot); // Subscribe to player damage events (this doesn't depend on initialization) PlayerCollisionBehavior.OnDamageTaken += OnPlayerDamageTaken; @@ -189,6 +150,7 @@ namespace Minigames.DivingForPictures viewfinderManager.OnViewfinderTappedDuringAnimation += OnViewfinderTappedDuringAnimation; viewfinderManager.OnReverseAnimationStarted += OnReverseAnimationStarted; + // TODO: Give this a second look and make sure this is correct // Add the viewfinder manager to exempt list to ensure it keeps working during photo sequences if (viewfinderManager is IPausable viewfinderPausable) { @@ -197,6 +159,36 @@ namespace Minigames.DivingForPictures } OnMonsterSpawned += DoMonsterSpawned; + } + + private void InitializePostBoot() + { + // Register this manager with the global GameManager + if (GameManager.Instance != null) + { + GameManager.Instance.RegisterPausableComponent(this); + } + + // Subscribe to SceneOrientationEnforcer's event + if (SceneOrientationEnforcer.Instance != null) + { + // TODO: This is a bit of a hack to make sure the game is initialized after the orientation is correct + SceneOrientationEnforcer.Instance.OnOrientationCorrect += InitializeGame; + SceneOrientationEnforcer.Instance.OnOrientationIncorrect += Pause; + + // If orientation is already correct, initialize right away + // This prevents issues if the orientation was already correct before subscription + if (SceneOrientationEnforcer.Instance.IsOrientationCorrect()) + { + InitializeGame(); + } + } + else + { + Logging.Warning("[DivingGameManager] SceneOrientationEnforcer not found. Initializing game immediately."); + InitializeGame(); + } + CinematicsManager.Instance.OnCinematicStopped += EndGame; } @@ -211,14 +203,6 @@ namespace Minigames.DivingForPictures SceneOrientationEnforcer.Instance.OnOrientationIncorrect -= Pause; } - // Unsubscribe from PauseMenu events - PauseMenu pauseMenu = PauseMenu.Instance; - if (pauseMenu != null) - { - pauseMenu.OnGamePaused -= Pause; - pauseMenu.OnGameResumed -= DoResume; - } - // Unregister from GameManager if (GameManager.Instance != null) { @@ -241,19 +225,19 @@ namespace Minigames.DivingForPictures private void Update() { - timeSinceLastSpawn += Time.deltaTime; + _timeSinceLastSpawn += Time.deltaTime; // Gradually increase spawn probability over time - float previousProbability = currentSpawnProbability; - if (currentSpawnProbability < settings.MaxSpawnProbability) + float previousProbability = _currentSpawnProbability; + if (_currentSpawnProbability < _settings.MaxSpawnProbability) { - currentSpawnProbability += settings.ProbabilityIncreaseRate * Time.deltaTime; - currentSpawnProbability = Mathf.Min(currentSpawnProbability, settings.MaxSpawnProbability); + _currentSpawnProbability += _settings.ProbabilityIncreaseRate * Time.deltaTime; + _currentSpawnProbability = Mathf.Min(_currentSpawnProbability, _settings.MaxSpawnProbability); // Only fire event if probability changed significantly - if (Mathf.Abs(currentSpawnProbability - previousProbability) > 0.01f) + if (Mathf.Abs(_currentSpawnProbability - previousProbability) > 0.01f) { - OnSpawnProbabilityChanged?.Invoke(currentSpawnProbability); + OnSpawnProbabilityChanged?.Invoke(_currentSpawnProbability); } } } @@ -268,14 +252,14 @@ namespace Minigames.DivingForPictures // If we're surfacing, don't spawn new monsters if (_isSurfacing) return; - bool forceSpawn = timeSinceLastSpawn >= settings.GuaranteedSpawnTime; - bool onCooldown = timeSinceLastSpawn < settings.SpawnCooldown; + bool forceSpawn = _timeSinceLastSpawn >= _settings.GuaranteedSpawnTime; + bool onCooldown = _timeSinceLastSpawn < _settings.SpawnCooldown; // Don't spawn if on cooldown, unless forced if (onCooldown && !forceSpawn) return; // Check probability or forced spawn - if (forceSpawn || UnityEngine.Random.value <= currentSpawnProbability) + if (forceSpawn || UnityEngine.Random.value <= _currentSpawnProbability) { // Pick a random spawn point from this tile MonsterSpawnPoint spawnPoint = spawnPoints[UnityEngine.Random.Range(0, spawnPoints.Length)]; @@ -284,10 +268,10 @@ namespace Minigames.DivingForPictures SpawnMonster(spawnPoint.transform); // Reset timer and adjust probability - lastSpawnTime = Time.time; - timeSinceLastSpawn = 0f; - currentSpawnProbability = settings.BaseSpawnProbability; - OnSpawnProbabilityChanged?.Invoke(currentSpawnProbability); + _lastSpawnTime = Time.time; + _timeSinceLastSpawn = 0f; + _currentSpawnProbability = _settings.BaseSpawnProbability; + OnSpawnProbabilityChanged?.Invoke(_currentSpawnProbability); } } @@ -335,14 +319,14 @@ namespace Minigames.DivingForPictures private void DoPictureTaken(Monster monster) { // Calculate points based on depth - int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * settings.DepthMultiplier); - int pointsAwarded = settings.BasePoints + depthBonus; + int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * _settings.DepthMultiplier); + int pointsAwarded = _settings.BasePoints + depthBonus; // Add score - playerScore += pointsAwarded; + _playerScore += pointsAwarded; // Fire events - OnScoreChanged?.Invoke(playerScore); + OnScoreChanged?.Invoke(_playerScore); OnPictureTaken?.Invoke(monster, pointsAwarded); } @@ -351,13 +335,13 @@ namespace Minigames.DivingForPictures /// private void OnPlayerDamageTaken() { - if (isGameOver) return; + if (_isGameOver) return; // Break the next rope in sequence BreakNextRope(); // Check if all ropes are broken - if (currentRopeIndex >= playerRopes.Length) + if (_currentRopeIndex >= playerRopes.Length) { TriggerGameOver(); deathAudioPlayer.Play(); @@ -365,7 +349,7 @@ namespace Minigames.DivingForPictures else { // Notify listeners about rope break and remaining ropes - int remainingRopes = playerRopes.Length - currentRopeIndex; + int remainingRopes = playerRopes.Length - _currentRopeIndex; OnRopeBroken?.Invoke(remainingRopes); Logging.Debug($"[DivingGameManager] Rope broken! {remainingRopes} ropes remaining."); @@ -377,9 +361,9 @@ namespace Minigames.DivingForPictures /// private void BreakNextRope() { - if (currentRopeIndex < playerRopes.Length) + if (_currentRopeIndex < playerRopes.Length) { - RopeBreaker ropeToBreak = playerRopes[currentRopeIndex]; + RopeBreaker ropeToBreak = playerRopes[_currentRopeIndex]; if (ropeToBreak != null) { @@ -388,11 +372,11 @@ namespace Minigames.DivingForPictures } else { - Logging.Warning($"[DivingGameManager] Rope at index {currentRopeIndex} is null!"); + Logging.Warning($"[DivingGameManager] Rope at index {_currentRopeIndex} is null!"); } // Move to the next rope regardless if current was null - currentRopeIndex++; + _currentRopeIndex++; } } @@ -401,7 +385,7 @@ namespace Minigames.DivingForPictures /// public void ForceBreakRope() { - if (!isGameOver) + if (!_isGameOver) { OnPlayerDamageTaken(); } @@ -412,9 +396,9 @@ namespace Minigames.DivingForPictures /// private void TriggerGameOver() { - if (isGameOver) return; + if (_isGameOver) return; - isGameOver = true; + _isGameOver = true; Logging.Debug("[DivingGameManager] Game Over! All ropes broken. Starting surfacing sequence..."); // Fire game over event @@ -450,8 +434,8 @@ namespace Minigames.DivingForPictures public void ResetRopeSystem() { // Reset rope state - currentRopeIndex = 0; - isGameOver = false; + _currentRopeIndex = 0; + _isGameOver = false; // Restore all broken ropes if (playerRopes != null) @@ -477,8 +461,11 @@ namespace Minigames.DivingForPictures _isSurfacing = true; + // TODO: Call it here? + UIPageController.Instance.HideAllUI(); + // 1. Initiate smooth velocity transition to surfacing speed - float targetVelocityFactor = -1.0f * settings.SurfacingSpeedFactor; + float targetVelocityFactor = -1.0f * _settings.SurfacingSpeedFactor; SetVelocityFactor(targetVelocityFactor); // 2. Find and notify trench tile spawner about direction change (for spawning/despawning logic) @@ -497,7 +484,7 @@ namespace Minigames.DivingForPictures tileSpawner.StartSurfacing(); // Immediately send current velocity factor - tileSpawner.OnVelocityFactorChanged(currentVelocityFactor); + tileSpawner.OnVelocityFactorChanged(_currentVelocityFactor); } // Handle the Rock object - disable components and animate it falling offscreen @@ -565,15 +552,15 @@ namespace Minigames.DivingForPictures obstacleSpawner.StartSurfacing(); // Immediately send current velocity factor - obstacleSpawner.OnVelocityFactorChanged(currentVelocityFactor); + obstacleSpawner.OnVelocityFactorChanged(_currentVelocityFactor); } // Start the surfacing sequence coroutine - if (surfacingSequenceCoroutine != null) + if (_surfacingSequenceCoroutine != null) { - StopCoroutine(surfacingSequenceCoroutine); + StopCoroutine(_surfacingSequenceCoroutine); } - surfacingSequenceCoroutine = StartCoroutine(SurfacingSequence()); + _surfacingSequenceCoroutine = StartCoroutine(SurfacingSequence()); Logging.Debug($"[DivingGameManager] Started surfacing with target velocity factor: {targetVelocityFactor}"); } @@ -649,7 +636,7 @@ namespace Minigames.DivingForPictures private IEnumerator SurfacingSequence() { // Wait for the configured delay - yield return new WaitForSeconds(settings.SurfacingSpawnDelay); + yield return new WaitForSeconds(_settings.SurfacingSpawnDelay); // Find tile spawner and tell it to stop spawning TrenchTileSpawner tileSpawner = FindFirstObjectByType(); @@ -686,7 +673,6 @@ namespace Minigames.DivingForPictures // Call this when the game ends public void EndGame() { - // TODO: Investigate why called twice CinematicsManager.Instance.OnCinematicStopped -= EndGame; // Start the end game sequence that grants a booster, waits for the UI animation, then shows Game Over. StartCoroutine(EndGameSequence()); @@ -695,7 +681,7 @@ namespace Minigames.DivingForPictures private IEnumerator EndGameSequence() { // Clean up active monsters - foreach (var monster in activeMonsters.ToArray()) + foreach (var monster in _activeMonsters.ToArray()) { if (monster != null) { @@ -703,7 +689,7 @@ namespace Minigames.DivingForPictures } } - activeMonsters.Clear(); + _activeMonsters.Clear(); // 1) Call the booster pack giver if available bool completed = false; @@ -714,6 +700,7 @@ namespace Minigames.DivingForPictures UnityAction onDone = null; onDone = () => { completed = true; giver.OnCompleted.RemoveListener(onDone); }; giver.OnCompleted.AddListener(onDone); + UIPageController.Instance.ShowAllUI(); giver.GiveBoosterPack(); // 2) Wait for it to finish (with a safety timeout in case it's not wired) @@ -733,9 +720,9 @@ namespace Minigames.DivingForPictures // 3) Only then show the game over screen CinematicsManager.Instance.ShowGameOverScreen(); - + // Final score could be saved to player prefs or other persistence - Logging.Debug($"Final Score: {playerScore}"); + Logging.Debug($"Final Score: {_playerScore}"); } /// @@ -744,12 +731,12 @@ namespace Minigames.DivingForPictures /// Target velocity factor (e.g., -1.0 for surfacing speed) public void SetVelocityFactor(float targetFactor) { - if (velocityTransitionCoroutine != null) + if (_velocityTransitionCoroutine != null) { - StopCoroutine(velocityTransitionCoroutine); + StopCoroutine(_velocityTransitionCoroutine); } - velocityTransitionCoroutine = StartCoroutine(TransitionVelocityFactor(targetFactor)); + _velocityTransitionCoroutine = StartCoroutine(TransitionVelocityFactor(targetFactor)); } /// @@ -757,29 +744,29 @@ namespace Minigames.DivingForPictures /// private IEnumerator TransitionVelocityFactor(float targetFactor) { - float startFactor = currentVelocityFactor; + float startFactor = _currentVelocityFactor; float elapsed = 0f; - while (elapsed < settings.SpeedTransitionDuration) + while (elapsed < _settings.SpeedTransitionDuration) { elapsed += Time.deltaTime; - float t = Mathf.Clamp01(elapsed / settings.SpeedTransitionDuration); + float t = Mathf.Clamp01(elapsed / _settings.SpeedTransitionDuration); // Smooth step interpolation float smoothStep = t * t * (3f - 2f * t); - currentVelocityFactor = Mathf.Lerp(startFactor, targetFactor, smoothStep); + _currentVelocityFactor = Mathf.Lerp(startFactor, targetFactor, smoothStep); // Notify listeners about the velocity factor change - OnVelocityFactorChanged?.Invoke(currentVelocityFactor); + OnVelocityFactorChanged?.Invoke(_currentVelocityFactor); yield return null; } - currentVelocityFactor = targetFactor; + _currentVelocityFactor = targetFactor; // Final assignment to ensure exact target value - OnVelocityFactorChanged?.Invoke(currentVelocityFactor); + OnVelocityFactorChanged?.Invoke(_currentVelocityFactor); } /// @@ -793,7 +780,7 @@ namespace Minigames.DivingForPictures _pausableComponents.Add(component); // If the game is already paused, pause the component immediately - if (_isPaused) + if (GameManager.Instance.IsPaused) { component.Pause(); } @@ -820,48 +807,46 @@ namespace Minigames.DivingForPictures /// public void Pause() { + // Ignore pause requests once the game has reached Game Over + if (_isGameOver) return; DoPause(); - } - - public void DoPause(bool turnOffGameInput = true) - { - if (_isPaused) return; // Already paused - - _isPaused = true; - - // Pause all registered components - foreach (var component in _pausableComponents) - { - component.Pause(); - } - - // Change input mode to UI when menu is open - if(turnOffGameInput) - InputManager.Instance.SetInputMode(InputMode.GameAndUI); - - Logging.Debug($"[DivingGameManager] Game paused. Paused {_pausableComponents.Count} components."); - } - - /// - /// Resume the game and all registered components - /// - public void DoResume() - { - if (!_isPaused) return; // Already running - - _isPaused = false; - - // Resume all registered components - foreach (var component in _pausableComponents) - { - component.DoResume(); - } - - // Change input mode to UI when menu is open - InputManager.Instance.SetInputMode(InputMode.GameAndUI); - - Logging.Debug($"[DivingGameManager] Game resumed. Resumed {_pausableComponents.Count} components."); - } + } + + public void DoPause(bool turnOffGameInput = true) + { + // Ignore pause requests once the game has ended + if (_isGameOver) return; + // Pause all registered components + foreach (var component in _pausableComponents) + { + component.Pause(); + } + + // Change input mode to UI when menu is open + if(turnOffGameInput) + InputManager.Instance.SetInputMode(InputMode.GameAndUI); + + Logging.Debug($"[DivingGameManager] Game paused. Paused {_pausableComponents.Count} components."); + } + + /// + /// Resume the game and all registered components + /// + public void DoResume() + { + // Ignore resume requests once the game has ended + if (_isGameOver) return; + // Resume all registered components + foreach (var component in _pausableComponents) + { + component.DoResume(); + } + + // Change input mode to UI when menu is open + InputManager.Instance.SetInputMode(InputMode.GameAndUI); + + Logging.Debug($"[DivingGameManager] Game resumed. Resumed {_pausableComponents.Count} components."); + } #region Photo Sequence Methods @@ -938,23 +923,23 @@ namespace Minigames.DivingForPictures if (monster == null) return; // Calculate base points from depth - int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * settings.DepthMultiplier); + int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * _settings.DepthMultiplier); // Apply proximity multiplier (0-100%) float proximityMultiplier = Mathf.Clamp01(proximity); // Ensure it's in 0-1 range - int proximityBonus = Mathf.RoundToInt(settings.BasePoints * proximityMultiplier); + int proximityBonus = Mathf.RoundToInt(_settings.BasePoints * proximityMultiplier); // Calculate total score - int pointsAwarded = settings.BasePoints + proximityBonus + depthBonus; + int pointsAwarded = _settings.BasePoints + proximityBonus + depthBonus; Logging.Debug($"[DivingGameManager] Picture score calculation: base={proximityBonus} (proximity={proximity:F2}), " + $"depth bonus={depthBonus}, total={pointsAwarded}"); // Add score - playerScore += pointsAwarded; + _playerScore += pointsAwarded; // Fire events - OnScoreChanged?.Invoke(playerScore); + OnScoreChanged?.Invoke(_playerScore); OnPictureTaken?.Invoke(monster, pointsAwarded); } @@ -1061,14 +1046,14 @@ namespace Minigames.DivingForPictures monster.OnMonsterDespawned += DoMonsterDespawned; // Add to active monsters list - activeMonsters.Add(monster); + _activeMonsters.Add(monster); } } private void DoMonsterDespawned(Monster monster) { // Remove from active list - activeMonsters.Remove(monster); + _activeMonsters.Remove(monster); // Unsubscribe from monster events if (monster != null) diff --git a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/FloatingObstacle.cs b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/FloatingObstacle.cs index df5a7c4e..1b41fd2d 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/FloatingObstacle.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/FloatingObstacle.cs @@ -59,12 +59,6 @@ namespace Minigames.DivingForPictures private float _screenNormalizationFactor = 1.0f; private IDivingMinigameSettings _settings; - // Pause state - private bool _isPaused = false; - - // IPausable implementation - public bool IsPaused => _isPaused; - private void Awake() { _collider = GetComponent(); @@ -124,7 +118,7 @@ namespace Minigames.DivingForPictures private void OnEnable() { // Only start coroutines if not paused - if (!_isPaused) + if (!GameManager.Instance.IsPaused) { StartObstacleCoroutines(); } @@ -143,9 +137,6 @@ namespace Minigames.DivingForPictures /// public void Pause() { - if (_isPaused) return; // Already paused - - _isPaused = true; StopObstacleCoroutines(); Logging.Debug($"[FloatingObstacle] Paused obstacle: {name}"); @@ -156,9 +147,6 @@ namespace Minigames.DivingForPictures /// public void DoResume() { - if (!_isPaused) return; // Already running - - _isPaused = false; StartObstacleCoroutines(); Logging.Debug($"[FloatingObstacle] Resumed obstacle: {name}"); diff --git a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs index 3efe33f1..ed03de3f 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Obstacles/ObstacleSpawner.cs @@ -43,12 +43,6 @@ namespace Minigames.DivingForPictures private bool _isSurfacing = false; // Flag to track surfacing state private float _velocityFactor = 1.0f; // Current velocity factor from the game manager - // Pause state - private bool _isPaused = false; - - // IPausable implementation - public bool IsPaused => _isPaused; - private void Awake() { _mainCamera = UnityEngine.Camera.main; @@ -127,10 +121,6 @@ namespace Minigames.DivingForPictures /// public void Pause() { - if (_isPaused) return; // Already paused - - _isPaused = true; - // Stop spawning coroutine if (_spawnCoroutine != null) { @@ -159,10 +149,6 @@ namespace Minigames.DivingForPictures /// public void DoResume() { - if (!_isPaused) return; // Already running - - _isPaused = false; - // Restart spawning coroutine if not in surfacing mode if (!_isSurfacing) { @@ -190,7 +176,7 @@ namespace Minigames.DivingForPictures /// private void StartSpawnCoroutine() { - if (_spawnCoroutine == null && !_isPaused && !_isSurfacing) + if (_spawnCoroutine == null && !GameManager.Instance.IsPaused && !_isSurfacing) { _spawnCoroutine = StartCoroutine(SpawnObstacleRoutine()); } @@ -201,7 +187,7 @@ namespace Minigames.DivingForPictures /// private void StartMoveCoroutine() { - if (_moveCoroutine == null && !_isPaused) + if (_moveCoroutine == null && !GameManager.Instance.IsPaused) { _moveCoroutine = StartCoroutine(MoveObstaclesRoutine()); } @@ -212,7 +198,7 @@ namespace Minigames.DivingForPictures /// private void StartDespawnCoroutine() { - if (_despawnCoroutine == null && !_isPaused) + if (_despawnCoroutine == null && !GameManager.Instance.IsPaused) { _despawnCoroutine = StartCoroutine(DespawnObstaclesRoutine()); } @@ -553,7 +539,7 @@ namespace Minigames.DivingForPictures obstacleComponent.SetSpawner(this); // If spawner is already paused, pause the obstacle immediately - if (_isPaused) + if (GameManager.Instance.IsPaused) { obstacleComponent.Pause(); } @@ -677,7 +663,7 @@ namespace Minigames.DivingForPictures { Logging.Debug("[ObstacleSpawner] Started spawning coroutine"); - while (enabled && gameObject.activeInHierarchy && !_isPaused && !_isSurfacing) + while (enabled && gameObject.activeInHierarchy && !GameManager.Instance.IsPaused && !_isSurfacing) { // Calculate next spawn time with variation float nextSpawnTime = _settings.ObstacleSpawnInterval + @@ -707,7 +693,7 @@ namespace Minigames.DivingForPictures Logging.Debug("[ObstacleSpawner] Started obstacle monitoring coroutine"); // This coroutine now just monitors obstacles, not moves them - while (enabled && gameObject.activeInHierarchy && !_isPaused) + while (enabled && gameObject.activeInHierarchy && !GameManager.Instance.IsPaused) { // Clean up any null references in the active obstacles list _activeObstacles.RemoveAll(obstacle => obstacle == null); @@ -729,7 +715,7 @@ namespace Minigames.DivingForPictures const float checkInterval = 0.5f; // Check every half second Logging.Debug("[ObstacleSpawner] Started despawn coroutine with interval: " + checkInterval); - while (enabled && gameObject.activeInHierarchy && !_isPaused) + while (enabled && gameObject.activeInHierarchy && !GameManager.Instance.IsPaused) { // Calculate screen bounds for despawning float despawnBuffer = 2f; // Extra buffer beyond screen edges diff --git a/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerController.cs b/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerController.cs index 6766e42b..2e1615fd 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerController.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Player/PlayerController.cs @@ -18,26 +18,26 @@ namespace Minigames.DivingForPictures.Player [SerializeField] private EdgeAnchor edgeAnchor; // Settings reference - private IDivingMinigameSettings settings; + private IDivingMinigameSettings _settings; - private float targetFingerX; - private bool isTouchActive; - private float originY; + private float _targetFingerX; + private bool _isTouchActive; + private float _originY; // Tap impulse system variables - private float tapImpulseStrength = 0f; - private float tapDirection = 0f; + private float _tapImpulseStrength = 0f; + private float _tapDirection = 0f; // Initialization flag - private bool isInitialized = false; + private bool _isInitialized = false; void Awake() { - originY = transform.position.y; + _originY = transform.position.y; // Get settings from GameManager - settings = GameManager.GetSettingsObject(); - if (settings == null) + _settings = GameManager.GetSettingsObject(); + if (_settings == null) { Debug.LogError("[PlayerController] Failed to load diving minigame settings!"); } @@ -49,8 +49,8 @@ namespace Minigames.DivingForPictures.Player DivingGameManager.Instance.RegisterPausableComponent(this); // Initialize target to current position - targetFingerX = transform.position.x; - isTouchActive = false; + _targetFingerX = transform.position.x; + _isTouchActive = false; // Try to find edge anchor if not assigned if (edgeAnchor == null) @@ -100,12 +100,12 @@ namespace Minigames.DivingForPictures.Player /// private void Initialize() { - if (isInitialized) return; + if (_isInitialized) return; // Register as default consumer for input InputManager.Instance?.SetDefaultConsumer(this); - isInitialized = true; + _isInitialized = true; Logging.Debug("[PlayerController] Initialized"); } @@ -130,19 +130,19 @@ namespace Minigames.DivingForPictures.Player public void OnTap(Vector2 worldPosition) { // Ignore input when paused - if (isPaused) return; + if (GameManager.Instance.IsPaused) return; // Logging.Debug($"[EndlessDescenderController] OnTap at {worldPosition}"); - float targetX = Mathf.Clamp(worldPosition.x, settings.ClampXMin, settings.ClampXMax); + float targetX = Mathf.Clamp(worldPosition.x, _settings.ClampXMin, _settings.ClampXMax); // Calculate tap direction (+1 for right, -1 for left) - tapDirection = Mathf.Sign(targetX - transform.position.x); + _tapDirection = Mathf.Sign(targetX - transform.position.x); // Set impulse strength to full - tapImpulseStrength = 1.0f; + _tapImpulseStrength = 1.0f; // Store target X for animation purposes - targetFingerX = targetX; + _targetFingerX = targetX; // Do not set _isTouchActive for taps anymore // _isTouchActive = true; - Removed to prevent continuous movement @@ -154,11 +154,11 @@ namespace Minigames.DivingForPictures.Player public void OnHoldStart(Vector2 worldPosition) { // Ignore input when paused - if (isPaused) return; + if (GameManager.Instance.IsPaused) return; // Logging.Debug($"[EndlessDescenderController] OnHoldStart at {worldPosition}"); - targetFingerX = Mathf.Clamp(worldPosition.x, settings.ClampXMin, settings.ClampXMax); - isTouchActive = true; + _targetFingerX = Mathf.Clamp(worldPosition.x, _settings.ClampXMin, _settings.ClampXMax); + _isTouchActive = true; } /// @@ -167,10 +167,10 @@ namespace Minigames.DivingForPictures.Player public void OnHoldMove(Vector2 worldPosition) { // Ignore input when paused - if (isPaused) return; + if (GameManager.Instance.IsPaused) return; // Logging.Debug($"[EndlessDescenderController] OnHoldMove at {worldPosition}"); - targetFingerX = Mathf.Clamp(worldPosition.x, settings.ClampXMin, settings.ClampXMax); + _targetFingerX = Mathf.Clamp(worldPosition.x, _settings.ClampXMin, _settings.ClampXMax); } /// @@ -179,25 +179,25 @@ namespace Minigames.DivingForPictures.Player public void OnHoldEnd(Vector2 worldPosition) { // Ignore input when paused - if (isPaused) return; + if (GameManager.Instance.IsPaused) return; // Logging.Debug($"[EndlessDescenderController] OnHoldEnd at {worldPosition}"); - isTouchActive = false; + _isTouchActive = false; } void Update() { // Skip movement processing if paused - if (isPaused) return; + if (GameManager.Instance.IsPaused) return; // Handle hold movement - if (isTouchActive) + if (_isTouchActive) { float currentX = transform.position.x; - float lerpSpeed = settings.LerpSpeed; - float maxOffset = settings.MaxOffset; - float exponent = settings.SpeedExponent; - float targetX = targetFingerX; + float lerpSpeed = _settings.LerpSpeed; + float maxOffset = _settings.MaxOffset; + float exponent = _settings.SpeedExponent; + float targetX = _targetFingerX; float offset = targetX - currentX; offset = Mathf.Clamp(offset, -maxOffset, maxOffset); float absOffset = Mathf.Abs(offset); @@ -206,32 +206,32 @@ namespace Minigames.DivingForPictures.Player // Prevent overshooting moveStep = Mathf.Clamp(moveStep, -absOffset, absOffset); float newX = currentX + moveStep; - newX = Mathf.Clamp(newX, settings.ClampXMin, settings.ClampXMax); + newX = Mathf.Clamp(newX, _settings.ClampXMin, _settings.ClampXMax); UpdatePosition(newX); } // Handle tap impulse movement - else if (tapImpulseStrength > 0) + else if (_tapImpulseStrength > 0) { float currentX = transform.position.x; - float maxOffset = settings.MaxOffset; - float lerpSpeed = settings.LerpSpeed; + float maxOffset = _settings.MaxOffset; + float lerpSpeed = _settings.LerpSpeed; // Calculate move distance based on impulse strength - float moveDistance = maxOffset * tapImpulseStrength * Time.deltaTime * lerpSpeed; + float moveDistance = maxOffset * _tapImpulseStrength * Time.deltaTime * lerpSpeed; // Limit total movement from single tap - moveDistance = Mathf.Min(moveDistance, settings.TapMaxDistance * tapImpulseStrength); + moveDistance = Mathf.Min(moveDistance, _settings.TapMaxDistance * _tapImpulseStrength); // Apply movement in tap direction - float newX = currentX + (moveDistance * tapDirection); - newX = Mathf.Clamp(newX, settings.ClampXMin, settings.ClampXMax); + float newX = currentX + (moveDistance * _tapDirection); + newX = Mathf.Clamp(newX, _settings.ClampXMin, _settings.ClampXMax); // Reduce impulse strength over time - tapImpulseStrength -= Time.deltaTime * settings.TapDecelerationRate; - if (tapImpulseStrength < 0.01f) + _tapImpulseStrength -= Time.deltaTime * _settings.TapDecelerationRate; + if (_tapImpulseStrength < 0.01f) { - tapImpulseStrength = 0f; + _tapImpulseStrength = 0f; } UpdatePosition(newX); @@ -243,7 +243,7 @@ namespace Minigames.DivingForPictures.Player /// private void UpdatePosition(float newX) { - float newY = originY; + float newY = _originY; // Add vertical offset from WobbleBehavior if present WobbleBehavior wobble = GetComponent(); if (wobble != null) @@ -258,7 +258,7 @@ namespace Minigames.DivingForPictures.Player /// public void UpdateOriginY(float newOriginY) { - originY = newOriginY; + _originY = newOriginY; } /// @@ -267,7 +267,7 @@ namespace Minigames.DivingForPictures.Player /// private void UpdateOriginYFromCurrentPosition() { - originY = transform.position.y; + _originY = transform.position.y; } /// @@ -276,24 +276,19 @@ namespace Minigames.DivingForPictures.Player /// private void UpdateOriginYFromAnchor() { - originY = edgeAnchor.transform.position.y; + _originY = edgeAnchor.transform.position.y; } #region IPausable Implementation - private bool isPaused = false; /// /// Pauses the player controller, blocking all input processing /// public void Pause() { - if (isPaused) return; - - isPaused = true; - // If we're being paused, stop any active touch and tap impulse - isTouchActive = false; - tapImpulseStrength = 0f; + _isTouchActive = false; + _tapImpulseStrength = 0f; Logging.Debug("[PlayerController] Paused"); } @@ -303,16 +298,8 @@ namespace Minigames.DivingForPictures.Player /// public void DoResume() { - if (!isPaused) return; - - isPaused = false; Logging.Debug("[PlayerController] Resumed"); } - - /// - /// Returns whether the player controller is currently paused - /// - public bool IsPaused => isPaused; #endregion } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs b/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs index 469d38e3..c6755b84 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Player/WobbleBehavior.cs @@ -1,5 +1,6 @@ using UnityEngine; using AppleHills.Core.Settings; +using Core; /// /// Adds a wobble (rocking and vertical movement) effect to the object, based on speed and time. diff --git a/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs index 2d66b080..bc4950d4 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Tiles/TrenchTileSpawner.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.Serialization; -using Pooling; using AppleHills.Core.Settings; using Utils; using AppleHills.Core.Interfaces; @@ -113,12 +111,6 @@ namespace Minigames.DivingForPictures } } - // Pause state - private bool _isPaused = false; - - // IPausable implementation - public bool IsPaused => _isPaused; - private void Awake() { _mainCamera = UnityEngine.Camera.main; @@ -193,10 +185,6 @@ namespace Minigames.DivingForPictures /// public void Pause() { - if (_isPaused) return; // Already paused - - _isPaused = true; - // Stop all active coroutines but save their references if (_movementCoroutine != null) { @@ -230,10 +218,6 @@ namespace Minigames.DivingForPictures /// public void DoResume() { - if (!_isPaused) return; // Already running - - _isPaused = false; - // Restart all necessary coroutines StartMovementCoroutine(); StartTileDestructionCoroutine(); @@ -500,7 +484,7 @@ namespace Minigames.DivingForPictures /// private void StartMovementCoroutine() { - if (_movementCoroutine == null && !_isPaused) + if (_movementCoroutine == null && !GameManager.Instance.IsPaused) { _movementCoroutine = StartCoroutine(MoveActiveTilesRoutine()); } @@ -513,7 +497,7 @@ namespace Minigames.DivingForPictures { Logging.Debug($"[TrenchTileSpawner] Started movement coroutine with normalized speed: {_baseMoveSpeed:F3}"); - while (enabled && gameObject.activeInHierarchy && !_isPaused) + while (enabled && gameObject.activeInHierarchy && !GameManager.Instance.IsPaused) { // Skip if no active tiles if (_activeTiles.Count == 0) @@ -554,7 +538,7 @@ namespace Minigames.DivingForPictures const float checkInterval = 1.0f; // Check once per second Logging.Debug($"[TrenchTileSpawner] Started speed ramping coroutine with interval: {checkInterval}s"); - while (enabled && gameObject.activeInHierarchy && !_isPaused) + while (enabled && gameObject.activeInHierarchy && !GameManager.Instance.IsPaused) { // Increase the base move speed up to the maximum _baseMoveSpeed = Mathf.Min(_baseMoveSpeed + _settings.SpeedUpFactor, _settings.MaxNormalizedMoveSpeed); diff --git a/Assets/Scripts/Minigames/DivingForPictures/Utilities/BottlePauser.cs b/Assets/Scripts/Minigames/DivingForPictures/Utilities/BottlePauser.cs index 81146531..0efc9b9b 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Utilities/BottlePauser.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Utilities/BottlePauser.cs @@ -7,8 +7,6 @@ namespace Minigames.DivingForPictures.Utilities { [SerializeField] private WobbleBehavior wobbleReference; - private bool isPaused = false; - private void Start() { DivingGameManager.Instance.RegisterPausableComponent(this); @@ -17,15 +15,11 @@ namespace Minigames.DivingForPictures.Utilities public void Pause() { wobbleReference.enabled = false; - isPaused = true; } public void DoResume() { wobbleReference.enabled = true; - isPaused = false; } - - public bool IsPaused => isPaused; } } diff --git a/Assets/Scripts/Minigames/DivingForPictures/Utilities/RockPauser.cs b/Assets/Scripts/Minigames/DivingForPictures/Utilities/RockPauser.cs index ce143f6f..9203bed7 100644 --- a/Assets/Scripts/Minigames/DivingForPictures/Utilities/RockPauser.cs +++ b/Assets/Scripts/Minigames/DivingForPictures/Utilities/RockPauser.cs @@ -8,8 +8,6 @@ namespace Minigames.DivingForPictures.Utilities [SerializeField] private RockFollower rockReference; [SerializeField] private WobbleBehavior rockWobbleReference; - private bool isPaused = false; - private void Start() { DivingGameManager.Instance.RegisterPausableComponent(this); @@ -19,16 +17,12 @@ namespace Minigames.DivingForPictures.Utilities { rockReference.enabled = false; rockWobbleReference.enabled = false; - isPaused = true; } public void DoResume() { rockReference.enabled = true; rockWobbleReference.enabled = true; - isPaused = false; } - - public bool IsPaused => isPaused; } } diff --git a/Assets/Scripts/Movement/FollowerController.cs b/Assets/Scripts/Movement/FollowerController.cs index 3aba4b8b..dc4b0238 100644 --- a/Assets/Scripts/Movement/FollowerController.cs +++ b/Assets/Scripts/Movement/FollowerController.cs @@ -4,6 +4,7 @@ using Pathfinding; using UnityEngine.SceneManagement; using Utils; using AppleHills.Core.Settings; +using Core; /// /// Controls the follower character, including following the player, handling pickups, and managing held items. diff --git a/Assets/Scripts/UI/BlinkingCanvasGroup.cs b/Assets/Scripts/UI/BlinkingCanvasGroup.cs new file mode 100644 index 00000000..e59ecb55 --- /dev/null +++ b/Assets/Scripts/UI/BlinkingCanvasGroup.cs @@ -0,0 +1,112 @@ +using Pixelplacement; +using UnityEngine; + +namespace UI +{ + /// + /// Adds a CanvasGroup (if missing) and plays a continuous blinking tween on its alpha. + /// Attach to any GameObject to make it pulse/fade in and out. + /// + [DisallowMultipleComponent] + [RequireComponent(typeof(CanvasGroup))] + public class BlinkingCanvasGroup : MonoBehaviour + { + [Header("Blink Settings")] [Tooltip("Minimum alpha value during blink")] [Range(0f, 1f)] + public float minAlpha; + + [Tooltip("Maximum alpha value during blink")] [Range(0f, 1f)] + public float maxAlpha = 1f; + + [Tooltip("Duration of one leg (min->max or max->min)")] + public float legDuration = 0.5f; + + [Tooltip("Delay before starting the blinking")] + public float startDelay; + + [Tooltip("Whether the tween should obey Time.timeScale (false = unscaled)")] + public bool obeyTimescale; + + [Tooltip("Optional animation curve for easing. Leave null for default ease in/out.")] + public AnimationCurve easeCurve; + + // Internal + private CanvasGroup _canvasGroup; + private Pixelplacement.TweenSystem.TweenBase _activeTween; + + void Awake() + { + // Ensure we have a CanvasGroup + _canvasGroup = GetComponent(); + if (_canvasGroup == null) + { + _canvasGroup = gameObject.AddComponent(); + } + + // Ensure starting alpha + _canvasGroup.alpha = minAlpha; + } + + void Start() + { + StartBlinking(); + } + + void OnEnable() + { + // If re-enabled, ensure tween is running + if (_activeTween == null) + StartBlinking(); + } + + void OnDisable() + { + StopBlinking(); + } + + void OnDestroy() + { + StopBlinking(); + } + + /// + /// Start the continuous blinking tween. + /// + public void StartBlinking() + { + StopBlinking(); + + if (_canvasGroup == null) return; + + // Use PingPong-like behavior by chaining two opposite legs with LoopType.Loop + // Simpler: Tween.CanvasGroupAlpha has an overload that sets startAlpha then end. + // We'll tween min->max then set LoopType.PingPong if available. The API supports LoopType.PingPong. + + // Start from minAlpha to maxAlpha + _canvasGroup.alpha = minAlpha; + + // Use the Tween.Value overload for float with obeyTimescale parameter + _activeTween = Tween.Value(minAlpha, maxAlpha, v => _canvasGroup.alpha = v, legDuration, startDelay, + easeCurve ?? Tween.EaseInOut, Tween.LoopType.PingPong, null, null, obeyTimescale); + } + + /// + /// Stops the blinking tween if running. + /// + public void StopBlinking() + { + if (_activeTween != null) + { + try + { + _activeTween.Cancel(); + } + catch + { + // Some versions of the tween API may not expose Cancel; ignore safely. + } + + _activeTween = null; + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/UI/BlinkingCanvasGroup.cs.meta b/Assets/Scripts/UI/BlinkingCanvasGroup.cs.meta new file mode 100644 index 00000000..1d0d116a --- /dev/null +++ b/Assets/Scripts/UI/BlinkingCanvasGroup.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b75de64fb2fd4ff0b869bf469e2becea +timeCreated: 1761299308 \ No newline at end of file diff --git a/Assets/Scripts/UI/CardSystem/BackpackInput.cs b/Assets/Scripts/UI/CardSystem/BackpackInput.cs deleted file mode 100644 index 018bc4d7..00000000 --- a/Assets/Scripts/UI/CardSystem/BackpackInput.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UnityEngine; - -namespace UI.CardSystem -{ - public class BackpackInput : MonoBehaviour, ITouchInputConsumer - { - // Start is called once before the first execution of Update after the MonoBehaviour is created - void Start() - { - - } - - // Update is called once per frame - void Update() - { - - } - - public void OnTap(Vector2 position) - { - return; - } - - public void OnHoldStart(Vector2 position) - { - return; - } - - public void OnHoldMove(Vector2 position) - { - return; - } - - public void OnHoldEnd(Vector2 position) - { - return; - } - } -} diff --git a/Assets/Scripts/UI/CardSystem/BackpackInput.cs.meta b/Assets/Scripts/UI/CardSystem/BackpackInput.cs.meta deleted file mode 100644 index 5809875e..00000000 --- a/Assets/Scripts/UI/CardSystem/BackpackInput.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e7ea50a695c58944799b4f27a9014301 \ No newline at end of file diff --git a/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs b/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs index d728cafb..3bcafcc4 100644 --- a/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs +++ b/Assets/Scripts/UI/CardSystem/CardAlbumUI.cs @@ -2,7 +2,6 @@ using Bootstrap; using Core; using Data.CardSystem; -using Input; using Pixelplacement; using UI.Core; using UnityEngine; @@ -33,8 +32,7 @@ namespace UI.CardSystem public GameObject BackpackIcon => backpackIcon; private UIPageController PageController => UIPageController.Instance; private CardSystemManager _cardManager; - private bool _isInitialized = false; - private bool _hasUnseenCards = false; + private bool _hasUnseenCards; private void Awake() { @@ -44,9 +42,6 @@ namespace UI.CardSystem backpackButton.onClick.AddListener(OnBackpackButtonClicked); } - // Initially show only the backpack icon - ShowOnlyBackpackIcon(); - // Hide notification dot initially if (boosterNotificationDot != null) boosterNotificationDot.gameObject.SetActive(false); @@ -57,8 +52,18 @@ namespace UI.CardSystem private void InitializePostBoot() { + // Initially show only the backpack icon + ShowOnlyBackpackIcon(); + // Initialize pages and hide them InitializePages(); + + // React to global UI hide/show events (top-page only) by toggling this GameObject + if (UIPageController.Instance != null) + { + UIPageController.Instance.OnAllUIHidden += HandleAllUIHidden; + UIPageController.Instance.OnAllUIShown += HandleAllUIShown; + } } private void Start() @@ -77,8 +82,6 @@ namespace UI.CardSystem // Initialize UI with current values UpdateBoosterCount(_cardManager.GetBoosterPackCount()); } - - _isInitialized = true; } private void OnDestroy() @@ -103,6 +106,13 @@ namespace UI.CardSystem { PageController.OnPageChanged -= OnPageChanged; } + + // Unsubscribe from global UI hide/show events + if (UIPageController.Instance != null) + { + UIPageController.Instance.OnAllUIHidden -= HandleAllUIHidden; + UIPageController.Instance.OnAllUIShown -= HandleAllUIShown; + } } /// @@ -142,10 +152,6 @@ namespace UI.CardSystem if (notificationSound != null) notificationSound.Play(); - // Eat input so the characters don't walk around - var backpackInput = backpackIcon.gameObject.GetComponentInParent(); - InputManager.Instance.RegisterOverrideConsumer(backpackInput); - PageController.PushPage(mainMenuPage); // Clear notification for unseen cards when opening menu @@ -159,6 +165,8 @@ namespace UI.CardSystem { backpackButton.gameObject.SetActive(false); } + + GameManager.Instance.RequestPause(this); } /// @@ -170,8 +178,7 @@ namespace UI.CardSystem if (newPage == null) { ShowOnlyBackpackIcon(); - var backpackInput = backpackIcon.gameObject.GetComponentInParent(); - InputManager.Instance.UnregisterOverrideConsumer(backpackInput); + GameManager.Instance.ReleasePause(this); } else { @@ -315,5 +322,19 @@ namespace UI.CardSystem // Just log the upgrade event without showing a notification Logging.Debug($"[CardAlbumUI] Card upgraded: {card.Name} to {card.Rarity}"); } + + // Handlers for UI controller hide/show events — toggle this GameObject active state + private void HandleAllUIHidden() + { + // Ensure UI returns to backpack-only state before deactivating so we don't leave the game paused + ShowOnlyBackpackIcon(); + backpackButton.gameObject.SetActive(false); + + } + + private void HandleAllUIShown() + { + backpackButton.gameObject.SetActive(true); + } } } diff --git a/Assets/Scripts/UI/Core/UIPageController.cs b/Assets/Scripts/UI/Core/UIPageController.cs index 9966f216..808ca9fd 100644 --- a/Assets/Scripts/UI/Core/UIPageController.cs +++ b/Assets/Scripts/UI/Core/UIPageController.cs @@ -23,6 +23,10 @@ namespace UI.Core // Event fired when the page stack changes public event Action OnPageChanged; + // Events fired when the controller hides or shows all UI pages + public event Action OnAllUIHidden; + public event Action OnAllUIShown; + private PlayerInput _playerInput; private InputAction _cancelAction; @@ -141,5 +145,39 @@ namespace UI.Core OnPageChanged?.Invoke(null); Logging.Debug("[UIPageController] Cleared page stack"); } + + /// + /// Hide all currently stacked UI pages by calling TransitionOut on each. + /// This does not modify the page stack; it simply asks pages to transition out + /// so UI elements can be hidden. Subscribers can react to . + /// + public void HideAllUI() + { + // Only transition out the top (active) page — maintains stack semantics. + var current = CurrentPage; + if (current != null) + { + current.TransitionOut(); + } + + OnAllUIHidden?.Invoke(); + } + + /// + /// Show all currently stacked UI pages by calling TransitionIn on each from bottom to top. + /// This does not modify the page stack; it asks pages to transition in so UI elements become visible. + /// Subscribers can react to . + /// + public void ShowAllUI() + { + // Only transition in the top (active) page — maintains stack semantics. + var current = CurrentPage; + if (current != null) + { + current.TransitionIn(); + } + + OnAllUIShown?.Invoke(); + } } } diff --git a/Assets/Scripts/UI/PauseMenu.cs b/Assets/Scripts/UI/PauseMenu.cs index 507d28ac..db8b500d 100644 --- a/Assets/Scripts/UI/PauseMenu.cs +++ b/Assets/Scripts/UI/PauseMenu.cs @@ -2,7 +2,6 @@ using System; using Core; using UnityEngine; using UnityEngine.SceneManagement; -using Input; using Bootstrap; using UI.Core; using Pixelplacement; @@ -12,7 +11,6 @@ namespace UI public class PauseMenu : UIPage { private static PauseMenu _instance; - private static bool _isQuitting; /// /// Singleton instance of the PauseMenu. No longer creates an instance if one doesn't exist. @@ -24,15 +22,6 @@ namespace UI [SerializeField] private GameObject pauseButton; [SerializeField] private CanvasGroup canvasGroup; - public event Action OnGamePaused; - public event Action OnGameResumed; - - private bool _isPaused = false; - - /// - /// Returns whether the game is currently paused - /// - public bool IsPaused => _isPaused; private void Awake() { @@ -57,6 +46,13 @@ namespace UI // Subscribe to scene loaded events SceneManagerService.Instance.SceneLoadCompleted += SetPauseMenuByLevel; + // Also react to global UI hide/show events from the page controller + if (UIPageController.Instance != null) + { + UIPageController.Instance.OnAllUIHidden += HandleAllUIHidden; + UIPageController.Instance.OnAllUIShown += HandleAllUIShown; + } + // SceneManagerService subscription moved to InitializePostBoot // Set initial state based on current scene @@ -72,11 +68,11 @@ namespace UI { SceneManagerService.Instance.SceneLoadCompleted -= SetPauseMenuByLevel; } - } - - void OnApplicationQuit() - { - _isQuitting = true; + if (UIPageController.Instance != null) + { + UIPageController.Instance.OnAllUIHidden -= HandleAllUIHidden; + UIPageController.Instance.OnAllUIShown -= HandleAllUIShown; + } } /// @@ -85,7 +81,7 @@ namespace UI /// The name of the level/scene public void SetPauseMenuByLevel(string levelName) { - HidePauseMenu(false); + HidePauseMenu(); // TODO: Implement level-based pause menu visibility logic if needed /*if (string.IsNullOrEmpty(levelName)) return; @@ -103,7 +99,6 @@ namespace UI /// public void ShowPauseMenu() { - if (_isPaused) return; if (UIPageController.Instance != null) { UIPageController.Instance.PushPage(this); @@ -128,9 +123,9 @@ namespace UI /// /// Hides the pause menu and shows the pause button. Sets input mode to Game. /// - public void HidePauseMenu(bool resetInput = true) + public void HidePauseMenu() { - if (!_isPaused) + if (!GameManager.Instance.IsPaused) { // Ensure UI is hidden if somehow active without state if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); @@ -148,7 +143,6 @@ namespace UI pauseMenuPanel.SetActive(false); if (pauseButton != null) pauseButton.SetActive(true); - EndPauseSideEffects(resetInput); if (canvasGroup != null) { canvasGroup.alpha = 0f; @@ -159,29 +153,31 @@ namespace UI } } + public void HidePauseMenuAndResumeGame() + { + HidePauseMenu(); + EndPauseSideEffects(); + } + /// /// Resumes the game by hiding the pause menu. /// public void ResumeGame() { - HidePauseMenu(); + HidePauseMenuAndResumeGame(); } private void BeginPauseSideEffects() { - _isPaused = true; if (pauseButton != null) pauseButton.SetActive(false); - InputManager.Instance.SetInputMode(InputMode.UI); - OnGamePaused?.Invoke(); + GameManager.Instance.RequestPause(this); Logging.Debug("[PauseMenu] Game Paused"); } - private void EndPauseSideEffects(bool invokeEvent) + private void EndPauseSideEffects() { - _isPaused = false; if (pauseButton != null) pauseButton.SetActive(true); - InputManager.Instance.SetInputMode(InputMode.GameAndUI); - if (invokeEvent) OnGameResumed?.Invoke(); + GameManager.Instance.ReleasePause(this); Logging.Debug("[PauseMenu] Game Resumed"); } @@ -189,6 +185,8 @@ namespace UI { // Ensure the panel root is active if (pauseMenuPanel != null) pauseMenuPanel.SetActive(true); + // Pause side effects should run immediately (hide button, set input mode, etc.). + // The tween itself must run in unscaled time so it still animates while the game is paused. BeginPauseSideEffects(); if (canvasGroup != null) @@ -196,7 +194,16 @@ namespace UI canvasGroup.interactable = true; canvasGroup.blocksRaycasts = true; canvasGroup.alpha = 0f; - Tween.Value(0f, 1f, v => canvasGroup.alpha = v, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete); + // pass obeyTimescale = false so this tween runs even when Time.timeScale == 0 + Tween.Value(0f, 1f, (v) => + { + Logging.Debug($"[PauseMenu] Tweening pause menu alpha: {v}"); + canvasGroup.alpha = v; + }, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, () => + { + Logging.Debug("[PauseMenu] Finished tweening pause menu in."); + onComplete?.Invoke(); + }, false); } else { @@ -210,16 +217,17 @@ namespace UI { canvasGroup.interactable = false; canvasGroup.blocksRaycasts = false; + // Run out-tween in unscaled time as well so the fade completes while paused. Tween.Value(canvasGroup.alpha, 0f, v => canvasGroup.alpha = v, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, () => - { - EndPauseSideEffects(true); - if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); - onComplete?.Invoke(); - }); + { + EndPauseSideEffects(); + if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); + onComplete?.Invoke(); + }, false); } else { - EndPauseSideEffects(true); + EndPauseSideEffects(); if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false); onComplete?.Invoke(); } @@ -261,7 +269,7 @@ namespace UI public async void LoadLevel(int levelSelection) { // Hide the pause menu before loading a new level - HidePauseMenu(); + HidePauseMenuAndResumeGame(); // Replace with the actual scene name as set in Build Settings var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); @@ -278,5 +286,42 @@ namespace UI break; } } + + // Handlers for UI controller hide/show events — make these safe with respect to transitions and pause state + private void HandleAllUIHidden() + { + var parent = transform.parent?.gameObject ?? gameObject; + + // If we're currently transitioning, wait for the transition out to complete before deactivating + if (_isTransitioning) + { + Action handler = null; + handler = () => + { + OnTransitionOutCompleted -= handler; + parent.SetActive(false); + }; + OnTransitionOutCompleted += handler; + return; + } + + // If this page is visible, request a proper hide so transition/out side-effects run (e.g. releasing pause) + if (_isVisible) + { + HidePauseMenu(); + return; + } + + // Otherwise it's safe to immediately deactivate + parent.SetActive(false); + } + + private void HandleAllUIShown() + { + var parent = transform.parent?.gameObject ?? gameObject; + + // Just ensure the parent is active. Do not force pause or transitions here. + parent.SetActive(true); + } } } diff --git a/Assets/Scripts/UI/Tutorial/DivingTutorial.cs b/Assets/Scripts/UI/Tutorial/DivingTutorial.cs index 3063a3ac..56b4e1e7 100644 --- a/Assets/Scripts/UI/Tutorial/DivingTutorial.cs +++ b/Assets/Scripts/UI/Tutorial/DivingTutorial.cs @@ -1,175 +1,205 @@ using System.Collections; -using System.Linq; -using UnityEngine; -using Pixelplacement; -using Minigames.DivingForPictures; +using Bootstrap; +using Core; using Input; -using UnityEngine.Events; +using Pixelplacement; +using UI.Core; +using UnityEngine; -public class DivingTutorial : MonoBehaviour, ITouchInputConsumer +namespace UI.Tutorial { - private StateMachine stateMachine; - public DivingGameManager divingGameManager; - public bool playTutorial; - - // gating for input until current state's animation finishes first loop - private bool canAcceptInput = false; - private Coroutine waitLoopCoroutine; - - // Start is called once before the first execution of Update after the MonoBehaviour is created - void Start() + public class DivingTutorial : MonoBehaviour, ITouchInputConsumer { - if (playTutorial == true) + private StateMachine _stateMachine; + public bool playTutorial; + + // gating for input until current state's animation finishes first loop + [SerializeField] private GameObject tapPrompt; + + private bool _canAcceptInput; + private Coroutine _waitLoopCoroutine; + + // Start is called once before the first execution of Update after the MonoBehaviour is created + void Start() { - InitializeTutorial(); + BootCompletionService.RegisterInitAction(InitializeTutorial); + + // Ensure prompt is hidden initially (even before tutorial initialization) + if (tapPrompt != null) + tapPrompt.SetActive(false); } - else { RemoveTutorial(); } - } - void InitializeTutorial() - { - stateMachine = GetComponentInChildren(); - divingGameManager.Pause(); - InputManager.Instance.RegisterOverrideConsumer(this); - stateMachine.OnLastStateExited.AddListener(RemoveTutorial); - - // prepare gating for the initial active state - SetupInputGateForCurrentState(); - } - - void RemoveTutorial() - { - Debug.Log("Remove me!"); - if (waitLoopCoroutine != null) + void InitializeTutorial() { - StopCoroutine(waitLoopCoroutine); - waitLoopCoroutine = null; - } - InputManager.Instance.UnregisterOverrideConsumer(this); - divingGameManager.DoResume(); - Destroy(gameObject); - } - - public void OnTap(Vector2 position) - { - if (!canAcceptInput) return; // block taps until allowed - - // consume this tap and immediately block further taps - canAcceptInput = false; - - // move to next state - stateMachine.Next(true); - - // after the state changes, set up gating for the new active state's animation - SetupInputGateForCurrentState(); - } - - public void OnHoldStart(Vector2 position) - { - return; - } - - public void OnHoldMove(Vector2 position) - { - return; - } - - public void OnHoldEnd(Vector2 position) - { - return; - } - - private void SetupInputGateForCurrentState() - { - if (waitLoopCoroutine != null) - { - StopCoroutine(waitLoopCoroutine); - waitLoopCoroutine = null; - } - waitLoopCoroutine = StartCoroutine(WaitForFirstLoopOnActiveState()); - } - - private IEnumerator WaitForFirstLoopOnActiveState() - { - // wait a frame to ensure StateMachine has activated the correct state GameObject - yield return null; - - // find the active child under the StateMachine (the current state) - Transform smTransform = stateMachine != null ? stateMachine.transform : transform; - Transform activeState = null; - for (int i = 0; i < smTransform.childCount; i++) - { - var child = smTransform.GetChild(i); - if (child.gameObject.activeInHierarchy) + if (playTutorial) { - activeState = child; - break; + // pause the game, hide UI, and register for input overrides + GameManager.Instance.RequestPause(this); + UIPageController.Instance.HideAllUI(); + InputManager.Instance.RegisterOverrideConsumer(this); + + // Setup references + _stateMachine = GetComponentInChildren(); + _stateMachine.OnLastStateExited.AddListener(RemoveTutorial); + + // prepare gating for the initial active state + SetupInputGateForCurrentState(); + } + else + { + RemoveTutorial(); } } - if (activeState == null) + void RemoveTutorial() { - // if we can't find an active state, fail open: allow input - canAcceptInput = true; - yield break; - } - - // look for a legacy Animation component on the active state - var anim = activeState.GetComponent(); - if (anim == null) - { - // no animation to wait for; allow input immediately - canAcceptInput = true; - yield break; - } - - // determine a clip/state to observe - string clipName = anim.clip != null ? anim.clip.name : null; - AnimationState observedState = null; - - if (!string.IsNullOrEmpty(clipName)) - { - observedState = anim[clipName]; - } - else - { - // fallback: take the first enabled state in the Animation - foreach (AnimationState st in anim) + Debug.Log("Remove me!"); + if (_waitLoopCoroutine != null) { - observedState = st; - break; + StopCoroutine(_waitLoopCoroutine); + _waitLoopCoroutine = null; + } + + // Unpause, unregister input, and show UI + InputManager.Instance.UnregisterOverrideConsumer(this); + UIPageController.Instance.ShowAllUI(); + GameManager.Instance.ReleasePause(this); + + // hide prompt if present + if (tapPrompt != null) + tapPrompt.SetActive(false); + + Destroy(gameObject); + } + + public void OnTap(Vector2 position) + { + if (!_canAcceptInput) return; // block taps until allowed + + // consume this tap and immediately block further taps + SetInputEnabled(false); + + // move to next state + _stateMachine.Next(true); + + // after the state changes, set up gating for the new active state's animation + SetupInputGateForCurrentState(); + } + + public void OnHoldStart(Vector2 position) + { + } + + public void OnHoldMove(Vector2 position) + { + } + + public void OnHoldEnd(Vector2 position) + { + } + + // centralize enabling/disabling input and the tap prompt + private void SetInputEnabled(bool allow) + { + _canAcceptInput = allow; + if (tapPrompt != null) + { + tapPrompt.SetActive(allow); } } - if (observedState == null) + private void SetupInputGateForCurrentState() { - // nothing to observe; allow input - canAcceptInput = true; - yield break; + if (_waitLoopCoroutine != null) + { + StopCoroutine(_waitLoopCoroutine); + _waitLoopCoroutine = null; + } + _waitLoopCoroutine = StartCoroutine(WaitForFirstLoopOnActiveState()); } - // wait until the animation starts playing the observed clip - float safetyTimer = 0f; - while (anim.isActiveAndEnabled && activeState.gameObject.activeInHierarchy && !anim.IsPlaying(observedState.name) && safetyTimer < 2f) + private IEnumerator WaitForFirstLoopOnActiveState() { - safetyTimer += Time.deltaTime; + // wait a frame to ensure StateMachine has activated the correct state GameObject yield return null; - } - // wait until the first loop completes (normalizedTime >= 1) - while (anim.isActiveAndEnabled && activeState.gameObject.activeInHierarchy) - { - // if state changed (not playing anymore), allow input to avoid deadlock - if (!anim.IsPlaying(observedState.name)) break; - - if (observedState.normalizedTime >= 1f) + // find the active child under the StateMachine (the current state) + Transform smTransform = _stateMachine != null ? _stateMachine.transform : transform; + Transform activeState = null; + for (int i = 0; i < smTransform.childCount; i++) { - break; + var child = smTransform.GetChild(i); + if (child.gameObject.activeInHierarchy) + { + activeState = child; + break; + } } - yield return null; - } - canAcceptInput = true; - waitLoopCoroutine = null; + if (activeState == null) + { + // if we can't find an active state, fail open: allow input + SetInputEnabled(true); + yield break; + } + + // look for a legacy Animation component on the active state + var anim = activeState.GetComponent(); + if (anim == null) + { + // no animation to wait for; allow input immediately + SetInputEnabled(true); + yield break; + } + + // determine a clip/state to observe + string clipName = anim.clip != null ? anim.clip.name : null; + AnimationState observedState = null; + + if (!string.IsNullOrEmpty(clipName)) + { + observedState = anim[clipName]; + } + else + { + // fallback: take the first enabled state in the Animation + foreach (AnimationState st in anim) + { + observedState = st; + break; + } + } + + if (observedState == null) + { + // nothing to observe; allow input + SetInputEnabled(true); + yield break; + } + + // wait until the animation starts playing the observed clip + float safetyTimer = 0f; + while (anim.isActiveAndEnabled && activeState.gameObject.activeInHierarchy && !anim.IsPlaying(observedState.name) && safetyTimer < 2f) + { + safetyTimer += Time.deltaTime; + yield return null; + } + + // wait until the first loop completes (normalizedTime >= 1) + while (anim.isActiveAndEnabled && activeState.gameObject.activeInHierarchy) + { + // if state changed (not playing anymore), allow input to avoid deadlock + if (!anim.IsPlaying(observedState.name)) break; + + if (observedState.normalizedTime >= 1f) + { + break; + } + yield return null; + } + + SetInputEnabled(true); + _waitLoopCoroutine = null; + } } }