From 0d5538c6e364175967545b180a9fb308235cdf68 Mon Sep 17 00:00:00 2001 From: ZicongCai Date: Tue, 5 Sep 2017 00:40:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Corgi=20Engine=20=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=20MMTools=20=E5=B7=A5=E5=85=B7=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/CorgiEngine.meta | 9 + Assets/CorgiEngine/MMTools.meta | 9 + Assets/CorgiEngine/MMTools/Achievements.meta | 9 + .../MMTools/Achievements/Fonts.meta | 9 + .../MMTools/Achievements/Fonts/orange kid.ttf | Bin 0 -> 38676 bytes .../Achievements/Fonts/orange kid.ttf.meta | 20 + .../MMTools/Achievements/Prefabs.meta | 9 + .../Prefabs/AchievementDisplay.prefab | 600 +++++ .../Prefabs/AchievementDisplay.prefab.meta | 8 + .../MMTools/Achievements/Resources.meta | 9 + .../Resources/AchievementDisplay.prefab | 600 +++++ .../Resources/AchievementDisplay.prefab.meta | 8 + .../MMTools/Achievements/Scripts.meta | 9 + .../MMTools/Achievements/Scripts/Editor.meta | 9 + .../Editor/MMAchievementListInspector.cs | 30 + .../Editor/MMAchievementListInspector.cs.meta | 12 + .../Scripts/Editor/MMAchievementMenu.cs | 19 + .../Scripts/Editor/MMAchievementMenu.cs.meta | 12 + .../Achievements/Scripts/MMAchievement.cs | 118 + .../Scripts/MMAchievement.cs.meta | 12 + .../Scripts/MMAchievementDisplayItem.cs | 19 + .../Scripts/MMAchievementDisplayItem.cs.meta | 12 + .../Scripts/MMAchievementDisplayer.cs | 102 + .../Scripts/MMAchievementDisplayer.cs.meta | 12 + .../Scripts/MMAchievementEvent.cs | 24 + .../Scripts/MMAchievementEvent.cs.meta | 12 + .../Achievements/Scripts/MMAchievementList.cs | 29 + .../Scripts/MMAchievementList.cs.meta | 12 + .../Scripts/MMAchievementManager.cs | 257 ++ .../Scripts/MMAchievementManager.cs.meta | 12 + .../Scripts/MMAchievementRules.cs | 68 + .../Scripts/MMAchievementRules.cs.meta | 12 + .../Scripts/SerializedMMAchievementManager.cs | 39 + .../SerializedMMAchievementManager.cs.meta | 12 + .../MMTools/Achievements/Sprites.meta | 9 + .../Sprites/AchievementBackground.png | Bin 0 -> 1914 bytes .../Sprites/AchievementBackground.png.meta | 59 + .../Sprites/AchievementBackgroundUnlocked.png | Bin 0 -> 1823 bytes .../AchievementBackgroundUnlocked.png.meta | 59 + .../Achievements/Sprites/AchievementIcon.png | Bin 0 -> 2738 bytes .../Sprites/AchievementIcon.png.meta | 59 + Assets/CorgiEngine/MMTools/Attributes.meta | 9 + .../MMTools/Attributes/Editor.meta | 9 + .../Editor/HiddenAttributeDrawer.cs | 24 + .../Editor/HiddenAttributeDrawer.cs.meta | 12 + .../Attributes/Editor/InformationDrawer.cs | 118 + .../Editor/InformationDrawer.cs.meta | 12 + .../Editor/MoreMountainsMenuHelp.cs | 77 + .../Editor/MoreMountainsMenuHelp.cs.meta | 12 + .../Editor/ReadOnlyAttributeDrawer.cs | 28 + .../Editor/ReadOnlyAttributeDrawer.cs.meta | 12 + .../MMTools/Attributes/HiddenAttribute.cs | 12 + .../Attributes/HiddenAttribute.cs.meta | 12 + .../Attributes/InformationAttribute.cs | 37 + .../Attributes/InformationAttribute.cs.meta | 12 + .../Attributes/InspectorButtonAttribute.cs | 55 + .../InspectorButtonAttribute.cs.meta | 12 + .../MMTools/Attributes/ReadOnlyAttribute.cs | 12 + .../Attributes/ReadOnlyAttribute.cs.meta | 12 + Assets/CorgiEngine/MMTools/AutoDestroy.meta | 9 + .../MMTools/AutoDestroy/TimedAutoDestroy.cs | 34 + .../AutoDestroy/TimedAutoDestroy.cs.meta | 12 + Assets/CorgiEngine/MMTools/Automation.meta | 9 + .../MMTools/Automation/AutoOrderInLayer.cs | 116 + .../Automation/AutoOrderInLayer.cs.meta | 12 + .../MMTools/Automation/AutoRotate.cs | 24 + .../MMTools/Automation/AutoRotate.cs.meta | 12 + .../MMTools/Automation/PathMovement.meta | 9 + .../Automation/PathMovement/Editor.meta | 9 + .../PathMovement/Editor/PathMovementEditor.cs | 78 + .../Editor/PathMovementEditor.cs.meta | 12 + .../Automation/PathMovement/PathMovement.cs | 409 +++ .../PathMovement/PathMovement.cs.meta | 12 + .../CorgiEngine/MMTools/Automation/Wiggle.cs | 188 ++ .../MMTools/Automation/Wiggle.cs.meta | 12 + Assets/CorgiEngine/MMTools/Camera.meta | 9 + .../MMTools/Camera/CameraAspectRatio.cs | 67 + .../MMTools/Camera/CameraAspectRatio.cs.meta | 12 + Assets/CorgiEngine/MMTools/Editor.meta | 9 + .../Editor/FindMissingScriptsRecursively.cs | 70 + .../FindMissingScriptsRecursively.cs.meta | 12 + Assets/CorgiEngine/MMTools/Events.meta | 9 + .../MMTools/Events/MMEventManager.cs | 215 ++ .../MMTools/Events/MMEventManager.cs.meta | 12 + Assets/CorgiEngine/MMTools/Extension.meta | 9 + .../MMTools/Extension/ExtensionMethods.cs | 62 + .../Extension/ExtensionMethods.cs.meta | 12 + Assets/CorgiEngine/MMTools/FPSCounter.meta | 9 + .../MMTools/FPSCounter/FPSCounter.cs | 53 + .../MMTools/FPSCounter/FPSCounter.cs.meta | 12 + Assets/CorgiEngine/MMTools/GUI.meta | 9 + .../MMTools/GUI/GetFocusOnEnable.cs | 17 + .../MMTools/GUI/GetFocusOnEnable.cs.meta | 12 + Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs | 28 + .../MMTools/GUI/ProgressBar.cs.meta | 12 + Assets/CorgiEngine/MMTools/Icons.meta | 9 + Assets/CorgiEngine/MMTools/Icons/Editor.meta | 9 + .../MMTools/Icons/Editor/ManagerIconEditor.cs | 26 + .../Icons/Editor/ManagerIconEditor.cs.meta | 12 + .../MMTools/Icons/SceneViewIcon.cs | 16 + .../MMTools/Icons/SceneViewIcon.cs.meta | 12 + Assets/CorgiEngine/MMTools/MMControls.meta | 9 + .../MMControls/MMControlsTestInputManager.cs | 53 + .../MMControlsTestInputManager.cs.meta | 12 + .../MMControls/MMControlsTestScene.unity | 2383 +++++++++++++++++ .../MMControls/MMControlsTestScene.unity.meta | 8 + .../MMTools/MMControls/MMSwipeZone.cs | 173 ++ .../MMTools/MMControls/MMSwipeZone.cs.meta | 12 + .../MMTools/MMControls/MMTouchAxis.cs | 172 ++ .../MMTools/MMControls/MMTouchAxis.cs.meta | 12 + .../MMTools/MMControls/MMTouchButton.cs | 176 ++ .../MMTools/MMControls/MMTouchButton.cs.meta | 12 + .../MMTools/MMControls/MMTouchControls.cs | 70 + .../MMControls/MMTouchControls.cs.meta | 12 + .../MMControls/MMTouchDynamicJoystick.cs | 88 + .../MMControls/MMTouchDynamicJoystick.cs.meta | 12 + .../MMTools/MMControls/MMTouchJoystick.cs | 186 ++ .../MMControls/MMTouchJoystick.cs.meta | 12 + .../MMTools/MMControls/Sprites.meta | 9 + .../Sprites/mobile-controls-spritesheet.png | Bin 0 -> 105880 bytes .../mobile-controls-spritesheet.png.meta | 359 +++ Assets/CorgiEngine/MMTools/MMHelpers.meta | 9 + .../MMTools/MMHelpers/MMAnimator.cs | 212 ++ .../MMTools/MMHelpers/MMAnimator.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMBounds.cs | 90 + .../MMTools/MMHelpers/MMBounds.cs.meta | 12 + .../MMTools/MMHelpers/MMConsole.cs | 98 + .../MMTools/MMHelpers/MMConsole.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMDebug.cs | 258 ++ .../MMTools/MMHelpers/MMDebug.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMFade.cs | 118 + .../MMTools/MMHelpers/MMFade.cs.meta | 12 + Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs | 19 + .../MMTools/MMHelpers/MMGUI.cs.meta | 12 + .../MMTools/MMHelpers/MMHelpers.cs | 35 + .../MMTools/MMHelpers/MMHelpers.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMImage.cs | 46 + .../MMTools/MMHelpers/MMImage.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMInput.cs | 99 + .../MMTools/MMHelpers/MMInput.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMLayers.cs | 21 + .../MMTools/MMHelpers/MMLayers.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMLists.cs | 20 + .../MMTools/MMHelpers/MMLists.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMMaths.cs | 234 ++ .../MMTools/MMHelpers/MMMaths.cs.meta | 12 + .../MMTools/MMHelpers/MMMovement.cs | 39 + .../MMTools/MMHelpers/MMMovement.cs.meta | 12 + .../CorgiEngine/MMTools/MMHelpers/MMTime.cs | 49 + .../MMTools/MMHelpers/MMTime.cs.meta | 12 + Assets/CorgiEngine/MMTools/ObjectBounds.meta | 9 + .../MMTools/ObjectBounds/Editor.meta | 9 + .../ObjectBounds/Editor/ObjectBoundsEditor.cs | 36 + .../Editor/ObjectBoundsEditor.cs.meta | 12 + .../MMTools/ObjectBounds/ObjectBounds.cs | 84 + .../MMTools/ObjectBounds/ObjectBounds.cs.meta | 12 + Assets/CorgiEngine/MMTools/ObjectPool.meta | 9 + .../ObjectPool/MultipleObjectPooler.cs | 514 ++++ .../ObjectPool/MultipleObjectPooler.cs.meta | 12 + .../MMTools/ObjectPool/ObjectPooler.cs | 50 + .../MMTools/ObjectPool/ObjectPooler.cs.meta | 12 + .../MMTools/ObjectPool/PoolableObject.cs | 61 + .../MMTools/ObjectPool/PoolableObject.cs.meta | 12 + .../MMTools/ObjectPool/SimpleObjectPooler.cs | 83 + .../ObjectPool/SimpleObjectPooler.cs.meta | 12 + Assets/CorgiEngine/MMTools/Particles.meta | 9 + .../Particles/AutoDestroyParticleSystem.cs | 64 + .../AutoDestroyParticleSystem.cs.meta | 8 + .../MMTools/Particles/ChangeFogColor.cs | 42 + .../MMTools/Particles/ChangeFogColor.cs.meta | 12 + .../CorgiEngine/MMTools/Particles/Editor.meta | 9 + .../MMTrailRendererSortingLayerEditor.cs | 62 + .../MMTrailRendererSortingLayerEditor.cs.meta | 12 + .../Particles/MMTrailRendererSortingLayer.cs | 9 + .../MMTrailRendererSortingLayer.cs.meta | 12 + .../MMTools/Particles/VisibleParticle.cs | 19 + .../MMTools/Particles/VisibleParticle.cs.meta | 8 + .../MMTools/RigidbodyInterface.meta | 9 + .../RigidbodyInterface/RigidbodyInterface.cs | 315 +++ .../RigidbodyInterface.cs.meta | 12 + Assets/CorgiEngine/MMTools/SaveLoad.meta | 9 + .../MMTools/SaveLoad/SaveLoadManager.cs | 77 + .../MMTools/SaveLoad/SaveLoadManager.cs.meta | 12 + Assets/CorgiEngine/MMTools/Singletons.meta | 9 + .../Singletons/PersistentHumbleSingleton.cs | 69 + .../PersistentHumbleSingleton.cs.meta | 8 + .../MMTools/Singletons/PersistentSingleton.cs | 63 + .../Singletons/PersistentSingleton.cs.meta | 8 + .../MMTools/Singletons/Singleton.cs | 47 + .../MMTools/Singletons/Singleton.cs.meta | 12 + Assets/CorgiEngine/MMTools/StateMachine.meta | 9 + .../MMTools/StateMachine/MMStateMachine.cs | 136 + .../StateMachine/MMStateMachine.cs.meta | 12 + 193 files changed, 11772 insertions(+) create mode 100644 Assets/CorgiEngine.meta create mode 100644 Assets/CorgiEngine/MMTools.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Fonts.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Fonts/orange kid.ttf create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Fonts/orange kid.ttf.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Prefabs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Prefabs/AchievementDisplay.prefab create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Prefabs/AchievementDisplay.prefab.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Resources.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Resources/AchievementDisplay.prefab create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Resources/AchievementDisplay.prefab.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementListInspector.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementListInspector.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementManager.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementManager.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementRules.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementRules.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png.meta create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png create mode 100644 Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs create mode 100644 Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/AutoDestroy.meta create mode 100644 Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs create mode 100644 Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs create mode 100644 Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs create mode 100644 Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/PathMovement.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor/PathMovementEditor.cs create mode 100644 Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor/PathMovementEditor.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/PathMovement/PathMovement.cs create mode 100644 Assets/CorgiEngine/MMTools/Automation/PathMovement/PathMovement.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Automation/Wiggle.cs create mode 100644 Assets/CorgiEngine/MMTools/Automation/Wiggle.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Camera.meta create mode 100644 Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs create mode 100644 Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs create mode 100644 Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Events.meta create mode 100644 Assets/CorgiEngine/MMTools/Events/MMEventManager.cs create mode 100644 Assets/CorgiEngine/MMTools/Events/MMEventManager.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Extension.meta create mode 100644 Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs create mode 100644 Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/FPSCounter.meta create mode 100644 Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs create mode 100644 Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/GUI.meta create mode 100644 Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs create mode 100644 Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs create mode 100644 Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Icons.meta create mode 100644 Assets/CorgiEngine/MMTools/Icons/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs create mode 100644 Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs create mode 100644 Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs create mode 100644 Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/Sprites.meta create mode 100644 Assets/CorgiEngine/MMTools/MMControls/Sprites/mobile-controls-spritesheet.png create mode 100644 Assets/CorgiEngine/MMTools/MMControls/Sprites/mobile-controls-spritesheet.png.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMAnimator.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMAnimator.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMImage.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMImage.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMInput.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMInput.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs create mode 100644 Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectBounds.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectBounds/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs create mode 100644 Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs create mode 100644 Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs create mode 100644 Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs create mode 100644 Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs create mode 100644 Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles/Editor.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles/Editor/MMTrailRendererSortingLayerEditor.cs create mode 100644 Assets/CorgiEngine/MMTools/Particles/Editor/MMTrailRendererSortingLayerEditor.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles/MMTrailRendererSortingLayer.cs create mode 100644 Assets/CorgiEngine/MMTools/Particles/MMTrailRendererSortingLayer.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Particles/VisibleParticle.cs create mode 100644 Assets/CorgiEngine/MMTools/Particles/VisibleParticle.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/RigidbodyInterface.meta create mode 100644 Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs create mode 100644 Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/SaveLoad.meta create mode 100644 Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs create mode 100644 Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Singletons.meta create mode 100644 Assets/CorgiEngine/MMTools/Singletons/PersistentHumbleSingleton.cs create mode 100644 Assets/CorgiEngine/MMTools/Singletons/PersistentHumbleSingleton.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Singletons/PersistentSingleton.cs create mode 100644 Assets/CorgiEngine/MMTools/Singletons/PersistentSingleton.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/Singletons/Singleton.cs create mode 100644 Assets/CorgiEngine/MMTools/Singletons/Singleton.cs.meta create mode 100644 Assets/CorgiEngine/MMTools/StateMachine.meta create mode 100644 Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs create mode 100644 Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs.meta diff --git a/Assets/CorgiEngine.meta b/Assets/CorgiEngine.meta new file mode 100644 index 0000000..3dc391a --- /dev/null +++ b/Assets/CorgiEngine.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b00c0c284be3c1444a05b6379a2eebfb +folderAsset: yes +timeCreated: 1504542008 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools.meta b/Assets/CorgiEngine/MMTools.meta new file mode 100644 index 0000000..df9865b --- /dev/null +++ b/Assets/CorgiEngine/MMTools.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 9d6b4a7c42912f8489127fe42bb2b6a0 +folderAsset: yes +timeCreated: 1504542297 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements.meta b/Assets/CorgiEngine/MMTools/Achievements.meta new file mode 100644 index 0000000..b9055a7 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 67fd5358ba9281741a5298b81723baa6 +folderAsset: yes +timeCreated: 1482071371 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Fonts.meta b/Assets/CorgiEngine/MMTools/Achievements/Fonts.meta new file mode 100644 index 0000000..a18419f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Fonts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 602325afa3a7e49e6a02b9bef136256b +folderAsset: yes +timeCreated: 1480001014 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Fonts/orange kid.ttf b/Assets/CorgiEngine/MMTools/Achievements/Fonts/orange kid.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8c1b9b2a1feca5e460ff9424d18556149fc8cd7b GIT binary patch literal 38676 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 + /// Custom inspector for the MMAchievementList scriptable object. + /// + public class MMAchievementListInspector : Editor + { + /// + /// When drawing the GUI, adds a "Reset Achievements" button, that does exactly what you think it does. + /// + public override void OnInspectorGUI() + { + DrawDefaultInspector (); + MMAchievementList achievementList = (MMAchievementList)target; + if(GUILayout.Button("Reset Achievements")) + { + achievementList.ResetAchievements(); + } + EditorUtility.SetDirty (achievementList); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementListInspector.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementListInspector.cs.meta new file mode 100644 index 0000000..47f6736 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementListInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 70d3db55e641fa4428b605f636e0015b +timeCreated: 1480263802 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs new file mode 100644 index 0000000..ca17442 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; +using UnityEditor; + +namespace MoreMountains.Tools +{ + public static class MMAchievementMenu + { + [MenuItem("More Mountains/Reset all achievements",false,21)] + /// + /// Adds a menu item to enable help + /// + private static void EnableHelpInInspectors() + { + MMAchievementManager.ResetAllAchievements (); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs.meta new file mode 100644 index 0000000..0cab590 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/Editor/MMAchievementMenu.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a1d53c42aa1029048b98580478952105 +timeCreated: 1482318762 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs new file mode 100644 index 0000000..0c2ab1a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs @@ -0,0 +1,118 @@ +using UnityEngine; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// This achievement system supports 2 types of achievements : simple (do something > get achievement), and progress based (jump X times, kill X enemies, etc). + /// + public enum AchievementTypes { Simple, Progress } + + [Serializable] + public class MMAchievement + { + [Header("Identification")] + /// the unique identifier for this achievement + public string AchievementID; + /// is this achievement progress based or + public AchievementTypes AchievementType; + /// if this is true, the achievement won't be displayed in a list + public bool HiddenAchievement; + /// if this is true, the achievement has been unlocked. Otherwise, it's still up for grabs + public bool UnlockedStatus; + + [Header("Description")] + /// the achievement's name/title + public string Title; + /// the achievement's description + public string Description; + /// the amount of points unlocking this achievement gets you + public int Points; + + [Header("Image and Sounds")] + /// the image to display while this achievement is locked + public Sprite LockedImage; + /// the image to display when the achievement is unlocked + public Sprite UnlockedImage; + /// a sound to play when the achievement is unlocked + public AudioClip UnlockedSound; + + [Header("Progress")] + /// the amount of progress needed to unlock this achievement. + public int ProgressTarget; + /// the current amount of progress made on this achievement + public int ProgressCurrent; + + protected MMAchievementDisplayItem _achievementDisplayItem; + + /// + /// Unlocks the achievement, asks for a save of the current achievements, and triggers an MMAchievementUnlockedEvent for this achievement. + /// This will usually then be caught by the MMAchievementDisplay class. + /// + public virtual void UnlockAchievement() + { + // if the achievement has already been unlocked, we do nothing and exit + if (UnlockedStatus) + { + return; + } + + UnlockedStatus = true; + + MMEventManager.TriggerEvent(new MMGameEvent("Save")); + MMEventManager.TriggerEvent(new MMAchievementUnlockedEvent(this)); + } + + /// + /// Locks the achievement. + /// + public virtual void LockAchievement() + { + UnlockedStatus = false; + } + + /// + /// Adds the specified value to the current progress. + /// + /// New progress. + public virtual void AddProgress(int newProgress) + { + ProgressCurrent += newProgress; + EvaluateProgress(); + } + + /// + /// Sets the progress to the value passed in parameter. + /// + /// New progress. + public virtual void SetProgress(int newProgress) + { + ProgressCurrent = newProgress; + EvaluateProgress(); + } + + /// + /// Evaluates the current progress of the achievement, and unlocks it if needed. + /// + protected virtual void EvaluateProgress() + { + if (ProgressCurrent >= ProgressTarget) + { + ProgressCurrent = ProgressTarget; + UnlockAchievement(); + } + } + + /// + /// Copies this achievement (useful when loading from a scriptable object list) + /// + public virtual MMAchievement Copy() + { + MMAchievement clone = new MMAchievement (); + // we use Json utility to store a copy of our achievement, not a reference + clone = JsonUtility.FromJson(JsonUtility.ToJson(this)); + return clone; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs.meta new file mode 100644 index 0000000..79976f8 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievement.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 12a7d0ec5f1424c129527c01a444e964 +timeCreated: 1480085861 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs new file mode 100644 index 0000000..c532a56 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; + +namespace MoreMountains.Tools +{ + /// + /// This class is used to display an achievement. Add it to a prefab containing all the required elements listed below. + /// + public class MMAchievementDisplayItem : MonoBehaviour + { + public Image BackgroundLocked; + public Image BackgroundUnlocked; + public Image Icon; + public Text Title; + public Text Description; + public ProgressBar ProgressBarDisplay; + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs.meta new file mode 100644 index 0000000..668faa2 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayItem.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4e775494159ed5e4c997035d2df49776 +timeCreated: 1480176460 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs new file mode 100644 index 0000000..3a86ee4 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs @@ -0,0 +1,102 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; + +namespace MoreMountains.Tools +{ + /// + /// A class used to display the achievements on screen. + /// The AchievementDisplayItems will be parented to it, so it's better if it has a LayoutGroup (Vertical or Horizontal) too. + /// + public class MMAchievementDisplayer : MonoBehaviour, MMEventListener + { + [Header("Achievements")] + /// the prefab to use to display achievements + public MMAchievementDisplayItem AchievementDisplayPrefab; + /// the duration the achievement will remain on screen for when unlocked + public float AchievementDisplayDuration = 5f; + /// the fade in/out speed + public float AchievementFadeDuration = 0.2f; + + protected WaitForSeconds _achievementFadeOutWFS; + + /// + /// Instantiates an achievement display prefab and shows it for the specified duration + /// + /// The achievement. + /// Achievement. + public virtual IEnumerator DisplayAchievement(MMAchievement achievement) + { + if ((this.transform == null) || (AchievementDisplayPrefab == null)) + { + yield break; + } + + // we instantiate our achievement display prefab, and add it to the group that will automatically handle its position + GameObject instance = (GameObject)Instantiate(AchievementDisplayPrefab.gameObject); + instance.transform.SetParent(this.transform,false); + + // we get the achievement displayer + MMAchievementDisplayItem achievementDisplay = instance.GetComponent (); + if (achievementDisplay == null) + { + yield break; + } + + // we fill our achievement + achievementDisplay.Title.text = achievement.Title; + achievementDisplay.Description.text = achievement.Description; + achievementDisplay.Icon.sprite = achievement.UnlockedImage; + if (achievement.AchievementType == AchievementTypes.Progress) + { + achievementDisplay.ProgressBarDisplay.gameObject.SetActive(true); + } + else + { + achievementDisplay.ProgressBarDisplay.gameObject.SetActive(false); + } + + // we play a sound if set + if (achievement.UnlockedSound != null) + { + MMEventManager.TriggerEvent(new MMSfxEvent (achievement.UnlockedSound)); + } + + // we fade it in and out + CanvasGroup achievementCanvasGroup = instance.GetComponent (); + if (achievementCanvasGroup != null) + { + achievementCanvasGroup.alpha = 0; + StartCoroutine(MMFade.FadeCanvasGroup(achievementCanvasGroup, AchievementFadeDuration, 1)); + yield return _achievementFadeOutWFS; + StartCoroutine(MMFade.FadeCanvasGroup(achievementCanvasGroup, AchievementFadeDuration, 0)); + } + } + + /// + /// When an achievement is unlocked, we display it + /// + /// Achievement unlocked event. + public virtual void OnMMEvent(MMAchievementUnlockedEvent achievementUnlockedEvent) + { + StartCoroutine(DisplayAchievement (achievementUnlockedEvent.Achievement)); + } + + /// + /// On enable, we start listening for unlocked achievements + /// + protected virtual void OnEnable() + { + this.MMEventStartListening(); + _achievementFadeOutWFS = new WaitForSeconds (AchievementFadeDuration + AchievementDisplayDuration); + } + + /// + /// On disable, we stop listening for unlocked achievements + /// + protected virtual void OnDisable() + { + this.MMEventStopListening(); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs.meta new file mode 100644 index 0000000..432921d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementDisplayer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f759f536b039f2b46881cfc8ea77c496 +timeCreated: 1480176245 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs new file mode 100644 index 0000000..3c494dd --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs @@ -0,0 +1,24 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + /// + /// An event type used to broadcast the fact that an achievement has been unlocked + /// + public struct MMAchievementUnlockedEvent + { + /// the achievement that has been unlocked + public MMAchievement Achievement; + + /// + /// Constructor + /// + /// New achievement. + public MMAchievementUnlockedEvent(MMAchievement newAchievement) + { + Achievement = newAchievement; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs.meta new file mode 100644 index 0000000..6c0b7d9 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementEvent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d8ab561ba77c94d3fafd05ffde7d9f7c +timeCreated: 1480085871 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs new file mode 100644 index 0000000..01b1819 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + [CreateAssetMenu(fileName="AchievementList",menuName="MoreMountains/Achievement List")] + /// + /// A scriptable object containing a list of achievements. You need to create one and store it in a Resources folder for this to work. + /// + public class MMAchievementList : ScriptableObject + { + /// the unique ID of this achievement list. This is used to save/load data. + public string AchievementsListID = "AchievementsList"; + + /// the list of achievements + public List Achievements; + + /// + /// Asks for a reset of all the achievements in this list (they'll all be locked again, their progress lost). + /// + public virtual void ResetAchievements() + { + MMDebug.DebugLogTime ("Reset Achievements"); + MMAchievementManager.ResetAchievements (AchievementsListID); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs.meta new file mode 100644 index 0000000..165a9cf --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementList.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6f8f916bd4c5a444fa42e298d90f488d +timeCreated: 1480177569 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementManager.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementManager.cs new file mode 100644 index 0000000..15d8c61 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementManager.cs @@ -0,0 +1,257 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +namespace MoreMountains.Tools +{ + [ExecuteInEditMode] + /// + /// This static class is in charge of storing the current state of the achievements, unlocking/locking them, and saving them to data files + /// + public static class MMAchievementManager + { + private static List Achievements; + private static MMAchievement _achievement = null; + + const string _defaultFileName = "Achievements"; + const string _saveFolderName = "/MMData/MMAchievements/"; + private static string _savePath; + private static string _saveFileName; + private static string _listID; + + /// + /// You'll need to call this method to initialize the manager + /// + public static void LoadAchievementList() + { + Achievements = new List (); + + // the Achievement List scriptable object must be in a Resources folder inside your project, like so : Resources/Achievements/PUT_SCRIPTABLE_OBJECT_HERE + MMAchievementList achievementList = (MMAchievementList) Resources.Load("Achievements/AchievementList"); + + if (achievementList == null) + { + return; + } + + // we store the ID for save purposes + _listID = achievementList.AchievementsListID; + + foreach (MMAchievement achievement in achievementList.Achievements) + { + Achievements.Add (achievement.Copy()); + } + } + + /// + /// Unlocks the specified achievement (if found). + /// + /// Achievement I. + public static void UnlockAchievement(string achievementID) + { + _achievement = AchievementManagerContains(achievementID); + if (_achievement != null) + { + _achievement.UnlockAchievement(); + } + } + + /// + /// Locks the specified achievement (if found). + /// + /// Achievement ID. + public static void LockAchievement(string achievementID) + { + _achievement = AchievementManagerContains(achievementID); + if (_achievement != null) + { + _achievement.LockAchievement(); + } + } + + /// + /// Adds progress to the specified achievement (if found). + /// + /// Achievement ID. + /// New progress. + public static void AddProgress(string achievementID, int newProgress) + { + _achievement = AchievementManagerContains(achievementID); + if (_achievement != null) + { + _achievement.AddProgress(newProgress); + } + } + + /// + /// Sets the progress of the specified achievement (if found) to the specified progress. + /// + /// Achievement ID. + /// New progress. + public static void SetProgress(string achievementID, int newProgress) + { + _achievement = AchievementManagerContains(achievementID); + if (_achievement != null) + { + _achievement.SetProgress(newProgress); + } + } + + /// + /// Determines if the achievement manager contains an achievement of the specified ID. Returns it if found, otherwise returns null + /// + /// The achievement corresponding to the searched ID if found, otherwise null. + /// Searched I. + private static MMAchievement AchievementManagerContains(string searchedID) + { + if (Achievements.Count == 0) + { + return null; + } + foreach(MMAchievement achievement in Achievements) + { + if (achievement.AchievementID == searchedID) + { + return achievement; + } + } + return null; + } + + /// + /// Removes saved data and resets all achievements from a list + /// + /// The ID of the achievement list to reset. + public static void ResetAchievements(string listID) + { + if (Achievements != null) + { + foreach(MMAchievement achievement in Achievements) + { + achievement.ProgressCurrent = 0; + achievement.UnlockedStatus = false; + } + } + + DeterminePath (listID); + MMDebug.DebugLogTime ("Removing " + _saveFileName); + File.Delete(_saveFileName); + MMDebug.DebugLogTime ("Removing " + _saveFileName+".meta"); + File.Delete(_saveFileName+".meta"); + MMDebug.DebugLogTime ("Achievements Reset"); + } + + public static void ResetAllAchievements() + { + LoadAchievementList (); + ResetAchievements (_listID); + } + + // SAVE ------------------------------------------------------------------------------------------------------------------------------------ + + /// + /// Loads the saved achievements file and updates the array with its content. + /// + public static void LoadSavedAchievements() + { + DeterminePath (); + + // if the MMSaves directory or the save file doesn't exist, there's nothing to load, we do nothing and exit + if (!Directory.Exists(_savePath) || !File.Exists(_saveFileName)) + { + //MMDebug.DebugLogTime("Nothing to load at "+_saveFileName); + return; + } + BinaryFormatter formatter = new BinaryFormatter(); + FileStream saveFile = File.Open(_saveFileName, FileMode.Open, FileAccess.Read, FileShare.Read); + SerializedMMAchievementManager serializedMMAchievementManager = (SerializedMMAchievementManager)formatter.Deserialize(saveFile); + saveFile.Close(); + + ExtractSerializedMMAchievementManager(serializedMMAchievementManager); + } + + /// + /// Saves the achievements current status to a file on disk + /// + public static void SaveAchievements() + { + DeterminePath (); + + if (!Directory.Exists(_savePath)) + { + Directory.CreateDirectory(_savePath); + } + BinaryFormatter formatter = new BinaryFormatter(); + FileStream saveFile = File.Create(_saveFileName); + + SerializedMMAchievementManager serializedMMAchievementManager = new SerializedMMAchievementManager(); + FillSerializedMMAchievementManager(serializedMMAchievementManager); + + formatter.Serialize(saveFile, serializedMMAchievementManager); + + saveFile.Close(); + } + + /// + /// Determines the path the achievements save file should be saved to. + /// + private static void DeterminePath(string specifiedFileName = "") + { + if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.Android) + { + _savePath = Application.persistentDataPath + _saveFolderName; + } + else + { + _savePath = Application.dataPath + _saveFolderName; + } + + string tempFileName = (_listID != "") ? _listID : _defaultFileName; + if (specifiedFileName != "") + { + tempFileName = specifiedFileName; + } + + _saveFileName = _savePath + tempFileName + ".achievements.binary"; + } + + /// + /// Serializes the contents of the achievements array to a serialized, ready to save object + /// + /// Serialized inventory. + public static void FillSerializedMMAchievementManager(SerializedMMAchievementManager serializedAchievements) + { + serializedAchievements.Achievements = new SerializedMMAchievement[Achievements.Count]; + + for (int i = 0; i < Achievements.Count(); i++) + { + SerializedMMAchievement newAchievement = new SerializedMMAchievement (Achievements[i].AchievementID, Achievements[i].UnlockedStatus, Achievements[i].ProgressCurrent); + serializedAchievements.Achievements [i] = newAchievement; + //MMDebug.DebugLogTime ("Save : " + serializedAchievements.Achievements [i].AchievementID+", status : "+serializedAchievements.Achievements [i].UnlockedStatus); + } + } + + /// + /// Extracts the serialized achievements into our achievements array if the achievements ID match. + /// + /// Serialized achievements. + public static void ExtractSerializedMMAchievementManager(SerializedMMAchievementManager serializedAchievements) + { + for (int i = 0; i < Achievements.Count(); i++) + { + for (int j=0; j + /// That class is meant to be extended to implement the achievement rules specific to your game. + /// + public class MMAchievementRules : MonoBehaviour, MMEventListener + { + /// + /// On Awake, loads the achievement list and the saved file + /// + protected virtual void Awake() + { + // we load the list of achievements, stored in a ScriptableObject in our Resources folder. + MMAchievementManager.LoadAchievementList (); + // we load our saved file, to update that list with the saved values. + MMAchievementManager.LoadSavedAchievements (); + } + + /// + /// On enable, we start listening for MMGameEvents. You may want to extend that to listen to other types of events. + /// + protected virtual void OnEnable() + { + this.MMEventStartListening(); + } + + /// + /// On disable, we stop listening for MMGameEvents. You may want to extend that to stop listening to other types of events. + /// + protected virtual void OnDisable() + { + this.MMEventStopListening(); + } + + /// + /// When we catch an MMGameEvent, we do stuff based on its name + /// + /// Game event. + public virtual void OnMMEvent(MMGameEvent gameEvent) + { + switch (gameEvent.EventName) + { + case "Save": + MMAchievementManager.SaveAchievements (); + break; + /* + // These are just examples of how you could catch a GameStart MMGameEvent and trigger the potential unlock of a corresponding achievement + case "GameStart": + MMAchievementManager.UnlockAchievement("theFirestarter"); + break; + case "LifeLost": + MMAchievementManager.UnlockAchievement("theEndOfEverything"); + break; + case "Pause": + MMAchievementManager.UnlockAchievement("timeStop"); + break; + case "Jump": + MMAchievementManager.UnlockAchievement ("aSmallStepForMan"); + MMAchievementManager.AddProgress ("toInfinityAndBeyond", 1); + break;*/ + } + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementRules.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementRules.cs.meta new file mode 100644 index 0000000..21842b6 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/MMAchievementRules.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2c7c529377dc843a9a2316262550c4d6 +timeCreated: 1480085861 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs b/Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs new file mode 100644 index 0000000..39a3809 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs @@ -0,0 +1,39 @@ +using UnityEngine; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + [Serializable] + /// + /// A serializable class used to store an achievement into a save file + /// + public class SerializedMMAchievement + { + public string AchievementID; + public bool UnlockedStatus; + public int ProgressCurrent; + + /// + /// Initializes a new instance of the class. + /// + /// Achievement I. + /// If set to true unlocked status. + /// Progress current. + public SerializedMMAchievement(string achievementID, bool unlockedStatus, int progressCurrent) + { + AchievementID = achievementID; + UnlockedStatus = unlockedStatus; + ProgressCurrent = progressCurrent; + } + } + + [Serializable] + /// + /// Serializable MM achievement manager. + /// + public class SerializedMMAchievementManager + { + public SerializedMMAchievement[] Achievements; + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs.meta b/Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs.meta new file mode 100644 index 0000000..6c9219c --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Scripts/SerializedMMAchievementManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 24f964aa3d55241b591bc1da7903819e +timeCreated: 1480085861 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites.meta b/Assets/CorgiEngine/MMTools/Achievements/Sprites.meta new file mode 100644 index 0000000..e667666 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Sprites.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 7fee60bb7a0474e9bad63c409ba9e02d +folderAsset: yes +timeCreated: 1479996221 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png new file mode 100644 index 0000000000000000000000000000000000000000..dd09ede68c4dd2b58556f4882a04e5363aa457e6 GIT binary patch literal 1914 zcmbtTdrVVT7(ZZj$RwgTd}h0;Zs@dcTW`A-gchWUEvDc=X@qNgDV_A*OYemiM2Z*% zjT5I?oFcl#1mX*p`KUZ>AyElYWSa;|MFr6fK^*8%DB^AraDT|Mxs#Ke?>qT^zwft`sfS)o_p#i{~7D0}W=jjthXz6Dz6&^=L<4KeSF{2<h8WhoI zq}HKQ049BIjESS-RI4RA+{8vaHf*bj@NfZXh?PKen@|ech#HKTjQO z;;INanrw{BG^5d(>KI+-CY@N%3<-v$RtZgDLMa5Yno=-QVwEvRcqO##`Q|X85ff^Y zjQJ+0IMo_Rj+;?P$QH45JOLkCA!hT0Vm`cl8N}!EL>%sN4o}SD2_*Dg&V@z~llErT zCrLDlh*4j(CSxX36d~boEEWsfB4FcY1C14nIb1%6&u7sVEHVS55GxBK7mhI~P*P_$ z5|k0gAP*z55l^FJOghpxDVT_HT8tbm6J0Ql6(Kl0HrJEVn4wDb&!#5RxHU;>(6{lv zlbDRjAW)75CGj+~j&5AiLXRs#A~&N5g_~n=JY}qkYm#vaCzEjklFL0+6S9TSVilq@ zVxI3MBRG{xqQpoF!E~rnA!E`^Y@<;x5pl!#qEH1K8paFd@xn!LI4p)0Vj(XSh9iUm zcnqt+b!jFPqsFlMw^;FnSkFk92->p(H5=1WeS{e|K_jV4j1$Kqnvic4tDiWQ6%%4P zbTAxGga6j>u`haxJmUEH(#rS(pcp;%W_rBkixO|sTk}Y%2#v9}bi3l!u9dTL+KLtJ zQ!fR~-pc4^#v`?-a<>1t^?s8^e_K#%NSQigXp82XLfEmpc6jRvx7}_J0Vxd*_p{+) z3<9Eu1Uw}41+GP&70v+w0>J0}#Qz4b{>uHp*7_@E@0y)?%(;j0G#yV%sI=sDJ->P* zc1Kt9`H=PGS##BacI2o-{i0A>)1H5`>EOrYqf>b&{p{Sq#2e+)>|aVxv?%@TpG58M zgld=88h9=ElQ}!0G5E!k%1@Bh{ z0{#Fn0Pq4U!F!4S8!rF1gJ7&|?HL$Y2Y~3e;o;%7wzk2+^|aE_*VotCc|87ir!T1H zG;eX*ES6?gRaKQ%t9@S9Ve?tcxNnsP#>y^k(Eeacy`aYXeToZle*xLn#}xVcGmgMupmpUmNjs^vu0Uke($M0A0X)o1LWNL zI^q7R&PTpIxh;FIJR|zQZR(1zWYjFUDm~tG@5t!@hdbN3`MJ~KxO?Z$;SE_?S*wG) z`s>;@{G3y~%8>02DfhbUUn*|IvFhCV^PEQm?GwEe=K6bqxn(npv zfe(=$930$)Uy1@?{-^I;vBL$n*8wjHc#cob>4BMvz`HTEC@-#JEufDwT&*Y#OU(TX DjAfK{ literal 0 HcmV?d00001 diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png.meta b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png.meta new file mode 100644 index 0000000..10c3d03 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackground.png.meta @@ -0,0 +1,59 @@ +fileFormatVersion: 2 +guid: cecdbf60b0c9d47c9a26b99a41425de0 +timeCreated: 1480068956 +licenseType: Store +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 7 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: 1 + aniso: 16 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 32, y: 32, z: 32, w: 32} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + buildTargetSettings: [] + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..429c27915515a0658245e23bf2a2317427619fd5 GIT binary patch literal 1823 zcmbtUeM}Q)7=PKS5;yz+KbNUT$%f+E>j%Z&rInVJLZ<~RIBKYjJNSOf5`G!wd6qgb>Frgfw21CvcJ+26>$YE*AA_#e=*kFALAG7#cL; z^0ngULD?)Rkbz=INF|Mt;But`T7XF9Dny}m3cWeQlKknk1~*5~3duf)Yh3^N!>7H1qZ z=b&7Wz=&m2ZcZ!aBYmENlOCaUu|s9z3kG{J8kS3CLQ2Di7Rx`II-MidEN3NO#QRcW zHqA$qu$5#fH-qzy%ZU(N(WrqTF^*!=D5_w%iYa*%N3nSn4H*nV)l^ayG}nUR4wvve zZxCm(pk^1#VJ@6B8?|DdN$PMAXpAgQ5u-O6^l@^%Tpk~zj#ndUBchV))oPPUsUF4} zDctQOUEDC1c!4#JiWNq}N%NkKB;!~?5+;UnLW8NJj?rV$kIFZMB}R`WZd5GH2LlTY z{#(O`pZF;f#F6pkm5~J?UHsHD{CJ-q@2=ywX1&>{PxJnIx5e*j(t-N6Jq^~`Ae<=O zuy^wIUCVr6?5QcGL>4wz@V_)PAMz)Ad#tE-CMp|i|-6n zJk9^)*2c3pCV+cQA3oduQ;t-mt12^9Mx+Prst^Gk0FwX^0TA>`;(vtylCk+GG1iza2f8p0c|D|VL z?bRin_kR2ifJ2Qvw+BuiY^-eC%PZ%8Yl(&THN%QVRwChN4q~~ zhvRzEm}*01)Be_;S+!2CQt6p)m#|g+C+@BYYdVM)Pp-W`KVXkynVsgVx7yywniWK*JNzoh9j+CKVGdVPwIQv(Cf*qm{{GpDLEk7Ulg49Hr;*mCQ)Mxo>#x- zSzqk2W83~jyHG9C4EyT)$70CW}ky^cFLGR-^ry@bgXQ3~h`Uz(r1w^wqnD{+u8JL^}LV{LW TgHkmM0e{T#NycyDvey0uS|VJv literal 0 HcmV?d00001 diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png.meta b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png.meta new file mode 100644 index 0000000..03f8ee2 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementBackgroundUnlocked.png.meta @@ -0,0 +1,59 @@ +fileFormatVersion: 2 +guid: c379ef0fb8ae144a5b0a0b1d0c02d125 +timeCreated: 1480077007 +licenseType: Store +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 7 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + buildTargetSettings: [] + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c7f05f2fc1214a851d0910472c01618e28a3ac GIT binary patch literal 2738 zcmaJ@4LFop8-B+y!k{*?$Tp24F@G3lhS7|nXidgn$p7WgRyhj<^ zwip{)83F)c?C<9j4BE8CZy5}1o4uGFps|$thD$?)vC>4Qm;+GR!Wa(1pU>RQ3Fa`_ zN%3tQPXK^!?x1!ASpJ0$=n|Rv`J1CNNJ8p|OigS_0DP z{~yZde?&{9!JL2M{hz{;&?FHD6U>na6T~cV<6>0dAGqvK$3pld7Xt=^S=`|N*zlz(u!ytPW%ow=bF7cKWW8)%MS?r`L@tRuY$GeubG8$_jF zsi8cE62vgmMVSR%*GE;F)7F-UtuqVK4VpBa@mEj-RK2iH1_U@df+C8ZVch8;Og;==Cf z;AwQN8Q0mhZQ-D&>z#$UlUgILu2n`cg{Eb3fw9bIeVPsc;%vT05mDNs4XEszN`!mA zQh8`mqjoR+U>kCYS{<&JtLdKB*K?_gD(a3>M=js7Y17_z3)(SNy7n-o_zCfzev};D zTSbG3S}U~9mCbGwYnugUEyfO2bpZFfOcK01jJQKl(iQ#Mq|uA;yxe;29AZk11$vANSwn!z@A=yLMRWmNF=#4(1 zLq|2q@8U)){SE2(xU}`!2io9}!g=^X{isS`=w7)S?N>VgIY5HjoHK_89IlPX!OI)v zCJ|)EOOWrWeX5u3uiuGRlK`?)8N}<*RaHUza}T1?)ASTnJ|vdfpu#9022I)4SYQ1L z2Aw>q+0yn4TVAC8O6MW$RBWLOey*oMfi7X`l)whLsTx@FHdsdA!A_H8t2E8LO?%18 znVx?ktf1Ac62Irk6$x@w3++N{CM0~|ZFl93$5z|n8eDEiOO-2=I6L}e!?3chn|q1FP9<3}!UOPX#!Qna(k=$zi**5ZV%Dd>AW;qDo10R2Zq z`R{+WAg!Mxt^(-6`2nTS`0G(o&Z8aQx2J!Pj!3(|HD&GX>I;|}J#J34)ACKB0YQ;v%q-Au2kk}B27~rq{niluS(`V07jopARNq8d4+f4xZ@L*S$i1Ze zG)>N2|Iar}-%%zy0Oz!;Kmx={chB-st`*^I-R9;>NkI$HoE7H^pTjn-yot9ep2arh zflg^&@X7G_oQzvuaI6XhrkO%tI97FG$0B(_+}D$}8WU8n9}UzUDcz_ z9k%*3E+qTlaw|$h=DbS`i#$5i9QkZ!II8xRa?QkQm)vK@`+D?3wJ)bq3>MzZLgLc& z+Y2OL{Pr8EzvR(Xy~=6>l5S4ez_*izy*f7WZ?CK2lo@XirHB2v;%ahobs%_XXJ}6f zG(Qkb_?g8wV*%&RT3yG2TLIr**34(B)lF9*%dR*)#kIYyjd?M-Yoy4oCwF&@8*p0h z=?R12^{MXC2d^xKIcZzJ)PDCYI<#|P*F()b63(z(x%bzm$s(v4>NPii)|heHX7AgE>OaeFB=Z@t|){)4dwX!p#{foqI=P%}Po z!@|$H)Yy)gUcPG3F-m>kV~?TN0S8@ZXgKqPuLJ^bCzqt64`&**0f!xrb$iDJ zWHn4655>*aWj`s8uQ^qg4XeajL>T`hAF~bgJSmX8L|cdkmOXvsji;pVt6Ui&81g7M z?6I$or+6}h$$XPnYOT`whP)G&aB9-MTwG@NYAn8?`=u6YeGIZuv|~EPgAuumzU z8G%+}GZ^K%!sbWoLdXEyqy{)++Uck|7QONpmn4~!QQ%0O4Qn~et*LPG#YMMD^E3Pg zg9r9!OnOI(9#2kfs4cV`J`~_gNx9dqYw`=>9ml(eaE8@TFd&Stkbnz_z@4Kk6O``(`QtJ^$S03G8v-lV3@4L;X$~#*A E7q`M~2><{9 literal 0 HcmV?d00001 diff --git a/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png.meta b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png.meta new file mode 100644 index 0000000..5d4fe4b --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Achievements/Sprites/AchievementIcon.png.meta @@ -0,0 +1,59 @@ +fileFormatVersion: 2 +guid: 16a86a7f581fc4dfb953e0df8ad3ecac +timeCreated: 1479996616 +licenseType: Store +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + cubemapConvolution: 0 + cubemapConvolutionSteps: 7 + cubemapConvolutionExponent: 1.5 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + rGBM: 0 + compressionQuality: 50 + allowsAlphaSplitting: 0 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + buildTargetSettings: [] + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes.meta b/Assets/CorgiEngine/MMTools/Attributes.meta new file mode 100644 index 0000000..82b43a2 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 35da110902fbc43f79f283baf2a738c6 +folderAsset: yes +timeCreated: 1459528287 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor.meta b/Assets/CorgiEngine/MMTools/Attributes/Editor.meta new file mode 100644 index 0000000..0f2f544 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 385b7857efb05b545bdae2dc7110a011 +folderAsset: yes +timeCreated: 1462451050 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs b/Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs new file mode 100644 index 0000000..29ea5c4 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs @@ -0,0 +1,24 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; +using UnityEditor; + +namespace MoreMountains.Tools +{ + + [CustomPropertyDrawer(typeof(HiddenAttribute))] + + public class HiddenAttributeDrawer : PropertyDrawer + { + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return 0f; + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs.meta new file mode 100644 index 0000000..e81b18d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/HiddenAttributeDrawer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ac53bf83e8327014bbc73944dfec5ff8 +timeCreated: 1456270265 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs b/Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs new file mode 100644 index 0000000..d8be951 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs @@ -0,0 +1,118 @@ +#if UNITY_EDITOR + +using System; +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.Reflection; + +namespace MoreMountains.Tools +{ + [CustomPropertyDrawer (typeof(InformationAttribute))] + /// + /// This class allows the display of a message box (warning, info, error...) next to a property (before or after) + /// + public class InformationAttributeDrawer : PropertyDrawer + { + // determines the space after the help box, the space before the text box, and the width of the help box icon + const int spaceBeforeTheTextBox = 5; + const int spaceAfterTheTextBox = 10; + const int iconWidth = 55; + + InformationAttribute informationAttribute { get { return ((InformationAttribute)attribute); } } + + /// + /// OnGUI, displays the property and the textbox in the specified order + /// + /// Rect. + /// Property. + /// Label. + public override void OnGUI (Rect rect, SerializedProperty prop, GUIContent label) + { + if (HelpEnabled()) + { + EditorStyles.helpBox.richText=true ; + Rect helpPosition = rect; + Rect textFieldPosition = rect; + + if (!informationAttribute.MessageAfterProperty) + { + // we position the message before the property + helpPosition.height = DetermineTextboxHeight(informationAttribute.Message); + + textFieldPosition.y += helpPosition.height + spaceBeforeTheTextBox; + textFieldPosition.height = GetPropertyHeight(prop,label); + } + else + { + // we position the property first, then the message + textFieldPosition.height = GetPropertyHeight(prop,label); + + helpPosition.height = DetermineTextboxHeight(informationAttribute.Message); + // we add the complete property height (property + helpbox, as overridden in this very script), and substract both to get just the property + helpPosition.y += GetPropertyHeight(prop,label) - DetermineTextboxHeight(informationAttribute.Message) - spaceAfterTheTextBox; + } + + EditorGUI.HelpBox (helpPosition, informationAttribute.Message, informationAttribute.Type); + EditorGUI.PropertyField(textFieldPosition, prop, label, true); + } + else + { + Rect textFieldPosition = rect; + textFieldPosition.height = GetPropertyHeight(prop,label); + EditorGUI.PropertyField(textFieldPosition, prop, label, true); + } + } + + /// + /// Returns the complete height of the whole block (property + help text) + /// + /// The block height. + /// Property. + /// Label. + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + if (HelpEnabled()) + { + return EditorGUI.GetPropertyHeight(property) + DetermineTextboxHeight(informationAttribute.Message) + spaceAfterTheTextBox + spaceBeforeTheTextBox; + } + else + { + return EditorGUI.GetPropertyHeight(property); + } + } + + /// + /// Checks the editor prefs to see if help is enabled or not + /// + /// true, if enabled was helped, false otherwise. + protected virtual bool HelpEnabled() + { + bool helpEnabled = false; + if (EditorPrefs.HasKey("MMShowHelpInInspectors")) + { + if (EditorPrefs.GetBool("MMShowHelpInInspectors")) + { + helpEnabled = true; + } + } + return helpEnabled; + } + + /// + /// Determines the height of the textbox. + /// + /// The textbox height. + /// Message. + protected virtual float DetermineTextboxHeight(string message) + { + GUIStyle style = new GUIStyle(EditorStyles.helpBox); + style.richText=true; + + float newHeight = style.CalcHeight(new GUIContent(message),EditorGUIUtility.currentViewWidth - iconWidth); + return newHeight; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs.meta new file mode 100644 index 0000000..739312a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/InformationDrawer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 45b7f6ca773ad4e81ad2ed6d93158953 +timeCreated: 1459517598 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs b/Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs new file mode 100644 index 0000000..0ca08ac --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs @@ -0,0 +1,77 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; +using UnityEditor; + +namespace MoreMountains.Tools +{ + /// + /// This class adds a MoreMountains entry in Unity's top menu, allowing to enable/disable the help texts from the engine's inspectors + /// + public static class MoreMountainsMenuHelp + { + [MenuItem("More Mountains/Enable Help in Inspectors",false,0)] + /// + /// Adds a menu item to enable help + /// + private static void EnableHelpInInspectors() + { + SetHelpEnabled(true); + } + + [MenuItem("More Mountains/Enable Help in Inspectors", true)] + /// + /// Conditional method to determine if the "enable help" entry should be greyed or not + /// + private static bool EnableHelpInInspectorsValidation() + { + return !HelpEnabled(); + } + + [MenuItem("More Mountains/Disable Help in Inspectors",false,1)] + /// + /// Adds a menu item to disable help + /// + private static void DisableHelpInInspectors() + { + SetHelpEnabled(false); + } + + [MenuItem("More Mountains/Disable Help in Inspectors", true)] + /// + /// Conditional method to determine if the "disable help" entry should be greyed or not + /// + private static bool DisableHelpInInspectorsValidation() + { + return HelpEnabled(); + } + + /// + /// Checks editor prefs to see if help is enabled or not + /// + /// true, if enabled was helped, false otherwise. + private static bool HelpEnabled() + { + if (EditorPrefs.HasKey("MMShowHelpInInspectors")) + { + return EditorPrefs.GetBool("MMShowHelpInInspectors"); + } + else + { + EditorPrefs.SetBool("MMShowHelpInInspectors",true); + return true; + } + } + + /// + /// Sets the help enabled editor pref. + /// + /// If set to true status. + private static void SetHelpEnabled(bool status) + { + EditorPrefs.SetBool("MMShowHelpInInspectors",status); + SceneView.RepaintAll(); + + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs.meta new file mode 100644 index 0000000..7b367af --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/MoreMountainsMenuHelp.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 28fbd4d56656345f4ba40f7e4d786fb1 +timeCreated: 1477393124 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs b/Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs new file mode 100644 index 0000000..67406ee --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; +using UnityEditor; + +namespace MoreMountains.Tools +{ + + [CustomPropertyDrawer(typeof(ReadOnlyAttribute))] + + public class ReadOnlyAttributeDrawer : PropertyDrawer + { + // Necessary since some properties tend to collapse smaller than their content + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return EditorGUI.GetPropertyHeight(property, label, true); + } + + // Draw a disabled property field + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + GUI.enabled = false; // Disable fields + EditorGUI.PropertyField(position, property, label, true); + GUI.enabled = true; // Enable fields + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs.meta new file mode 100644 index 0000000..eeb3f3a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/Editor/ReadOnlyAttributeDrawer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8bc90960c2aea754a8ff74069babfa0b +timeCreated: 1456269803 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs b/Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs new file mode 100644 index 0000000..21b444d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs @@ -0,0 +1,12 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace MoreMountains.Tools +{ + public class HiddenAttribute : PropertyAttribute { } +} diff --git a/Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs.meta new file mode 100644 index 0000000..d102a86 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/HiddenAttribute.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 171bddfe1b8d24a049ab93c4b4d2323d +timeCreated: 1456675987 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs b/Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs new file mode 100644 index 0000000..936f029 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs @@ -0,0 +1,37 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace MoreMountains.Tools +{ + public class InformationAttribute : PropertyAttribute + { + public enum InformationType { Error, Info, None, Warning } + + #if UNITY_EDITOR + public string Message; + public MessageType Type; + public bool MessageAfterProperty; + + public InformationAttribute(string message, InformationType type, bool messageAfterProperty) + { + this.Message = message; + if (type==InformationType.Error) { this.Type = UnityEditor.MessageType.Error;} + if (type==InformationType.Info) { this.Type = UnityEditor.MessageType.Info;} + if (type==InformationType.Warning) { this.Type = UnityEditor.MessageType.Warning;} + if (type==InformationType.None) { this.Type = UnityEditor.MessageType.None;} + this.MessageAfterProperty = messageAfterProperty; + } + #else + public InformationAttribute(string message, InformationType type, bool messageAfterProperty) + { + + } + #endif + } +} diff --git a/Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs.meta new file mode 100644 index 0000000..a2e849f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/InformationAttribute.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a083e52b3b6964a0d88af5b74900af5e +timeCreated: 1459528299 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs b/Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs new file mode 100644 index 0000000..5eb136d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs @@ -0,0 +1,55 @@ +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif +using System.Reflection; + +namespace MoreMountains.Tools +{ + [System.AttributeUsage(System.AttributeTargets.Field)] + public class InspectorButtonAttribute : PropertyAttribute + { + public readonly string MethodName; + + public InspectorButtonAttribute(string MethodName) + { + this.MethodName = MethodName; + } + } + + #if UNITY_EDITOR + [CustomPropertyDrawer(typeof(InspectorButtonAttribute))] + public class InspectorButtonPropertyDrawer : PropertyDrawer + { + private MethodInfo _eventMethodInfo = null; + + public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label) + { + InspectorButtonAttribute inspectorButtonAttribute = (InspectorButtonAttribute)attribute; + + float buttonLength = 50 + inspectorButtonAttribute.MethodName.Length * 6; + Rect buttonRect = new Rect(position.x + (position.width - buttonLength) * 0.5f, position.y, buttonLength, position.height); + + if (GUI.Button(buttonRect, inspectorButtonAttribute.MethodName)) + { + System.Type eventOwnerType = prop.serializedObject.targetObject.GetType(); + string eventName = inspectorButtonAttribute.MethodName; + + if (_eventMethodInfo == null) + { + _eventMethodInfo = eventOwnerType.GetMethod(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + } + + if (_eventMethodInfo != null) + { + _eventMethodInfo.Invoke(prop.serializedObject.targetObject, null); + } + else + { + Debug.LogWarning(string.Format("InspectorButton: Unable to find method {0} in {1}", eventName, eventOwnerType)); + } + } + } + } + #endif +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs.meta new file mode 100644 index 0000000..7ab47bc --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/InspectorButtonAttribute.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 325b5ef7494cead4fbd33a7e9c22c44d +timeCreated: 1490625668 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs b/Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs new file mode 100644 index 0000000..afaff5b --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs @@ -0,0 +1,12 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace MoreMountains.Tools +{ + public class ReadOnlyAttribute : PropertyAttribute { } +} diff --git a/Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs.meta b/Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs.meta new file mode 100644 index 0000000..7ec9134 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Attributes/ReadOnlyAttribute.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f54d7ad2fa8f4411e8cceb8d27f6e1ae +timeCreated: 1456675993 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/AutoDestroy.meta b/Assets/CorgiEngine/MMTools/AutoDestroy.meta new file mode 100644 index 0000000..07cdce3 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/AutoDestroy.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 67155e3a3dfa77249ab7fc42886767ff +folderAsset: yes +timeCreated: 1462451360 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs b/Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs new file mode 100644 index 0000000..abe6d94 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs @@ -0,0 +1,34 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// Add this component to an object and it'll be auto destroyed X seconds after its Start() + /// + public class TimedAutoDestroy : MonoBehaviour + { + /// The time (in seconds) before we destroy the object + public float TimeBeforeDestruction=2; + + protected WaitForSeconds _timeBeforeDestructionWFS; + + /// + /// On Start(), we schedule the object's destruction + /// + protected virtual void Start () + { + _timeBeforeDestructionWFS = new WaitForSeconds(TimeBeforeDestruction); + StartCoroutine(Destruction()); + } + + /// + /// Destroys the object after TimeBeforeDestruction seconds + /// + protected virtual IEnumerator Destruction() + { + yield return _timeBeforeDestructionWFS; + Destroy(gameObject); + } + } +} diff --git a/Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs.meta b/Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs.meta new file mode 100644 index 0000000..78f323a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/AutoDestroy/TimedAutoDestroy.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b1f8ba1afe72bf14cba8d9016de8ad13 +timeCreated: 1447014954 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation.meta b/Assets/CorgiEngine/MMTools/Automation.meta new file mode 100644 index 0000000..3576c15 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2c16e5490aee6c6428a7947bd3b899fe +folderAsset: yes +timeCreated: 1462451511 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs b/Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs new file mode 100644 index 0000000..ea6fd31 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs @@ -0,0 +1,116 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + [RequireComponent(typeof(SpriteRenderer))] + /// + /// Add this component to an object to have it pick a new order in layer on start, useful to have unique sorting layer numbers + /// + public class AutoOrderInLayer : MonoBehaviour + { + static int CurrentMaxCharacterOrderInLayer = 0; + + [Header("Global Counter")] + [Information("Add this component to an object with a sprite renderer, and it'll give it a new order in layer based on the settings defined here. First is the global counter increment, or how much you'd like to increment the layer order between two objects on that same layer.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + /// the number by which to increment each new object's order in layer + public int GlobalCounterIncrement = 5; + + [Header("Parent")] + [Information("You can also decide to determine the new layer order based on the parent sprite's order (it'll have to be on the same layer).",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + /// if this is true, the new order in layer value will be based on the highest order value found on a parent with a similar sorting layer + public bool BasedOnParentOrder = false; + /// if BasedOnParentOrder is true, the new value will be the parent's order value + this value + public int ParentIncrement = 1; + + [Header("Children")] + [Information("And here you can decide to apply your new layer order to all children.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + /// if this is true, the new order value will be passed to all children with a similar sorting layer + public bool ApplyNewOrderToChildren = false; + /// the value by which the new order value should be incremented to pass it to children + public int ChildrenIncrement = 0; + + protected SpriteRenderer _spriteRenderer; + + /// + /// On Start, we get our sprite renderer and determine the new order in layer + /// + protected virtual void Start() + { + Initialization(); + AutomateLayerOrder(); + } + + /// + /// Gets the sprite renderer component and stores it + /// + protected virtual void Initialization() + { + _spriteRenderer = GetComponent(); + } + + /// + /// Picks a new order in layer based on the inspector's settings + /// + protected virtual void AutomateLayerOrder() + { + int newOrder = 0; + + // if there's no sprite renderer on this object, we do nothing and exit + if (_spriteRenderer == null) + { + return; + } + + // if we're supposed to base our new order in layer value on the parent's value + if (BasedOnParentOrder) + { + int maxLayerOrder = 0; + Component[] spriteRenderers = GetComponentsInParent( typeof(SpriteRenderer) ); + + // we look for all sprite renderers in parent objects + if( spriteRenderers != null ) + { + foreach( SpriteRenderer spriteRenderer in spriteRenderers ) + { + // if we find a parent with a sprite renderer, on the same sorting layer and with a higher sorting value than previously found + if ( (spriteRenderer.sortingLayerID == _spriteRenderer.sortingLayerID) + && (spriteRenderer.sortingOrder > maxLayerOrder)) + { + // we store the new value + maxLayerOrder = spriteRenderer.sortingOrder; + } + } + // we set our new value to the highest value found, plus our increment + newOrder = maxLayerOrder + ParentIncrement; + } + } + else + { + // if we're not based on parent, we base our pick on the current max order in layer + newOrder = CurrentMaxCharacterOrderInLayer + GlobalCounterIncrement; + // we increment the global order index + CurrentMaxCharacterOrderInLayer += GlobalCounterIncrement; + } + + // we apply our new order value + _spriteRenderer.sortingOrder = newOrder; + + // if we need to apply that new value to all children, we do it + if (ApplyNewOrderToChildren) + { + Component[] childrenSpriteRenderers = GetComponentsInChildren( typeof(SpriteRenderer) ); + if( childrenSpriteRenderers != null ) + { + foreach( SpriteRenderer childSpriteRenderer in childrenSpriteRenderers ) + { + if (childSpriteRenderer.sortingLayerID == _spriteRenderer.sortingLayerID) + { + childSpriteRenderer.sortingOrder = newOrder + ChildrenIncrement; + } + } + } + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs.meta b/Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs.meta new file mode 100644 index 0000000..f4b9ee7 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/AutoOrderInLayer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 79525dad8d23d4919ba5c982d77b09e5 +timeCreated: 1487001737 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs b/Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs new file mode 100644 index 0000000..552207a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs @@ -0,0 +1,24 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// Add this class to a GameObject to make it rotate on itself + /// + public class AutoRotate : MonoBehaviour + { + public Space RotationSpace = Space.Self; + + /// The rotation speed. Positive means clockwise, negative means counter clockwise. + public Vector3 RotationSpeed = new Vector3(100f,0f,0f); + + /// + /// Makes the object rotate on its center every frame. + /// + protected virtual void Update () + { + transform.Rotate(RotationSpeed*Time.deltaTime,RotationSpace); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs.meta b/Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs.meta new file mode 100644 index 0000000..7f0e7b4 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/AutoRotate.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1b6e40036cb64e54b8c49db07be5c79f +timeCreated: 1456589453 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation/PathMovement.meta b/Assets/CorgiEngine/MMTools/Automation/PathMovement.meta new file mode 100644 index 0000000..d561554 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/PathMovement.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a81a0d0af22c3fb45818ed2f5e47b2e1 +folderAsset: yes +timeCreated: 1483901466 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor.meta b/Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor.meta new file mode 100644 index 0000000..a72f9d2 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 80b21ea3114c3804180da81e24eab605 +folderAsset: yes +timeCreated: 1470336489 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor/PathMovementEditor.cs b/Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor/PathMovementEditor.cs new file mode 100644 index 0000000..423a59d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/PathMovement/Editor/PathMovementEditor.cs @@ -0,0 +1,78 @@ +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// This class adds names for each LevelMapPathElement next to it on the scene view, for easier setup + /// + [CustomEditor(typeof(PathMovement),true)] + [InitializeOnLoad] + public class PathMovementEditor : Editor + { + public PathMovement pathMovementTarget + { + get + { + return (PathMovement)target; + } + } + + public override void OnInspectorGUI() + { + serializedObject.Update (); + + if (pathMovementTarget.AccelerationType == PathMovement.PossibleAccelerationType.AnimationCurve) + { + DrawDefaultInspector (); + } + else + { + Editor.DrawPropertiesExcluding (serializedObject, new string [] { "Acceleration" }); + } + + serializedObject.ApplyModifiedProperties (); + } + + /// + /// OnSceneGUI, draws repositionable handles at every point in the path, for easier setup + /// + protected virtual void OnSceneGUI() + { + Handles.color=Color.green; + PathMovement t = (target as PathMovement); + + if (t.GetOriginalTransformPositionStatus() == false) + { + return; + } + + for (int i=0;i + /// This class describes an element on a PathMovement's path description + /// + public class PathMovementElement + { + /// the point that make up the path the object will follow + public Vector3 PathElementPosition; + /// the delay (in seconds) to apply when the object meets each item of the path. Obviously you'll want to have the same number of items in the Delays list as in the Path list. + public float Delay; + } + + [AddComponentMenu("Corgi Engine/Environment/Path Movement")] + /// + /// Add this component to an object and it'll be able to move along a path defined from its inspector. + /// + public class PathMovement : MonoBehaviour + { + /// the possible movement types + public enum PossibleAccelerationType + { + ConstantSpeed, + EaseOut, + AnimationCurve + } + + /// the possible cycle options + public enum CycleOptions + { + BackAndForth, + Loop, + OnlyOnce + } + + /// the possible movement directions + public enum MovementDirection + { + Ascending, + Descending + } + + [Header("Path")] + [Information("Here you can select the 'Cycle Option'. Back and Forth will have your object follow the path until its end, and go back to the original point. If you select Loop, the path will be closed and the object will move along it until told otherwise. If you select Only Once, the object will move along the path from the first to the last point, and remain there forever.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + public CycleOptions CycleOption; + + [Information("Add points to the Path (set the size of the path first), then position the points using either the inspector or by moving the handles directly in scene view. For each path element you can specify a delay (in seconds). The order of the points will be the order the object follows.\nFor looping paths, you can then decide if the object will go through the points in the Path in Ascending (1, 2, 3...) or Descending (Last, Last-1, Last-2...) order.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + /// the initial movement direction : ascending > will go from the points 0 to 1, 2, etc ; descending > will go from the last point to last-1, last-2, etc + public MovementDirection LoopInitialMovementDirection = MovementDirection.Ascending; + /// the points that make up the path the object will follow + public List PathElements; + + [Header("Movement")] + [Information("Set the speed at which the path will be crawled, and if the movement should be constant or eased.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + /// the movement speed + public float MovementSpeed = 1; + /// returns the current speed at which the object is traveling + public Vector3 CurrentSpeed { get; protected set; } + /// the movement type of the object + public PossibleAccelerationType AccelerationType = PossibleAccelerationType.ConstantSpeed; + /// the acceleration to apply to an object traveling between two points of the path. + public AnimationCurve Acceleration = new AnimationCurve(new Keyframe(0,1f),new Keyframe(1f,0f)); + + [Header("Settings")] + [Information("The MinDistanceToGoal is used to check if we've (almost) reached a point in the Path. The 2 other settings here are for debug only, don't change them.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + /// the minimum distance to a point at which we'll arbitrarily decide the point's been reached + public float MinDistanceToGoal = .1f; + /// the original position of the transform, hidden and shouldn't be accessed + protected Vector3 _originalTransformPosition; + /// internal flag, hidden and shouldn't be accessed + protected bool _originalTransformPositionStatus=false; + /// if this is true, the object can move along the path + public virtual bool CanMove + { + get + { + return true; + } + } + + protected bool _active=false; + protected IEnumerator _currentPoint; + protected int _direction = 1; + protected Vector3 _initialPosition; + protected Vector3 _finalPosition; + protected Vector3 _previousPoint = Vector3.zero; + protected float _waiting=0; + protected int _currentIndex; + protected float _distanceToNextPoint; + protected bool _endReached = false; + + /// + /// Initialization + /// + protected virtual void Start () + { + Initialization (); + } + + /// + /// Flag inits, initial movement determination, and object positioning + /// + protected virtual void Initialization() + { + // on Start, we set our active flag to true + _active=true; + _endReached = false; + + // if the path is null we exit + if(PathElements == null || PathElements.Count < 1) + { + return; + } + + // we set our initial direction based on the settings + if (LoopInitialMovementDirection == MovementDirection.Ascending) + { + _direction=1; + } + else + { + _direction=-1; + } + + // we initialize our path enumerator + _currentPoint = GetPathEnumerator(); + _previousPoint = _currentPoint.Current; + _currentPoint.MoveNext(); + + // initial positioning + if (!_originalTransformPositionStatus) + { + _originalTransformPositionStatus = true; + _originalTransformPosition = transform.position; + } + transform.position = _originalTransformPosition + _currentPoint.Current; + } + + /// + /// On update we keep moving along the path + /// + protected virtual void Update () + { + // if the path is null we exit, if we only go once and have reached the end we exit, if we can't move we exit + if(PathElements == null + || PathElements.Count < 1 + || _endReached + || !CanMove + ) + { + return; + } + + Move (); + } + + /// + /// Moves the object and determines when a point has been reached + /// + protected virtual void Move() + { + // we wait until we can proceed + _waiting -= Time.deltaTime; + if (_waiting > 0) + { + CurrentSpeed = Vector3.zero; + return; + } + + // we store our initial position to compute the current speed at the end of the udpate + _initialPosition=transform.position; + + // we move our object + MoveAlongThePath(); + + // we decide if we've reached our next destination or not, if yes, we move our destination to the next point + _distanceToNextPoint = (transform.position - (_originalTransformPosition + _currentPoint.Current)).magnitude; + if(_distanceToNextPoint < MinDistanceToGoal) + { + //we check if we need to wait + if (PathElements.Count > _currentIndex) + { + _waiting = PathElements[_currentIndex].Delay; + } + + _previousPoint = _currentPoint.Current; + _currentPoint.MoveNext(); + } + + // we determine the current speed + _finalPosition = transform.position; + CurrentSpeed = (_finalPosition-_initialPosition) / Time.deltaTime; + + if (_endReached) + { + CurrentSpeed = Vector3.zero; + } + } + + /// + /// Moves the object along the path according to the specified movement type. + /// + public virtual void MoveAlongThePath() + { + switch (AccelerationType) + { + case PossibleAccelerationType.ConstantSpeed: + transform.position = Vector3.MoveTowards (transform.position, _originalTransformPosition + _currentPoint.Current, Time.deltaTime * MovementSpeed); + break; + + case PossibleAccelerationType.EaseOut: + transform.position = Vector3.Lerp (transform.position, _originalTransformPosition + _currentPoint.Current, Time.deltaTime * MovementSpeed); + break; + + case PossibleAccelerationType.AnimationCurve: + float distanceBetweenPoints = Vector3.Distance (_previousPoint, _currentPoint.Current); + + if (distanceBetweenPoints <= 0) + { + return; + } + + float remappedDistance = 1 - MMMaths.Remap (_distanceToNextPoint, 0f, distanceBetweenPoints, 0f, 1f); + float speedFactor = Acceleration.Evaluate (remappedDistance); + + transform.position = Vector3.MoveTowards (transform.position, _originalTransformPosition + _currentPoint.Current, Time.deltaTime * MovementSpeed * speedFactor); + break; + } + } + + /// + /// Returns the current target point in the path + /// + /// The path enumerator. + public virtual IEnumerator GetPathEnumerator() + { + + // if the path is null we exit + if(PathElements == null || PathElements.Count < 1) + { + yield break; + } + + int index = 0; + _currentIndex = index; + while (true) + { + _currentIndex = index; + yield return PathElements[index].PathElementPosition; + + if(PathElements.Count <= 1) + { + continue; + } + + // if the path is looping + if (CycleOption == CycleOptions.Loop) + { + index = index + _direction; + if(index < 0) + { + index = PathElements.Count-1; + } + else if(index > PathElements.Count - 1) + { + index = 0; + } + } + + if (CycleOption == CycleOptions.BackAndForth) + { + if(index <= 0) + { + _direction = 1; + } + else if(index >= PathElements.Count - 1) + { + _direction = -1; + } + index = index + _direction; + } + + if (CycleOption == CycleOptions.OnlyOnce) + { + if(index <= 0) + { + _direction = 1; + } + else if(index >= PathElements.Count - 1) + { + _direction = 0; + CurrentSpeed = Vector3.zero; + _endReached = true; + } + index = index + _direction; + } + } + } + + /// + /// Call this method to force a change in direction at any time + /// + public virtual void ChangeDirection() + { + _direction = - _direction; + _currentPoint.MoveNext(); + } + + /// + /// On DrawGizmos, we draw lines to show the path the object will follow + /// + protected virtual void OnDrawGizmos() + { + #if UNITY_EDITOR + if (PathElements==null) + { + return; + } + + if (PathElements.Count==0) + { + return; + } + + // if we haven't stored the object's original position yet, we do it + if (_originalTransformPositionStatus==false) + { + _originalTransformPosition=transform.position; + _originalTransformPositionStatus=true; + } + // if we're not in runtime mode and the transform has changed, we update our position + if (transform.hasChanged && _active==false) + { + _originalTransformPosition=transform.position; + } + // for each point in the path + for (int i=0;i + /// Updates the original transform position. + /// + /// New original transform position. + public virtual void UpdateOriginalTransformPosition(Vector3 newOriginalTransformPosition) + { + _originalTransformPosition = newOriginalTransformPosition; + } + + /// + /// Gets the original transform position. + /// + /// The original transform position. + public virtual Vector3 GetOriginalTransformPosition() + { + return _originalTransformPosition; + } + + /// + /// Sets the original transform position status. + /// + /// If set to true status. + public virtual void SetOriginalTransformPositionStatus(bool status) + { + _originalTransformPositionStatus = status; + } + + /// + /// Gets the original transform position status. + /// + /// true, if original transform position status was gotten, false otherwise. + public virtual bool GetOriginalTransformPositionStatus() + { + return _originalTransformPositionStatus ; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Automation/PathMovement/PathMovement.cs.meta b/Assets/CorgiEngine/MMTools/Automation/PathMovement/PathMovement.cs.meta new file mode 100644 index 0000000..b91a833 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/PathMovement/PathMovement.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 63c6481b3d023ad4ca3f7cc767733efb +timeCreated: 1459779932 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 100 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Automation/Wiggle.cs b/Assets/CorgiEngine/MMTools/Automation/Wiggle.cs new file mode 100644 index 0000000..db09a60 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/Wiggle.cs @@ -0,0 +1,188 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// Add this class to a GameObject to be able to control its position/rotation/scale individually and periodically, allowing it to "wiggle" (or just move however you want on a periodic basis) + /// + public class Wiggle : MonoBehaviour + { + [Header("Position Frequency")] + /// the minimum time (in seconds) between two position changes + public float PositionFrequencyMin = 0; + /// the maximum time (in seconds) between two position changes + public float PositionFrequencyMax = 1; + [Header("Position Amplitude")] + /// the minimum position the object can have + public Vector3 PositionAmplitudeMin = Vector3.zero; + /// the maximum position the object can have + public Vector3 PositionAmplitudeMax = Vector3.one; + + [Header("Rotation Frequency")] + /// the minimum time (in seconds) between two rotation changes + public float RotationFrequencyMin = 0; + /// the maximum time (in seconds) between two rotation changes + public float RotationFrequencyMax = 1; + [Header("Rotation Amplitude")] + /// the object's minimum rotation + public Vector3 RotationAmplitudeMin = Vector3.zero; + /// the object's maximum rotation + public Vector3 RotationAmplitudeMax = Vector3.one; + + [Header("Scale Frequency")] + /// the minimum time (in seconds) between two scale changes + public float ScaleFrequencyMin = 0; + /// the maximum time (in seconds) between two scale changes + public float ScaleFrequencyMax = 1; + [Header("Scale Amplitude")] + /// the object's minimum scale + public Vector3 ScaleAmplitudeMin = Vector3.zero; + /// the object's maximum scale + public Vector3 ScaleAmplitudeMax = Vector3.one; + + protected Vector3 _startPosition; + protected Quaternion _startRotation; + protected Vector3 _startScale; + + protected float _randomPositionFrequency = 0; + protected Vector3 _randomPositionAmplitude = Vector3.zero; + + protected float _randomRotationFrequency = 0; + protected Vector3 _randomRotationAmplitude = Vector3.zero; + + protected float _randomScaleFrequency = 0; + protected Vector3 _randomScaleAmplitude = Vector3.zero; + + protected float _positionTimer = 0; + protected float _rotationTimer = 0; + protected float _scaleTimer = 0; + + protected float _positionT = 0; + protected float _rotationT = 0; + protected float _scaleT = 0; + + protected Vector3 _newPosition = Vector3.zero; + protected Quaternion _newRotation = Quaternion.identity; + protected Vector3 _newScale = Vector3.zero; + + /// + /// On Start() we trigger the initialization + /// + protected virtual void Start() + { + Initialization (); + } + + /// + /// On init we get the start values and trigger our coroutines for each property + /// + protected virtual void Initialization() + { + _startPosition = transform.localPosition; + _startRotation = transform.localRotation; + _startScale = transform.localScale; + StartCoroutine(ComputePosition ()); + StartCoroutine(ComputeRotation ()); + StartCoroutine(ComputeScale ()); + } + + /// + /// Every frame we update our object's position + /// + protected virtual void Update() + { + UpdatePosition (); + } + + /// + /// Lerps each property towards the new computed target position/rotation/scale + /// + protected virtual void UpdatePosition() + { + if (_randomPositionFrequency > 0) + { + _positionT = (Time.time - _positionTimer) / _randomPositionFrequency; + transform.localPosition = Vector3.Lerp (transform.localPosition, _newPosition, _positionT); + } + + if (_randomRotationFrequency > 0) + { + _rotationT = (Time.time - _rotationTimer) / _randomRotationFrequency; + transform.localRotation = Quaternion.Lerp (transform.localRotation, _newRotation, _rotationT); + } + + if (_randomScaleFrequency > 0) + { + _scaleT = (Time.time - _scaleTimer) / _randomScaleFrequency; + transform.localScale = Vector3.Lerp (transform.localScale, _newScale, _scaleT); + } + } + + /// + /// Computes a new target position + /// + /// The position. + protected virtual IEnumerator ComputePosition() + { + while (true) + { + // if it's time to decide of a new position + _randomPositionFrequency = UnityEngine.Random.Range(PositionFrequencyMin,PositionFrequencyMax); + + _randomPositionAmplitude.x = UnityEngine.Random.Range(PositionAmplitudeMin.x,PositionAmplitudeMax.x); + _randomPositionAmplitude.y = UnityEngine.Random.Range(PositionAmplitudeMin.y,PositionAmplitudeMax.y); + _randomPositionAmplitude.z = UnityEngine.Random.Range(PositionAmplitudeMin.z,PositionAmplitudeMax.z); + + _newPosition = _startPosition + _randomPositionAmplitude; + _positionTimer = Time.time; + + yield return new WaitForSeconds (_randomPositionFrequency); + } + } + + /// + /// Computes a new target rotation + /// + /// The rotation. + protected virtual IEnumerator ComputeRotation() + { + while (true) + { + // if it's time to decide of a new position + _randomRotationFrequency = UnityEngine.Random.Range(RotationFrequencyMin,RotationFrequencyMax); + + _randomRotationAmplitude.x = UnityEngine.Random.Range(RotationAmplitudeMin.x,RotationAmplitudeMax.x); + _randomRotationAmplitude.y = UnityEngine.Random.Range(RotationAmplitudeMin.y,RotationAmplitudeMax.y); + _randomRotationAmplitude.z = UnityEngine.Random.Range(RotationAmplitudeMin.z,RotationAmplitudeMax.z); + + _newRotation = Quaternion.Euler(_randomRotationAmplitude); + _rotationTimer = Time.time; + + yield return new WaitForSeconds (_randomRotationFrequency); + } + } + + /// + /// Computes a new target scale + /// + /// The scale. + protected virtual IEnumerator ComputeScale() + { + while (true) + { + // if it's time to decide of a new position + _randomScaleFrequency = UnityEngine.Random.Range(ScaleFrequencyMin,ScaleFrequencyMax); + + _randomScaleAmplitude.x = UnityEngine.Random.Range(ScaleAmplitudeMin.x,ScaleAmplitudeMax.x); + _randomScaleAmplitude.y = UnityEngine.Random.Range(ScaleAmplitudeMin.y,ScaleAmplitudeMax.y); + _randomScaleAmplitude.z = UnityEngine.Random.Range(ScaleAmplitudeMin.z,ScaleAmplitudeMax.z); + + _newScale = _startScale + _randomScaleAmplitude; + _scaleTimer = Time.time; + + yield return new WaitForSeconds (_randomScaleFrequency); + } + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Automation/Wiggle.cs.meta b/Assets/CorgiEngine/MMTools/Automation/Wiggle.cs.meta new file mode 100644 index 0000000..27f2429 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Automation/Wiggle.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bbb0d1ac33358e949848f609dd44e013 +timeCreated: 1456589453 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Camera.meta b/Assets/CorgiEngine/MMTools/Camera.meta new file mode 100644 index 0000000..f55f2d7 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Camera.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d770dabf77e4709498dc142364e5a6bd +folderAsset: yes +timeCreated: 1494780578 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs b/Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs new file mode 100644 index 0000000..1d06287 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + [RequireComponent(typeof(Camera))] + /// + /// Forces an aspect ratio on a camera + /// + public class CameraAspectRatio : MonoBehaviour + { + /// the desired aspect ratio + public Vector2 AspectRatio = Vector2.zero; + + protected Camera _camera; + + /// + /// On Start, we run the Initialization method + /// + protected virtual void Start () + { + Initialization (); + } + + /// + /// Checks the presence of a camera component, and applies the aspect ratio to it + /// + protected virtual void Initialization() + { + if (AspectRatio == Vector2.zero) + { + return; + } + + _camera = this.gameObject.GetComponent (); + if (_camera == null) + { + return; + } + + float newAspectRatio = AspectRatio.x / AspectRatio.y; + float currentWindowAspectRatio = (float)Screen.width / (float)Screen.height; + float newScaleHeight = currentWindowAspectRatio / newAspectRatio; + + if (newScaleHeight >= 1.0f) + { + float scalewidth = 1.0f / newScaleHeight; + Rect rectangle = _camera.rect; + rectangle.width = scalewidth; + rectangle.height = 1.0f; + rectangle.x = (1.0f - scalewidth) / 2.0f; + rectangle.y = 0; + _camera.rect = rectangle; + } + else + { + Rect rectangle = _camera.rect; + rectangle.width = 1.0f; + rectangle.height = newScaleHeight; + rectangle.x = 0; + rectangle.y = (1.0f - newScaleHeight) / 2.0f; + _camera.rect = rectangle; + } + } + + } +} diff --git a/Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs.meta b/Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs.meta new file mode 100644 index 0000000..3a0b788 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Camera/CameraAspectRatio.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f9fe44c149324334e8fccd24ec886401 +timeCreated: 1489250832 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Editor.meta b/Assets/CorgiEngine/MMTools/Editor.meta new file mode 100644 index 0000000..064cc63 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6eec8f8bd0db0434ca08675d8254b587 +folderAsset: yes +timeCreated: 1456229380 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs b/Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs new file mode 100644 index 0000000..b6a8a74 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs @@ -0,0 +1,70 @@ +// Original FindMissingScriptsRecursively script by SimTex and Clement +// http://wiki.unity3d.com/index.php?title=FindMissingScripts + +#if UNITY_EDITOR + +using UnityEngine; +using UnityEditor; + +namespace MoreMountains.Tools +{ + public class FindMissingScriptsRecursively : EditorWindow + { + static int go_count = 0, components_count = 0, missing_count = 0; + + [MenuItem("Window/FindMissingScriptsRecursively")] + public static void ShowWindow() + { + EditorWindow.GetWindow(typeof(FindMissingScriptsRecursively)); + } + + public void OnGUI() + { + if (GUILayout.Button("Find Missing Scripts in selected GameObjects")) + { + FindInSelected(); + } + } + private static void FindInSelected() + { + GameObject[] go = Selection.gameObjects; + go_count = 0; + components_count = 0; + missing_count = 0; + foreach (GameObject g in go) + { + FindInGO(g); + } + Debug.Log(string.Format("Searched {0} GameObjects, {1} components, found {2} missing", go_count, components_count, missing_count)); + } + + private static void FindInGO(GameObject g) + { + go_count++; + Component[] components = g.GetComponents(); + for (int i = 0; i < components.Length; i++) + { + components_count++; + if (components[i] == null) + { + missing_count++; + string s = g.name; + Transform t = g.transform; + while (t.parent != null) + { + s = t.parent.name +"/"+s; + t = t.parent; + } + Debug.Log (s + " has an empty script attached in position: " + i, g); + } + } + // Now recurse through each child GO (if there are any): + foreach (Transform childT in g.transform) + { + //Debug.Log("Searching " + childT.name + " " ); + FindInGO(childT.gameObject); + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs.meta b/Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs.meta new file mode 100644 index 0000000..b256d3f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Editor/FindMissingScriptsRecursively.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fd1c99feb0f8e2740939eb3284da4be4 +timeCreated: 1459841264 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Events.meta b/Assets/CorgiEngine/MMTools/Events.meta new file mode 100644 index 0000000..657f843 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Events.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: afab14809bccbc747836030b2b0c6da9 +folderAsset: yes +timeCreated: 1462618796 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Events/MMEventManager.cs b/Assets/CorgiEngine/MMTools/Events/MMEventManager.cs new file mode 100644 index 0000000..16293fa --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Events/MMEventManager.cs @@ -0,0 +1,215 @@ +#define EVENTROUTER_THROWEXCEPTIONS +#if EVENTROUTER_THROWEXCEPTIONS +//#define EVENTROUTER_REQUIRELISTENER // Uncomment this if you want listeners to be required for sending events. +#endif + +using System; +using UnityEngine; +using UnityEngine.Events; +using System.Collections; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + /// + /// MMGameEvents are used throughout the game for general game events (game started, game ended, life lost, etc.) + /// + public struct MMGameEvent + { + public string EventName; + public MMGameEvent(string newName) + { + EventName = newName; + } + } + + public struct MMSfxEvent + { + public AudioClip ClipToPlay; + public MMSfxEvent(AudioClip clipToPlay) + { + ClipToPlay = clipToPlay; + } + } + + /// + /// This class handles event management, and can be used to broadcast events throughout the game, to tell one class (or many) that something's happened. + /// Events are structs, you can define any kind of events you want. This manager comes with MMGameEvents, which are + /// basically just made of a string, but you can work with more complex ones if you want. + /// + /// To trigger a new event, from anywhere, just call MMEventManager.TriggerEvent(YOUR_EVENT); + /// For example : MMEventManager.TriggerEvent(new MMGameEvent("GameStart")); will broadcast an MMGameEvent named GameStart to all listeners. + /// + /// To start listening to an event from any class, there are 3 things you must do : + /// + /// 1 - tell that your class implements the MMEventListener interface for that kind of event. + /// For example: public class GUIManager : Singleton, MMEventListener + /// You can have more than one of these (one per event type). + /// + /// 2 - On Enable and Disable, respectively start and stop listening to the event : + /// void OnEnable() + /// { + /// this.MMEventStartListening(); + /// } + /// void OnDisable() + /// { + /// this.MMEventStopListening(); + /// } + /// + /// 3 - Implement the MMEventListener interface for that event. For example : + /// public void OnMMEvent(MMGameEvent gameEvent) + /// { + /// if (gameEvent.eventName == "GameOver") + /// { + /// // DO SOMETHING + /// } + /// } + /// will catch all events of type MMGameEvent emitted from anywhere in the game, and do something if it's named GameOver + /// + public static class MMEventManager + { + private static Dictionary> _subscribersList; + + static MMEventManager() + { + _subscribersList = new Dictionary>(); + } + + /// + /// Adds a new subscriber to a certain event. + /// + /// listener. + /// The event type. + public static void AddListener( MMEventListener listener ) where MMEvent : struct + { + Type eventType = typeof( MMEvent ); + + if( !_subscribersList.ContainsKey( eventType ) ) + _subscribersList[eventType] = new List(); + + if( !SubscriptionExists( eventType, listener ) ) + _subscribersList[eventType].Add( listener ); + } + + /// + /// Removes a subscriber from a certain event. + /// + /// listener. + /// The event type. + public static void RemoveListener( MMEventListener listener ) where MMEvent : struct + { + Type eventType = typeof( MMEvent ); + + if( !_subscribersList.ContainsKey( eventType ) ) + { + #if EVENTROUTER_THROWEXCEPTIONS + throw new ArgumentException( string.Format( "Removing listener \"{0}\", but the event type \"{1}\" isn't registered.", listener, eventType.ToString() ) ); + #else + return; + #endif + } + + List subscriberList = _subscribersList[eventType]; + bool listenerFound = false; + + foreach(MMEventListenerBase subscriber in subscriberList ) + { + if( subscriber == listener ) + { + subscriberList.Remove( subscriber ); + listenerFound = true; + + if( subscriberList.Count == 0 ) + _subscribersList.Remove( eventType ); + + return; + } + } + + #if EVENTROUTER_THROWEXCEPTIONS + if( !listenerFound ) + { + throw new ArgumentException( string.Format( "Removing listener, but the supplied receiver isn't subscribed to event type \"{0}\".", eventType.ToString() ) ); + } + #endif + } + + /// + /// Triggers an event. All instances that are subscribed to it will receive it (and will potentially act on it). + /// + /// The event to trigger. + /// The 1st type parameter. + public static void TriggerEvent( MMEvent newEvent ) where MMEvent : struct + { + List list; + if( !_subscribersList.TryGetValue( typeof( MMEvent ), out list ) ) + #if EVENTROUTER_REQUIRELISTENER + throw new ArgumentException( string.Format( "Attempting to send event of type \"{0}\", but no listener for this type has been found. Make sure this.Subscribe<{0}>(EventRouter) has been called, or that all listeners to this event haven't been unsubscribed.", typeof( MMEvent ).ToString() ) ); + #else + return; + #endif + + foreach( MMEventListenerBase b in list ) + { + ( b as MMEventListener ).OnMMEvent( newEvent ); + } + } + + /// + /// Checks if there are subscribers for a certain type of events + /// + /// true, if exists was subscriptioned, false otherwise. + /// Type. + /// Receiver. + private static bool SubscriptionExists( Type type, MMEventListenerBase receiver ) + { + List receivers; + + if( !_subscribersList.TryGetValue( type, out receivers ) ) return false; + + bool exists = false; + + foreach( MMEventListenerBase subscription in receivers ) + { + if( subscription == receiver ) + { + exists = true; + break; + } + } + + return exists; + } + } + + /// + /// Static class that allows any class to start or stop listening to events + /// + public static class EventRegister + { + public delegate void Delegate( T eventType ); + + public static void MMEventStartListening( this MMEventListener caller ) where EventType : struct + { + MMEventManager.AddListener( caller ); + } + + public static void MMEventStopListening( this MMEventListener caller ) where EventType : struct + { + MMEventManager.RemoveListener( caller ); + } + } + + /// + /// Event listener basic interface + /// + public interface MMEventListenerBase { }; + + /// + /// A public interface you'll need to implement for each type of event you want to listen to. + /// + public interface MMEventListener : MMEventListenerBase + { + void OnMMEvent( T eventType ); + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Events/MMEventManager.cs.meta b/Assets/CorgiEngine/MMTools/Events/MMEventManager.cs.meta new file mode 100644 index 0000000..c1a6e12 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Events/MMEventManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7e05480e454f546b4b1780169ecf2d6c +timeCreated: 1479905885 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Extension.meta b/Assets/CorgiEngine/MMTools/Extension.meta new file mode 100644 index 0000000..998d25e --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Extension.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: be3ca94e270583d4496a1b77a159a52f +folderAsset: yes +timeCreated: 1462451395 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs b/Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs new file mode 100644 index 0000000..e2168a7 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs @@ -0,0 +1,62 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + /// + /// Contains all extension methods of the Corgi Engine and Infinite Runner Engine. + /// + public static class ExtensionMethods + { + /// + /// Determines if an animator contains a certain parameter, based on a type and a name + /// + /// true if has parameter of type the specified self name type; otherwise, false. + /// Self. + /// Name. + /// Type. + public static bool HasParameterOfType (this Animator self, string name, AnimatorControllerParameterType type) + { + if (name == null || name == "") { return false; } + AnimatorControllerParameter[] parameters = self.parameters; + foreach (AnimatorControllerParameter currParam in parameters) + { + if (currParam.type == type && currParam.name == name) + { + return true; + } + } + return false; + } + + + public static bool Contains(this LayerMask mask, int layer) + { + return ((mask.value & (1 << layer)) > 0); + } + + public static bool Contains(this LayerMask mask, GameObject gameobject) + { + return ((mask.value & (1 << gameobject.layer)) > 0); + } + + static List m_ComponentCache = new List(); + + public static Component GetComponentNoAlloc(this GameObject @this, System.Type componentType) + { + @this.GetComponents(componentType, m_ComponentCache); + var component = m_ComponentCache.Count > 0 ? m_ComponentCache[0] : null; + m_ComponentCache.Clear(); + return component; + } + + public static T GetComponentNoAlloc(this GameObject @this) where T : Component + { + @this.GetComponents(typeof(T), m_ComponentCache); + var component = m_ComponentCache.Count > 0 ? m_ComponentCache[0] : null; + m_ComponentCache.Clear(); + return component as T; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs.meta b/Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs.meta new file mode 100644 index 0000000..78f9d15 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Extension/ExtensionMethods.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4ab2df1c36332884ea07b7b9d754f63a +timeCreated: 1427649564 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/FPSCounter.meta b/Assets/CorgiEngine/MMTools/FPSCounter.meta new file mode 100644 index 0000000..de3d1da --- /dev/null +++ b/Assets/CorgiEngine/MMTools/FPSCounter.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 230685454d7bf4c48a1eb09e8eee2cf4 +folderAsset: yes +timeCreated: 1462451383 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs b/Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs new file mode 100644 index 0000000..26b9bca --- /dev/null +++ b/Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; + +[RequireComponent(typeof(Text))] +/// +/// Add this class to a gameObject with a Text component and it'll feed it the number of FPS in real time. +/// +public class FPSCounter : MonoBehaviour +{ + /// + /// The frequency at which the FPS counter should update (in seconds) + /// + public float UpdateInterval = 0.5f; + + protected float _framesAccumulated = 0f; + protected float _framesDrawnInTheInterval = 0f; + protected float _timeLeft ; + protected Text _text; + + /// + /// On Start(), we get the Text component and initialize our counter + /// + protected virtual void Start() + { + if(GetComponent()==null) + { + Debug.LogWarning ("FPSCounter requires a GUIText component."); + return; + } + _text = GetComponent(); + _timeLeft = UpdateInterval; + } + + /// + /// On Update, we increment our various counters, and if we've reached our UpdateInterval, we update our FPS counter + /// with the number of frames displayed since the last counter update + /// + protected virtual void Update() + { + _framesDrawnInTheInterval++; + _framesAccumulated = _framesAccumulated + Time.timeScale/Time.deltaTime; + _timeLeft = _timeLeft - Time.deltaTime; + + if( _timeLeft <= 0.0 ) + { + _text.text = string.Concat("FPS: ",(_framesAccumulated/_framesDrawnInTheInterval).ToString("f2")); + _framesDrawnInTheInterval = 0; + _framesAccumulated = 0f; + _timeLeft = UpdateInterval; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs.meta b/Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs.meta new file mode 100644 index 0000000..2d23018 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/FPSCounter/FPSCounter.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 84135d2f094e44109a56a3bcd75e1290 +timeCreated: 1458649176 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/GUI.meta b/Assets/CorgiEngine/MMTools/GUI.meta new file mode 100644 index 0000000..938bd8a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/GUI.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3c354874824679f4b9f976c15e1aeee8 +folderAsset: yes +timeCreated: 1469890422 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs b/Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs new file mode 100644 index 0000000..4cc1788 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs @@ -0,0 +1,17 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using MoreMountains.Tools; +using UnityEngine.EventSystems; + +/// +/// Add this bar to an object and link it to a bar (possibly the same object the script is on), and you'll be able to resize the bar object based on a current value, located between a min and max value. +/// See the HealthBar.cs script for a use case +/// +public class GetFocusOnEnable : MonoBehaviour +{ + protected virtual void OnEnable() + { + EventSystem.current.SetSelectedGameObject(this.gameObject, null); + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs.meta b/Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs.meta new file mode 100644 index 0000000..e31190c --- /dev/null +++ b/Assets/CorgiEngine/MMTools/GUI/GetFocusOnEnable.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: dab7e4f64d3ec6c4bb624e6e556a87da +timeCreated: 1479762201 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs b/Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs new file mode 100644 index 0000000..4aec323 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs @@ -0,0 +1,28 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using MoreMountains.Tools; + +/// +/// Add this bar to an object and link it to a bar (possibly the same object the script is on), and you'll be able to resize the bar object based on a current value, located between a min and max value. +/// See the HealthBar.cs script for a use case +/// +public class ProgressBar : MonoBehaviour +{ + /// the healthbar's foreground bar + public Transform ForegroundBar; + public string PlayerID; + + protected Vector3 _newLocalScale = Vector3.one; + protected float _newPercent; + + public virtual void UpdateBar(float currentValue,float minValue,float maxValue) + { + _newPercent = MMMaths.Remap(currentValue,minValue,maxValue,0,1); + _newLocalScale.x = _newPercent; + if (ForegroundBar != null) + { + ForegroundBar.localScale = _newLocalScale; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs.meta b/Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs.meta new file mode 100644 index 0000000..f28a51d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/GUI/ProgressBar.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6de69b425c06ce749805fedd121b1356 +timeCreated: 1469890434 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Icons.meta b/Assets/CorgiEngine/MMTools/Icons.meta new file mode 100644 index 0000000..a2d0b00 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Icons.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 2acfa1852b7603f41b99690e35771ec5 +folderAsset: yes +timeCreated: 1462451476 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Icons/Editor.meta b/Assets/CorgiEngine/MMTools/Icons/Editor.meta new file mode 100644 index 0000000..1a3d19d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Icons/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 74c1ba8631a00c84d9dcf1e6d47b6884 +folderAsset: yes +timeCreated: 1462451499 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs b/Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs new file mode 100644 index 0000000..2976c55 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using UnityEditor; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// This class adds names for each LevelMapPathElement next to it on the scene view, for easier setup + /// + [CustomEditor(typeof(SceneViewIcon))] + [InitializeOnLoad] + public class SceneViewIconEditor : Editor + { + //protected SceneViewIcon _sceneViewIcon; + + [DrawGizmo(GizmoType.InSelectionHierarchy | GizmoType.NotInSelectionHierarchy)] + static void DrawGameObjectName(SceneViewIcon sceneViewIcon, GizmoType gizmoType) + { + GUIStyle style = new GUIStyle(); + style.normal.textColor = Color.blue; + Handles.Label(sceneViewIcon.transform.position, sceneViewIcon.gameObject.name,style); + } + + + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs.meta b/Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs.meta new file mode 100644 index 0000000..5d7cb06 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Icons/Editor/ManagerIconEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 22acaeb1b9f9e4fbbb6486133e649e67 +timeCreated: 1456229446 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs b/Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs new file mode 100644 index 0000000..0ab461a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs @@ -0,0 +1,16 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Add this class to a gameobject and it'll display its name on the scene view, selected or not. + /// + + public class SceneViewIcon : MonoBehaviour + { + + } +} diff --git a/Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs.meta b/Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs.meta new file mode 100644 index 0000000..c1376d6 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Icons/SceneViewIcon.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ab5051530564f46bf871d9268a5d004c +timeCreated: 1456229669 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls.meta b/Assets/CorgiEngine/MMTools/MMControls.meta new file mode 100644 index 0000000..beb2f4a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 92a846d385f0b4102b3984953a3501e2 +folderAsset: yes +timeCreated: 1462366972 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs new file mode 100644 index 0000000..704773b --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs @@ -0,0 +1,53 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; + +namespace MoreMountains.Tools +{ + /// + /// This persistent singleton handles the inputs and sends commands to the player + /// + public class MMControlsTestInputManager : MonoBehaviour + { + // on start, we force a high target frame rate for a more fluid experience on mobile devices + protected virtual void Start() + { + Application.targetFrameRate = 300; + } + + public virtual void LeftJoystickMovement(Vector2 movement) { MMDebug.DebugOnScreen("left joystick",movement); } + public virtual void RightJoystickMovement(Vector2 movement) { MMDebug.DebugOnScreen("right joystick",movement); } + + public virtual void APressed() { MMDebug.DebugOnScreen("Button A Pressed"); } + public virtual void BPressed() { MMDebug.DebugOnScreen("Button B Pressed"); } + public virtual void XPressed() { MMDebug.DebugOnScreen("Button X Pressed"); } + public virtual void YPressed() { MMDebug.DebugOnScreen("Button Y Pressed"); } + public virtual void RTPressed() { MMDebug.DebugOnScreen("Button RT Pressed"); } + + public virtual void APressedFirstTime() { MMDebug.DebugLogTime("Button A Pressed for the first time"); } + public virtual void BPressedFirstTime() { MMDebug.DebugLogTime("Button B Pressed for the first time"); } + public virtual void XPressedFirstTime() { MMDebug.DebugLogTime("Button X Pressed for the first time"); } + public virtual void YPressedFirstTime() { MMDebug.DebugLogTime("Button Y Pressed for the first time"); } + public virtual void RTPressedFirstTime() { MMDebug.DebugLogTime("Button RT Pressed for the first time"); } + + public virtual void AReleased() { MMDebug.DebugLogTime("Button A Released"); } + public virtual void BReleased() { MMDebug.DebugLogTime("Button B Released"); } + public virtual void XReleased() { MMDebug.DebugLogTime("Button X Released"); } + public virtual void YReleased() { MMDebug.DebugLogTime("Button Y Released"); } + public virtual void RTReleased() { MMDebug.DebugLogTime("Button RT Released"); } + + public virtual void HorizontalAxisPressed(float value) { MMDebug.DebugOnScreen("horizontal movement",value); } + public virtual void VerticalAxisPressed(float value) { MMDebug.DebugOnScreen("vertical movement",value); } + + public virtual void LeftPressedFirstTime() { MMDebug.DebugLogTime("Button Left Pressed for the first time"); } + public virtual void UpPressedFirstTime() { MMDebug.DebugLogTime("Button Up Pressed for the first time"); } + public virtual void DownPressedFirstTime() { MMDebug.DebugLogTime("Button Down Pressed for the first time"); } + public virtual void RightPressedFirstTime() { MMDebug.DebugLogTime("Button Right Pressed for the first time"); } + + public virtual void LeftReleased() { MMDebug.DebugLogTime("Button Left Released"); } + public virtual void UpReleased() { MMDebug.DebugLogTime("Button Up Released"); } + public virtual void DownReleased() { MMDebug.DebugLogTime("Button Down Released"); } + public virtual void RightReleased() { MMDebug.DebugLogTime("Button Right Released"); } + + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs.meta new file mode 100644 index 0000000..d5d4a04 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestInputManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9640016db57d1c646a90fa18111eebf0 +timeCreated: 1462436657 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity new file mode 100644 index 0000000..b20f1da --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity @@ -0,0 +1,2383 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 8 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 8 + m_Resolution: 2 + m_BakeResolution: 40 + m_TextureWidth: 1024 + m_TextureHeight: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 0 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 1024 + m_ReflectionCompression: 2 + m_MixedBakeMode: 1 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFiltering: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousColorSigma: 1 + m_PVRFilteringAtrousNormalSigma: 1 + m_PVRFilteringAtrousPositionSigma: 1 + m_LightingDataAsset: {fileID: 0} + m_ShadowMaskMode: 2 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &25665773 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 25665774} + - component: {fileID: 25665778} + - component: {fileID: 25665777} + - component: {fileID: 25665776} + - component: {fileID: 25665775} + m_Layer: 0 + m_Name: ArrowLeft + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &25665774 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 25665773} + m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.24999996, y: 0.24999996, z: 0.25} + m_Children: [] + m_Father: {fileID: 518415681} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -178.5, y: 120.4} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &25665775 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 25665773} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc509a62d8e6cfc45a8431d8d586b9f6, type: 3} + m_Name: + m_EditorClassIdentifier: + AxisPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: LeftPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: LeftReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: HorizontalAxisPressed + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: -1 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: MoreMountains.Tools.AxisEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.5 + AxisValue: -1 + MouseMode: 0 +--- !u!114 &25665776 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 25665773} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300014, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &25665777 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 25665773} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &25665778 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 25665773} +--- !u!1 &216932621 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 216932622} + - component: {fileID: 216932627} + - component: {fileID: 216932626} + - component: {fileID: 216932625} + - component: {fileID: 216932624} + m_Layer: 0 + m_Name: ButtonB + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &216932622 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 216932621} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 986164730} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -99.0002, y: 133} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &216932624 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 216932621} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 87a286771fa86194e979f187a1691a2a, type: 3} + m_Name: + m_EditorClassIdentifier: + ButtonPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: BPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: BReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: BPressed + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.3 + MouseMode: 0 +--- !u!114 &216932625 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 216932621} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300012, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &216932626 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 216932621} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &216932627 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 216932621} +--- !u!1 &245136920 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 245136921} + - component: {fileID: 245136924} + - component: {fileID: 245136923} + - component: {fileID: 245136922} + m_Layer: 0 + m_Name: Joystick2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &245136921 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 245136920} + 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_Children: + - {fileID: 1075232283} + m_Father: {fileID: 779537215} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 1865.6, y: 1314.8} + m_SizeDelta: {x: 512, y: 512} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &245136922 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 245136920} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300010, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &245136923 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 245136920} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &245136924 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 245136920} +--- !u!1 &431597985 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 431597986} + - component: {fileID: 431597990} + - component: {fileID: 431597989} + - component: {fileID: 431597988} + - component: {fileID: 431597987} + m_Layer: 0 + m_Name: ButtonA + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &431597986 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 431597985} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 986164730} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -158.9999, y: 73} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &431597987 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 431597985} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 87a286771fa86194e979f187a1691a2a, type: 3} + m_Name: + m_EditorClassIdentifier: + ButtonPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: APressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: AReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: APressed + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.3 + MouseMode: 0 +--- !u!114 &431597988 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 431597985} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300002, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &431597989 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 431597985} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &431597990 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 431597985} +--- !u!1 &518415680 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 518415681} + - component: {fileID: 518415683} + - component: {fileID: 518415682} + m_Layer: 5 + m_Name: Arrows + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &518415681 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 518415680} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 4, y: 4, z: 4} + m_Children: + - {fileID: 584722751} + - {fileID: 1839492109} + - {fileID: 824043105} + - {fileID: 25665774} + m_Father: {fileID: 779537215} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 296.79962, y: 877.38495} + m_SizeDelta: {x: -300.2, y: -257.50748} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!225 &518415682 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 518415680} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &518415683 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 518415680} +--- !u!1 &584722750 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 584722751} + - component: {fileID: 584722755} + - component: {fileID: 584722754} + - component: {fileID: 584722753} + - component: {fileID: 584722752} + m_Layer: 0 + m_Name: ArrowDown + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &584722751 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 584722750} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 518415681} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -108.5, y: 50.4} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &584722752 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 584722750} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc509a62d8e6cfc45a8431d8d586b9f6, type: 3} + m_Name: + m_EditorClassIdentifier: + AxisPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: DownPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: DownReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: VerticalAxisPressed + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: -1 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: MoreMountains.Tools.AxisEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.5 + AxisValue: -1 + MouseMode: 0 +--- !u!114 &584722753 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 584722750} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300014, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &584722754 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 584722750} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &584722755 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 584722750} +--- !u!1 &595800276 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 595800277} + - component: {fileID: 595800281} + - component: {fileID: 595800280} + - component: {fileID: 595800279} + - component: {fileID: 595800278} + m_Layer: 0 + m_Name: ButtonX + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &595800277 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 595800276} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 986164730} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -218.9998, y: 133} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &595800278 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 595800276} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 87a286771fa86194e979f187a1691a2a, type: 3} + m_Name: + m_EditorClassIdentifier: + ButtonPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: XPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: XReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: XPressed + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.3 + MouseMode: 0 +--- !u!114 &595800279 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 595800276} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300008, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &595800280 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 595800276} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &595800281 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 595800276} +--- !u!1 &775086466 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 186740, guid: ffe3c56180a798b468a52d62fa67827a, type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 775086467} + - component: {fileID: 775086470} + - component: {fileID: 775086469} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &775086467 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 444286, guid: ffe3c56180a798b468a52d62fa67827a, type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 775086466} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -24.6, y: -1.52, z: -100} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &775086469 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11474690, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 775086466} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1077351063, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &775086470 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11499252, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 775086466} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -619905303, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 5 +--- !u!1 &779537214 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 779537215} + - component: {fileID: 779537216} + m_Layer: 0 + m_Name: MMTouchControls + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &779537215 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 779537214} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: + - {fileID: 1749976725} + - {fileID: 986164730} + - {fileID: 245136921} + - {fileID: 518415681} + m_Father: {fileID: 1746135496} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 105.00005, y: 105.000015} + m_SizeDelta: {x: 512, y: 512} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!225 &779537216 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 779537214} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!1 &824043104 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 824043105} + - component: {fileID: 824043109} + - component: {fileID: 824043108} + - component: {fileID: 824043107} + - component: {fileID: 824043106} + m_Layer: 0 + m_Name: ArrowRight + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &824043105 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 824043104} + m_LocalRotation: {x: 0, y: 0, z: 0.7071068, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 518415681} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -38.5, y: 120.4} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &824043106 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 824043104} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc509a62d8e6cfc45a8431d8d586b9f6, type: 3} + m_Name: + m_EditorClassIdentifier: + AxisPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: RightPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: RightReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: HorizontalAxisPressed + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 1 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: MoreMountains.Tools.AxisEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.5 + AxisValue: 1 + MouseMode: 0 +--- !u!114 &824043107 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 824043104} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300014, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &824043108 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 824043104} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &824043109 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 824043104} +--- !u!1 &986164729 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 986164730} + - component: {fileID: 986164732} + - component: {fileID: 986164731} + m_Layer: 5 + m_Name: Buttons + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &986164730 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 986164729} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 4, y: 4, z: 4} + m_Children: + - {fileID: 431597986} + - {fileID: 1860798435} + - {fileID: 1060593625} + - {fileID: 595800277} + - {fileID: 216932622} + m_Father: {fileID: 779537215} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 2206, y: 194.98495} + m_SizeDelta: {x: -225, y: -205.50748} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!225 &986164731 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 986164729} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &986164732 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 986164729} +--- !u!1 &1060593624 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1060593625} + - component: {fileID: 1060593629} + - component: {fileID: 1060593628} + - component: {fileID: 1060593627} + - component: {fileID: 1060593626} + m_Layer: 0 + m_Name: ButtonY + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1060593625 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1060593624} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 986164730} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -158.9999, y: 193.5} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1060593626 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1060593624} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 87a286771fa86194e979f187a1691a2a, type: 3} + m_Name: + m_EditorClassIdentifier: + ButtonPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: YPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: YReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: YPressed + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.3 + MouseMode: 0 +--- !u!114 &1060593627 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1060593624} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300006, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &1060593628 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1060593624} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &1060593629 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1060593624} +--- !u!1 &1075232282 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1075232283} + - component: {fileID: 1075232287} + - component: {fileID: 1075232286} + - component: {fileID: 1075232285} + - component: {fileID: 1075232284} + m_Layer: 0 + m_Name: JoystickKnob + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1075232283 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1075232282} + 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_Children: [] + m_Father: {fileID: 245136921} + m_RootOrder: 0 + 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.000061035156, y: 0} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1075232284 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1075232282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f6a0577d46b80a4f929e57370c60650, type: 3} + m_Name: + m_EditorClassIdentifier: + TargetCamera: {fileID: 1353894569} + PressedOpacity: 0.5 + HorizontalAxisEnabled: 1 + VerticalAxisEnabled: 1 + MaxRange: 1.5 + JoystickValue: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: RightJoystickMovement + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: MoreMountains.Tools.JoystickEvent, Assembly-CSharp, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null +--- !u!114 &1075232285 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1075232282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300000, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &1075232286 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1075232282} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &1075232287 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1075232282} +--- !u!1 &1353894565 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 170940, guid: ffe3c56180a798b468a52d62fa67827a, type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1353894570} + - component: {fileID: 1353894569} + - component: {fileID: 1353894568} + - component: {fileID: 1353894567} + m_Layer: 0 + m_Name: UICamera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!92 &1353894567 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 9204502, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1353894565} + m_Enabled: 1 +--- !u!124 &1353894568 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 12432142, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1353894565} + m_Enabled: 1 +--- !u!20 &1353894569 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 2037892, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1353894565} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 4 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0.019607844} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 10 + m_Depth: 1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 32 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 0 + m_AllowMSAA: 1 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 + m_StereoMirrorMode: 0 +--- !u!4 &1353894570 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 479578, guid: ffe3c56180a798b468a52d62fa67827a, type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1353894565} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -52.7, y: 0, z: -100} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1746135496} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1497221652 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1497221654} + - component: {fileID: 1497221653} + m_Layer: 0 + m_Name: TestInputManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1497221653 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1497221652} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9640016db57d1c646a90fa18111eebf0, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &1497221654 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1497221652} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -52.7, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1626840950 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1626840951} + - component: {fileID: 1626840954} + - component: {fileID: 1626840953} + - component: {fileID: 1626840952} + - component: {fileID: 1626840955} + m_Layer: 0 + m_Name: JoystickKnob + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1626840951 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1626840950} + 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_Children: [] + m_Father: {fileID: 1749976725} + m_RootOrder: 0 + 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.000061035156, y: 0} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1626840952 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1626840950} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300000, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &1626840953 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1626840950} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &1626840954 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1626840950} +--- !u!114 &1626840955 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1626840950} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f6a0577d46b80a4f929e57370c60650, type: 3} + m_Name: + m_EditorClassIdentifier: + TargetCamera: {fileID: 1353894569} + PressedOpacity: 0.5 + HorizontalAxisEnabled: 1 + VerticalAxisEnabled: 1 + MaxRange: 1.5 + JoystickValue: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: LeftJoystickMovement + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: MoreMountains.Tools.JoystickEvent, Assembly-CSharp, Version=0.0.0.0, + Culture=neutral, PublicKeyToken=null +--- !u!1 &1725196231 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1725196233} + - component: {fileID: 1725196232} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1725196232 +Light: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1725196231} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1725196233 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1725196231} + m_LocalRotation: {x: 0.40821794, y: -0.23456973, z: 0.109381676, w: 0.87542605} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1746135495 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 122404, guid: ffe3c56180a798b468a52d62fa67827a, type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1746135496} + - component: {fileID: 1746135499} + - component: {fileID: 1746135498} + - component: {fileID: 1746135497} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1746135496 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 22453656, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1746135495} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 779537215} + m_Father: {fileID: 1353894570} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &1746135497 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11497174, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1746135495} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1301386320, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 0 +--- !u!114 &1746135498 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11425668, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1746135495} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1980459831, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &1746135499 +Canvas: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 22320466, guid: ffe3c56180a798b468a52d62fa67827a, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1746135495} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 1 + m_Camera: {fileID: 1353894569} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 25 + m_SortingLayerID: -1624263147 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!1 &1749976724 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1749976725} + - component: {fileID: 1749976729} + - component: {fileID: 1749976728} + - component: {fileID: 1749976727} + m_Layer: 0 + m_Name: Joystick + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1749976725 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1749976724} + 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_Children: + - {fileID: 1626840951} + m_Father: {fileID: 779537215} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 255.99948, y: 255.99986} + m_SizeDelta: {x: 512, y: 512} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1749976727 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1749976724} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300010, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &1749976728 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1749976724} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &1749976729 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1749976724} +--- !u!1 &1808251078 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1808251083} + - component: {fileID: 1808251082} + - component: {fileID: 1808251081} + - component: {fileID: 1808251080} + - component: {fileID: 1808251079} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1808251079 +AudioListener: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1808251078} + m_Enabled: 1 +--- !u!124 &1808251080 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1808251078} + m_Enabled: 1 +--- !u!92 &1808251081 +Behaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1808251078} + m_Enabled: 1 +--- !u!20 &1808251082 +Camera: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1808251078} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0.019607844} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 0 + m_AllowMSAA: 1 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 + m_StereoMirrorMode: 0 +--- !u!4 &1808251083 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1808251078} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1839492108 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1839492109} + - component: {fileID: 1839492113} + - component: {fileID: 1839492112} + - component: {fileID: 1839492111} + - component: {fileID: 1839492110} + m_Layer: 0 + m_Name: ArrowUp + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1839492109 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1839492108} + m_LocalRotation: {x: 0, y: 0, z: 1, w: -0.00000016292068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 518415681} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -108.5, y: 190.4} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1839492110 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1839492108} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc509a62d8e6cfc45a8431d8d586b9f6, type: 3} + m_Name: + m_EditorClassIdentifier: + AxisPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: UpPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: UpReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + AxisPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: VerticalAxisPressed + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 1 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: MoreMountains.Tools.AxisEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.5 + AxisValue: 1 + MouseMode: 0 +--- !u!114 &1839492111 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1839492108} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300014, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &1839492112 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1839492108} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &1839492113 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1839492108} +--- !u!1 &1860798434 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 5 + m_Component: + - component: {fileID: 1860798435} + - component: {fileID: 1860798439} + - component: {fileID: 1860798438} + - component: {fileID: 1860798437} + - component: {fileID: 1860798436} + m_Layer: 0 + m_Name: ButtonRT + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1860798435 +RectTransform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1860798434} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} + m_Children: [] + m_Father: {fileID: 986164730} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -74.00027, y: 218.3} + m_SizeDelta: {x: 256, y: 256} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1860798436 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1860798434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 87a286771fa86194e979f187a1691a2a, type: 3} + m_Name: + m_EditorClassIdentifier: + ButtonPressedFirstTime: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: RTPressedFirstTime + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonReleased: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: RTReleased + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + ButtonPressed: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 1497221653} + m_MethodName: RTPressed + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + m_TypeName: UnityEngine.Events.UnityEvent, UnityEngine, Version=0.0.0.0, Culture=neutral, + PublicKeyToken=null + PressedOpacity: 0.3 + MouseMode: 0 +--- !u!114 &1860798437 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1860798434} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 21300004, guid: 358b5d9f23bc9413e87c9948acc67ee1, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!225 &1860798438 +CanvasGroup: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1860798434} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 1 + m_BlocksRaycasts: 1 + m_IgnoreParentGroups: 0 +--- !u!222 &1860798439 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1860798434} diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity.meta b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity.meta new file mode 100644 index 0000000..9843f34 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMControlsTestScene.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fc659b5219bfdc945881c41b2c491b52 +timeCreated: 1462398451 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs b/Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs new file mode 100644 index 0000000..b919180 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs @@ -0,0 +1,173 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace MoreMountains.Tools +{ + /// + /// The possible directions a swipe can have + /// + public enum MMPossibleSwipeDirections { Up, Down, Left, Right } + + + [System.Serializable] + public class SwipeEvent : UnityEvent {} + + /// + /// An event usually triggered when a swipe happens. It contains the swipe "base" direction, and detailed information if needed (angle, length, origin and destination + /// + public struct MMSwipeEvent + { + public MMPossibleSwipeDirections SwipeDirection; + public float SwipeAngle; + public float SwipeLength; + public Vector2 SwipeOrigin; + public Vector2 SwipeDestination; + + /// + /// Initializes a new instance of the struct. + /// + /// Direction. + /// Angle. + /// Length. + /// Origin. + /// Destination. + public MMSwipeEvent(MMPossibleSwipeDirections direction, float angle, float length, Vector2 origin, Vector2 destination) + { + SwipeDirection = direction; + SwipeAngle = angle; + SwipeLength = length; + SwipeOrigin = origin; + SwipeDestination = destination; + } + } + + [RequireComponent(typeof(RectTransform))] + /// + /// Add a swipe manager to your scene, and it'll trigger MMSwipeEvents everytime a swipe happens. From its inspector you can determine the minimal length of a swipe. Shorter swipes will be ignored + /// + public class MMSwipeZone : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler + { + /// the minimal length of a swipe + public float MinimalSwipeLength = 50f; + /// the maximum press length of a swipe + public float MaximumPressLength = 10f; + + /// The method(s) to call when the zone is swiped + public SwipeEvent ZoneSwiped; + /// The method(s) to call while the zone is being pressed + public UnityEvent ZonePressed; + + [Header("Mouse Mode")] + [Information("If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).",InformationAttribute.InformationType.Info,false)] + /// If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input). + public bool MouseMode = false; + + protected Vector2 _firstTouchPosition; + protected float _angle; + protected float _length; + protected Vector2 _destination; + protected Vector2 _deltaSwipe; + protected MMPossibleSwipeDirections _swipeDirection; + + protected virtual void Swipe() + { + MMSwipeEvent swipeEvent = new MMSwipeEvent (_swipeDirection, _angle, _length, _firstTouchPosition, _destination); + MMEventManager.TriggerEvent(swipeEvent); + if (ZoneSwiped != null) + { + ZoneSwiped.Invoke (swipeEvent); + } + } + + protected virtual void Press() + { + if (ZonePressed != null) + { + ZonePressed.Invoke (); + } + } + + /// + /// Triggers the bound pointer down action + /// + public virtual void OnPointerDown(PointerEventData data) + { + _firstTouchPosition = Input.mousePosition; + } + + /// + /// Triggers the bound pointer up action + /// + public virtual void OnPointerUp(PointerEventData data) + { + _destination = Input.mousePosition; + _deltaSwipe = _destination - _firstTouchPosition; + _length = _deltaSwipe.magnitude; + + // if the swipe has been long enough + if (_length > MinimalSwipeLength) + { + _angle = MMMaths.AngleBetween (_deltaSwipe, Vector2.right); + _swipeDirection = AngleToSwipeDirection (_angle); + Swipe (); + } + + // if it's just a press + if (_deltaSwipe.magnitude < MaximumPressLength) + { + Press (); + } + } + + /// + /// Triggers the bound pointer enter action when touch enters zone + /// + public void OnPointerEnter(PointerEventData data) + { + if (!MouseMode) + { + OnPointerDown (data); + } + } + + /// + /// Triggers the bound pointer exit action when touch is out of zone + /// + public void OnPointerExit(PointerEventData data) + { + if (!MouseMode) + { + OnPointerUp(data); + } + } + + /// + /// Determines a MMPossibleSwipeDirection out of an angle in degrees. + /// + /// The to swipe direction. + /// Angle in degrees. + protected virtual MMPossibleSwipeDirections AngleToSwipeDirection(float angle) + { + if ((angle < 45) || (angle >= 315)) + { + return MMPossibleSwipeDirections.Right; + } + if ((angle >= 45) && (angle < 135)) + { + return MMPossibleSwipeDirections.Up; + } + if ((angle >= 135) && (angle < 225)) + { + return MMPossibleSwipeDirections.Left; + } + if ((angle >= 225) && (angle < 315)) + { + return MMPossibleSwipeDirections.Down; + } + return MMPossibleSwipeDirections.Right; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs.meta new file mode 100644 index 0000000..e888658 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMSwipeZone.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5a68e615c17d2bf49a1a082e34fd9b6e +timeCreated: 1494336562 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs b/Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs new file mode 100644 index 0000000..0e3be04 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs @@ -0,0 +1,172 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace MoreMountains.Tools +{ + [System.Serializable] + public class AxisEvent : UnityEvent {} + + [RequireComponent(typeof(Rect))] + [RequireComponent(typeof(CanvasGroup))] + /// + /// Add this component to a GUI Image to have it act as an axis. + /// Bind pressed down, pressed continually and released actions to it from the inspector + /// Handles mouse and multi touch + /// + public class MMTouchAxis : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler + { + public enum ButtonStates { Off, ButtonDown, ButtonPressed, ButtonUp } + [Header("Binding")] + /// The method(s) to call when the axis gets pressed down + public UnityEvent AxisPressedFirstTime; + /// The method(s) to call when the axis gets released + public UnityEvent AxisReleased; + /// The method(s) to call while the axis is being pressed + public AxisEvent AxisPressed; + + [Header("Pressed Behaviour")] + [Information("Here you can set the opacity of the button when it's pressed. Useful for visual feedback.",InformationAttribute.InformationType.Info,false)] + /// the new opacity to apply to the canvas group when the axis is pressed + public float PressedOpacity = 0.5f; + /// the value to send the bound method when the axis is pressed + public float AxisValue; + + [Header("Mouse Mode")] + [Information("If you set this to true, you'll need to actually press the axis for it to be triggered, otherwise a simple hover will trigger it (better for touch input).",InformationAttribute.InformationType.Info,false)] + /// If you set this to true, you'll need to actually press the axis for it to be triggered, otherwise a simple hover will trigger it (better for touch input). + public bool MouseMode = false; + + public ButtonStates CurrentState { get; protected set; } + + protected CanvasGroup _canvasGroup; + protected float _initialOpacity; + + /// + /// On Start, we get our canvasgroup and set our initial alpha + /// + protected virtual void Awake() + { + _canvasGroup = GetComponent(); + if (_canvasGroup!=null) + { + _initialOpacity = _canvasGroup.alpha; + } + ResetButton(); + } + + /// + /// Every frame, if the touch zone is pressed, we trigger the bound method if it exists + /// + protected virtual void Update() + { + if (AxisPressed != null) + { + if (CurrentState == ButtonStates.ButtonPressed) + { + AxisPressed.Invoke(AxisValue); + } + } + } + + /// + /// At the end of every frame, we change our button's state if needed + /// + protected virtual void LateUpdate() + { + if (CurrentState == ButtonStates.ButtonUp) + { + CurrentState = ButtonStates.Off; + } + if (CurrentState == ButtonStates.ButtonDown) + { + CurrentState = ButtonStates.ButtonPressed; + } + } + + /// + /// Triggers the bound pointer down action + /// + public virtual void OnPointerDown(PointerEventData data) + { + if (CurrentState != ButtonStates.Off) + { + return; + } + + CurrentState = ButtonStates.ButtonDown; + if (_canvasGroup!=null) + { + _canvasGroup.alpha=PressedOpacity; + } + if (AxisPressedFirstTime!=null) + { + AxisPressedFirstTime.Invoke(); + } + } + + /// + /// Triggers the bound pointer up action + /// + public virtual void OnPointerUp(PointerEventData data) + { + if (CurrentState != ButtonStates.ButtonPressed && CurrentState != ButtonStates.ButtonDown) + { + return; + } + + CurrentState = ButtonStates.ButtonUp; + if (_canvasGroup!=null) + { + _canvasGroup.alpha=_initialOpacity; + } + if (AxisReleased != null) + { + AxisReleased.Invoke(); + } + AxisPressed.Invoke(0); + } + + /// + /// OnEnable, we reset our button state + /// + protected virtual void OnEnable() + { + ResetButton(); + } + + /// + /// Resets the button's state and opacity + /// + protected virtual void ResetButton() + { + CurrentState = ButtonStates.Off; + _canvasGroup.alpha = _initialOpacity; + CurrentState = ButtonStates.Off; + } + + /// + /// Triggers the bound pointer enter action when touch enters zone + /// + public void OnPointerEnter(PointerEventData data) + { + if (!MouseMode) + { + OnPointerDown (data); + } + } + + /// + /// Triggers the bound pointer exit action when touch is out of zone + /// + public void OnPointerExit(PointerEventData data) + { + if (!MouseMode) + { + OnPointerUp(data); + } + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs.meta new file mode 100644 index 0000000..7e042cc --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchAxis.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fc509a62d8e6cfc45a8431d8d586b9f6 +timeCreated: 1462615497 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs b/Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs new file mode 100644 index 0000000..1e642b0 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs @@ -0,0 +1,176 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace MoreMountains.Tools +{ + [RequireComponent(typeof(Rect))] + [RequireComponent(typeof(CanvasGroup))] + /// + /// Add this component to a GUI Image to have it act as a button. + /// Bind pressed down, pressed continually and released actions to it from the inspector + /// Handles mouse and multi touch + /// + public class MMTouchButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler + { + public enum ButtonStates { Off, ButtonDown, ButtonPressed, ButtonUp } + [Header("Binding")] + /// The method(s) to call when the button gets pressed down + public UnityEvent ButtonPressedFirstTime; + /// The method(s) to call when the button gets released + public UnityEvent ButtonReleased; + /// The method(s) to call while the button is being pressed + public UnityEvent ButtonPressed; + + [Header("Pressed Behaviour")] + [Information("Here you can set the opacity of the button when it's pressed. Useful for visual feedback.",InformationAttribute.InformationType.Info,false)] + /// the new opacity to apply to the canvas group when the button is pressed + public float PressedOpacity = 0.5f; + + [Header("Mouse Mode")] + [Information("If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).",InformationAttribute.InformationType.Info,false)] + /// If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input). + public bool MouseMode = false; + + /// the current state of the button (off, down, pressed or up) + public ButtonStates CurrentState { get; protected set; } + + protected bool _zonePressed = false; + protected CanvasGroup _canvasGroup; + protected float _initialOpacity; + + /// + /// On Start, we get our canvasgroup and set our initial alpha + /// + protected virtual void Awake() + { + _canvasGroup = GetComponent(); + if (_canvasGroup!=null) + { + _initialOpacity = _canvasGroup.alpha; + } + ResetButton(); + } + + /// + /// Every frame, if the touch zone is pressed, we trigger the OnPointerPressed method, to detect continuous press + /// + protected virtual void Update() + { + if (CurrentState == ButtonStates.ButtonPressed) + { + OnPointerPressed(); + } + } + + /// + /// At the end of every frame, we change our button's state if needed + /// + protected virtual void LateUpdate() + { + if (CurrentState == ButtonStates.ButtonUp) + { + CurrentState = ButtonStates.Off; + } + if (CurrentState == ButtonStates.ButtonDown) + { + CurrentState = ButtonStates.ButtonPressed; + } + } + + /// + /// Triggers the bound pointer down action + /// + public virtual void OnPointerDown(PointerEventData data) + { + if (CurrentState != ButtonStates.Off) + { + return; + } + + CurrentState = ButtonStates.ButtonDown; + if (_canvasGroup!=null) + { + _canvasGroup.alpha=PressedOpacity; + } + if (ButtonPressedFirstTime!=null) + { + ButtonPressedFirstTime.Invoke(); + } + } + + /// + /// Triggers the bound pointer up action + /// + public virtual void OnPointerUp(PointerEventData data) + { + if (CurrentState != ButtonStates.ButtonPressed && CurrentState != ButtonStates.ButtonDown) + { + return; + } + + CurrentState = ButtonStates.ButtonUp; + if (_canvasGroup!=null) + { + _canvasGroup.alpha=_initialOpacity; + } + if (ButtonReleased != null) + { + ButtonReleased.Invoke(); + } + } + + /// + /// Triggers the bound pointer pressed action + /// + public virtual void OnPointerPressed() + { + CurrentState = ButtonStates.ButtonPressed; + if (ButtonPressed != null) + { + ButtonPressed.Invoke(); + } + } + + /// + /// OnEnable, we reset our button state + /// + protected virtual void OnEnable() + { + ResetButton(); + } + + /// + /// Resets the button's state and opacity + /// + protected virtual void ResetButton() + { + _canvasGroup.alpha = _initialOpacity; + CurrentState = ButtonStates.Off; + } + + /// + /// Triggers the bound pointer enter action when touch enters zone + /// + public void OnPointerEnter(PointerEventData data) + { + if (!MouseMode) + { + OnPointerDown (data); + } + } + + /// + /// Triggers the bound pointer exit action when touch is out of zone + /// + public void OnPointerExit(PointerEventData data) + { + if (!MouseMode) + { + OnPointerUp(data); + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs.meta new file mode 100644 index 0000000..eb0bd41 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchButton.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 87a286771fa86194e979f187a1691a2a +timeCreated: 1462399350 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs b/Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs new file mode 100644 index 0000000..51c3ae3 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs @@ -0,0 +1,70 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; +using UnityEngine.Events; +using UnityEngine.EventSystems; + +namespace MoreMountains.Tools +{ + [RequireComponent(typeof(CanvasGroup))] + public class MMTouchControls : MonoBehaviour + { + public enum InputForcedMode { None, Mobile, Desktop } + [Information("If you check Auto Mobile Detection, the engine will automatically switch to mobile controls when your build target is Android or iOS. You can also force mobile or desktop (keyboard, gamepad) controls using the dropdown below.\nNote that if you don't need mobile controls and/or GUI this component can also work on its own, just put it on an empty GameObject instead.",InformationAttribute.InformationType.Info,false)] + /// If you check Auto Mobile Detection, the engine will automatically switch to mobile controls when your build target is Android or iOS. + /// You can also force mobile or desktop (keyboard, gamepad) controls using the dropdown below.Note that if you don't need mobile controls + /// and/or GUI this component can also work on its own, just put it on an empty GameObject instead. + public bool AutoMobileDetection = true; + /// Force desktop mode (gamepad, keyboard...) or mobile (touch controls) + public InputForcedMode ForcedMode; + public bool IsMobile { get; protected set; } + + protected CanvasGroup _canvasGroup; + protected float _initialMobileControlsAlpha; + + /// + /// We get the player from its tag. + /// + protected virtual void Start() + { + _canvasGroup = GetComponent(); + + _initialMobileControlsAlpha = _canvasGroup.alpha; + SetMobileControlsActive(false); + IsMobile=false; + if (AutoMobileDetection) + { + #if UNITY_ANDROID || UNITY_IPHONE + SetMobileControlsActive(true); + IsMobile = true; + #endif + } + if (ForcedMode==InputForcedMode.Mobile) + { + SetMobileControlsActive(true); + IsMobile = true; + } + if (ForcedMode==InputForcedMode.Desktop) + { + SetMobileControlsActive(false); + IsMobile = false; + } + } + + public virtual void SetMobileControlsActive(bool state) + { + if (_canvasGroup!=null) + { + _canvasGroup.gameObject.SetActive(state); + if (state) + { + _canvasGroup.alpha=_initialMobileControlsAlpha; + } + else + { + _canvasGroup.alpha=0; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs.meta new file mode 100644 index 0000000..6ad3386 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchControls.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fec2335f22dc84b549dd9dfdeebca3e0 +timeCreated: 1467025573 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs b/Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs new file mode 100644 index 0000000..6717c65 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; +using UnityEngine.Events; +using UnityEngine.EventSystems; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Add this component to a UI rectangle and it'll act as a detection zone for a joystick. + /// Note that this component extends the MMTouchJoystick class so you don't need to add another joystick to it. It's both the detection zone and the stick itself. + /// + public class MMTouchDynamicJoystick : MMTouchJoystick, IPointerDownHandler + { + [Header("Dynamic Joystick")] + [Information("Here you can select an image for your joystick's knob, and decide if the joystick's detection zone should reset its position whenever the drag ends.",InformationAttribute.InformationType.Info,false)] + /// the image to use for your joystick's knob + public Sprite JoystickKnobImage; + /// if this is set to true, the joystick's touch zone will revert to its initial position whenever the player drops the joystick. If not, it'll stay where it was. + public bool RestorePosition = true; + + protected Vector3 _initialPosition; + protected Vector3 _newPosition; + protected CanvasGroup _knobCanvasGroup; + + /// + /// On Start, we instantiate our joystick's image if there's one + /// + protected override void Start() + { + base.Start(); + + // we store the detection zone's initial position + _initialPosition = GetComponent().localPosition; + + // we instantiate our joystick knob + if (JoystickKnobImage!=null) + { + GameObject knob = new GameObject(); + knob.transform.SetParent(gameObject.transform); + knob.name="DynamicJoystickKnob"; + knob.transform.position = transform.position; + knob.transform.localScale = transform.localScale; + + Image knobImage = knob.AddComponent(); + knobImage.sprite = JoystickKnobImage; + + _knobCanvasGroup = knob.AddComponent(); + } + } + + /// + /// When the zone is pressed, we move our joystick accordingly + /// + /// Data. + public virtual void OnPointerDown(PointerEventData data) + { + // if we're in "screen space - camera" render mode + if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera) + { + _newPosition = TargetCamera.ScreenToWorldPoint(data.position); + } + // otherwise + else + { + _newPosition = data.position; + } + _newPosition.z = this.transform.position.z; + + // we define a new neutral position + SetNeutralPosition(_newPosition); + } + + /// + /// When the player lets go of the stick, we restore our stick's position if needed + /// + /// Event data. + public override void OnEndDrag(PointerEventData eventData) + { + base.OnEndDrag(eventData); + if (RestorePosition) + { + GetComponent().localPosition = _initialPosition; + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs.meta new file mode 100644 index 0000000..c55a76e --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchDynamicJoystick.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9141eaf6926dad94a85fdf6495d2bbf9 +timeCreated: 1468691024 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs b/Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs new file mode 100644 index 0000000..c3d45dc --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs @@ -0,0 +1,186 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; +using UnityEngine.EventSystems; +using UnityEngine.Events; +using System; + +namespace MoreMountains.Tools +{ + [System.Serializable] + public class JoystickEvent : UnityEvent {} + + /// + /// Joystick input class. + /// In charge of the behaviour of the joystick mobile touch input. + /// Bind its actions from the inspector + /// Handles mouse and multi touch + /// + [RequireComponent(typeof(Rect))] + [RequireComponent(typeof(CanvasGroup))] + public class MMTouchJoystick : MonoBehaviour, IDragHandler, IEndDragHandler + { + [Header("Camera")] + public Camera TargetCamera; + + [Header("Pressed Behaviour")] + [Information("Here you can set the opacity of the joystick when it's pressed. Useful for visual feedback.",InformationAttribute.InformationType.Info,false)] + /// the new opacity to apply to the canvas group when the button is pressed + public float PressedOpacity = 0.5f; + + [Header("Axis")] + [Information("Choose if you want a joystick limited to one axis or not, and define the MaxRange. The MaxRange is the maximum distance from its initial center position you can drag the joystick to.",InformationAttribute.InformationType.Info,false)] + /// Is horizontal axis allowed + public bool HorizontalAxisEnabled = true; + /// Is vertical axis allowed + public bool VerticalAxisEnabled = true; + /// The max range allowed + [Information("And finally you can bind a function to get your joystick's values. Your method has to have a Vector2 as a parameter. Drag your object here and select the method.",InformationAttribute.InformationType.Info,true)] + public float MaxRange = 1.5f; + + [Header("Binding")] + /// The method(s) to call when the button gets pressed down + public JoystickEvent JoystickValue; + + + public RenderMode ParentCanvasRenderMode { get; protected set; } + + /// Store neutral position of the stick + protected Vector2 _neutralPosition; + /// Current horizontal and vertical values of the joystick (from -1 to 1) + protected Vector2 _joystickValue; + /// The canvas rect transform we're working with. + protected RectTransform _canvasRectTransform; + /// working vector + protected Vector2 _newTargetPosition; + protected Vector3 _newJoystickPosition; + protected float _initialZPosition; + + protected CanvasGroup _canvasGroup; + protected float _initialOpacity; + + + /// + /// On Start, we get our working canvas, and we set our neutral position + /// + protected virtual void Start() + { + Initialize(); + } + + public virtual void Initialize() + { + _canvasRectTransform = GetComponentInParent().transform as RectTransform; + _canvasGroup = GetComponent(); + + SetNeutralPosition(); + if (TargetCamera == null) + { + throw new Exception("MMTouchJoystick : you have to set a target camera"); + } + ParentCanvasRenderMode = GetComponentInParent().renderMode; + _initialZPosition = transform.position.z; + _initialOpacity = _canvasGroup.alpha; + } + + /// + /// On Update we check for an orientation change if needed, and send our input values. + /// + protected virtual void Update() + { + if (JoystickValue != null) + { + if (HorizontalAxisEnabled || VerticalAxisEnabled) + { + JoystickValue.Invoke(_joystickValue); + } + } + } + + /// + /// Sets the neutral position of the joystick + /// + public virtual void SetNeutralPosition() + { + _neutralPosition = GetComponent().transform.position; + } + + public virtual void SetNeutralPosition(Vector3 newPosition) + { + _neutralPosition = newPosition; + } + + /// + /// Handles dragging of the joystick + /// + public virtual void OnDrag(PointerEventData eventData) + { + _canvasGroup.alpha=PressedOpacity; + + // if we're in "screen space - camera" render mode + if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera) + { + _newTargetPosition = TargetCamera.ScreenToWorldPoint(eventData.position); + } + // otherwise + else + { + _newTargetPosition = eventData.position; + } + + // We clamp the stick's position to let it move only inside its defined max range + _newTargetPosition = Vector2.ClampMagnitude(_newTargetPosition - _neutralPosition, MaxRange); + + // If we haven't authorized certain axis, we force them to zero + if (!HorizontalAxisEnabled) + { + _newTargetPosition.x = 0; + } + if (!VerticalAxisEnabled) + { + _newTargetPosition.y = 0; + } + // For each axis, we evaluate its lerped value (-1...1) + _joystickValue.x = EvaluateInputValue(_newTargetPosition.x); + _joystickValue.y = EvaluateInputValue(_newTargetPosition.y); + + _newJoystickPosition = _neutralPosition + _newTargetPosition; + _newJoystickPosition.z = _initialZPosition; + + // We move the joystick to its dragged position + transform.position = _newJoystickPosition; + } + + /// + /// What happens when the stick is released + /// + public virtual void OnEndDrag(PointerEventData eventData) + { + // we reset the stick's position + _newJoystickPosition = _neutralPosition; + _newJoystickPosition.z = _initialZPosition; + transform.position = _newJoystickPosition; + _joystickValue.x = 0f; + _joystickValue.y = 0f; + + // we set its opacity back + _canvasGroup.alpha=_initialOpacity; + } + + /// + /// We compute the axis value from the interval between neutral position, current stick position (vectorPosition) and max range + /// + /// The axis value, a float between -1 and 1 + /// stick position. + protected virtual float EvaluateInputValue(float vectorPosition) + { + return Mathf.InverseLerp(0, MaxRange, Mathf.Abs(vectorPosition)) * Mathf.Sign(vectorPosition); + } + + protected virtual void OnEnable() + { + Initialize(); + _canvasGroup.alpha = _initialOpacity; + } + } +} diff --git a/Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs.meta b/Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs.meta new file mode 100644 index 0000000..c786528 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/MMTouchJoystick.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 8f6a0577d46b80a4f929e57370c60650 +timeCreated: 1462403537 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/Sprites.meta b/Assets/CorgiEngine/MMTools/MMControls/Sprites.meta new file mode 100644 index 0000000..52f6856 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMControls/Sprites.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b0fa082c85c0a432db31381c1a6aaa81 +folderAsset: yes +timeCreated: 1462375704 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMControls/Sprites/mobile-controls-spritesheet.png b/Assets/CorgiEngine/MMTools/MMControls/Sprites/mobile-controls-spritesheet.png new file mode 100644 index 0000000000000000000000000000000000000000..2eaf0fadf59aa00ac0928705a565e97024255cdf GIT binary patch literal 105880 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

^AlhF4HryFD{?(_?%VDN7fWZ{H+?9$3I4R zG%c@8xL~?>2B_~Dm-}i0M(iomyUZFv!sx8lc#}=mantkX&wEhY+uMV_lOY0Gt{DoT z*SCJp_*s` zf6SFa^3&Tz#Da6ig~5J)qY_l3V+K=W{Y|NPZI;39E(c}@=iEz(C0~EM>;vc)PaA}z@%}H>YwWfEI70nBL3Y!f+@z&PW_mMlkn+qkdZc<{ zb&N3C`DA9MO%k`nUY|JsDqLM=Pm9G^iE-%-^|k#Id+BK*x!}D-sB8_0g9P11^x_(V)W?e3B;l;h0uaH8~VUK^@~ z=YyJmoT}L$$e=X>{+ z5rF2=tte-3P-!zUwC-iVV;i3B(%|nlH@eCkezJd$a@XMczQyZlOkvu)v~^?d=WwOr9Q!ON=?*x0Kv7|cnD3l`RBHdmM&m7X0RTp_6peFKAdiW2R zvS^ZU*bYr~;VVbfCdQpfzY|h>2R&=wjq#%e>u!ezK*L``OVH?|MGtfhe9QB&E4Nsmb zcHNNQB$r?xkbDR3(M)l_L;7r^3eU3RujQH1=A^mE-*CJjSQs3u%O45v%c|ho9;^>iZu+xGmZ6WDFH-~!D^SvK09rs2jC*~oLGdt6F#O=SLK z?dk+CZ6k|?OTC3l(U1RlDI*-L)wrQJEX@zy5#R5Boxq&A8y>$p!G=+*M5f7O?~^v( zKYdzb$HZZ%z`yro`c{`uE3Bl=`JGEpzx|F%ukkYQ(ZM9}9{EKKtT3jEvxI!eZ=piq zZ-II~`IXNa;=xXuboU9FiMk(+P#!3dbka`S?|*{6+gd}$3~OtD#@a)bMKd%%2#?7K zQR$-zLbXsl3a?Ck2?LKxLVCwe3jU6H*uI778GbYBwZZUHF)_g3U_VZZLOVW=8-N6CIF%!`kxjH z(PG0@^!*0rV`6zekR9lyUWU6pD-(Vg*RLb}?>CqG&T;L9*hCa09$(rr;jvanO8@jz zbo!$V=3Eg!<&iZ5GYGVEl|F(stYB|kiD{z+`)fb!ztQ^&c7sE*My}Z1&`edhW^!Wp zdRPkegIfOfk2y^uvQ%B6Wk3{L5dMa*qCNta5#Te)DQw9atSq*TJ}38oobm1~DA1GN zX%+LvZ+Af(rhUcBqTMDfR=+=w%3WF6JIIfVLJj*CY|6zD+N~i0G`E&NBe9k{zff`X zn&Ixa!}|Rn{V@Cid!s0i8?Jr0j!^eIN}vQ=9Vz6el>aA`Ux`q?e<)l^Y<;^fRJ^qf z+ab`b#0%Jj@XDn}Xm;(r$@OOuf0Sc)!zfYVZGd+h*~7?Ij~d>h2?EvpTL#3iub947 zNJB({$neQ=u7xjCG4bn2SIGKM)(Bz!STG0J&}y{f2~+n<=B2Tz4H&L2kxe|)-_~R} zXW%bJ@GE}VnxrX3YS{rQ(W23jU2U`x;xhD5MAFX7{2?;MIz(>eu}*P5L35V|n9R)< z!r0RAj)d_B?qhB;G+~gh`<*TY?aS2oGP&ZE(-DG#-G_a1@VwvwLXCFf1&MKY!@}MW&a-(CbEf~Ka9XzkTnlb4<@01D~;!3Xt za~5N1opq$-Qyawh+SH(e-!0VD55*8;z;W(Tr_K!p=k~XH4xLJhd!UYCwrhSOx_+$V z%MdLA^+ahxUj@f`W*yttzONFSNYXIAi44SRFu-6DHtRlz{kdV@myd1wqzjOaW2;1& z`_}$7ykT(3Gsg?Mz{%pa08($sn>!yhcXRfvsJ5lX)ejSZ^ytTB&@ixAoO?gw$k(Bd z7cr6DIJf9;iOz5$vHGqbWBmyQ5vppyoXPr-d@NMp-J8GSO0G2ehRsw+!#oaoU<~o& zaqwYMv>_C3;X9AgG`1vd)acFABfz>Y zwhCuhDGiTWA(`XUFXLX;@pa0$(v*!EvGki9bG!7!0PNH7-!hHXz7@RBA*I_lejj@F z0Hi-}v#^ebozOG;rfwvBtGS^F9bqAfi=9#2vh>9f)gSVvRR);UDxMc%^Tkm=juUB$ zx3@$XolCrMZSH&Lpy#q9)aSy3@Wp`eijFl8K7T9?*;t5z>P5OjZgGT=(@JiQdkr<1 z1E`4lx{~VlXA;cFBlI-u5-1SQiMk$EPCd5s2vn0PVs&M&Ah)7^TB zh4|Mex*#7Qh-HhFSQY=VUv$`~Nt>opq)kLTucEsVtgyN#P=fx$A9x@qf`GHfATjKk z>2B+Q^b93{oXrH#dLavQs;)yxiPz?S3ShqVbVG(t-rhPu*1NoA__@BoBC zG~IW0zE4@<5s$_N=07u04^^^R0GKu8qBNI`48mW%2z4Q-s_$w`k!IswwP!vS8F5k|2MPciOx{WiP-GpMvlL+vGTUY43cEPKuONw7$ z{4{7Fyy8*%?j}35Lh&in92Qb)?zuI9EIH$FUr2w*0`3$!W*N|?@!QdYVtj-Zds~zs zB*S}||Hjzy)IQyU{6-d#0RBfsZIVYWyqW3i z8(=BUFU7Te8;D@&f9_YZ_U%^qYZH~d0Bpn|VH>Qzam-e2aq2_5!7k*>A8DrjD{$u@ zH%S6rxZlfVfXrorbmG!S0o@VM3o${;YmH9E3W)8;uchBi8ipP zp}Gnm;9?&i<6`RSWi2lt8k8Dz-z6=AAqXvYw=DTTfS4_V|rehQlA`(NX& zvU;kE#rUTdJpQwlt=@P;_nnhBjv(#PewF6Q_BK(ooRv=(JVs%#rb9B#Q-lpb7|BIyf0#MON)6(X^ z30SKj_v~b!+l5Si*|bn+F|rfnkjUacn0Fo*NWEjA1@2n z6eUPlz8^aZ?0WgJp$$hul@qSyT0n0B0^uJib9wYreQq!b5$q1r}$h{{?) z$)#0$j{*jmKm6!2t4n>?sSz!4CQcLc{AukUr~t{g!#xlCq|*bkU%$*1`C}n;qOTqR z{R0)5mT$c81#h)9DMLUv)cF;|?Rcwzj@y8Z9uQv%1DVHJ{k#zbT$*G=BZL>`{dJ9w zVbW8givF<6cNc~5Dk=3^x8-Y)o50IUdHCx;Wbw00Xe zCSIuHP6|T<^n&n|4Y%x;khLj$02byv^i@u9$1m4~)Jv z!(w{@vSR1Rb8;}Yw9pj5z=YX=V}qeyUO}{DzMQJpi2!fMx!lAx5+{yW!PFCoo zk`@&S58oppncap)R$|-ZdjVXAX$yIt5e;Mf;Qfa#=|SUyi#}K}DB$O!xV^<7I_z;D z$wVP2n$~Zxxsf(8_>m9hN@lLXe6Zh?I_p_m=-Qv_7Mz&so15irPJi&>zYd*k4pyYq zH@Q6PR)yny0NU=T=-N?Hr$eW4ijB~c48jPn&}sTB@SrYux{|tQNq`-2)UuGSah3`C zTY8;xhAE6e2=PfT(IA`MYepm^2UF#LJsW_hNNHV%L`IRR?zF=YiBD?-?mF2LY}0;u z{}tWM-GI4Hda&c2!!1~%>yX5l_-P4tvgR4kB-9;!{-JVPLe`fe%VW)9ssJ4*GWAyH zS1h;Ft%h%VJK?p01zE%=!^EX*c8~TcF6Fa;XjFReLu-{Z7bH>6P|ujn?mhy~>e@Z) z#`hw?86AhD1{pR~rXKKt@5V9NFNK(Vr`Z9gcXof|6@^HpE7`*&cdLpDh!2zDNeXY@ znm1M&vbze&utni?6LMTGYyNU;s>v5A$iPG>-MN?I`Z-@&k_7CF(I-2STswOieU8LJ8eih)U30Q!aHBfk(AOPIPJI@IlMxz$poeGL6=sPnC) zf*zs1-qQt`GOH)oUDq4-CuKnDv0gcWlQ_!dks!mq9Lu(`A|VU&eq7Hj>V@-Gq9R8D z*wK*@1Hs8@J7JP_8@FKKo;t$p=#j9U;a^|VSm0()yFG5ec|ga5*4vOu07_?h@LSYgLyZYI!QtbX zUr?y4kmhq1@O&g>yAUnMP)B7VPO-IGc%E8Y9iz2_F0&A0Nxq7e%lFtO@`b_(4!8G% zhj+EG@eLZGx9&gr+2{)GAs_-&W@GKWC;4lk&LOVo9(J(1&zld8@hA-_4X<=+Zha5w zgt(O;{;|G?rZJ8RhWgw5cq16OW6*sZbR04p&cHkYkXKoV*(5os%2A{`C1a8epl>p` zw3ln%0O`$opp5kZ8{1P=XSm!!7?vGl1_Z0u+7i~K zcfT-!hLnagp`rIF_Yz>BCs9_8Vo-*;@L`KU>X7^ed#i9qb|VjCwsC#!^j$lh$xg@cQnt$!9BS> zCpc#E)06e&r_Lv;eJd^*hi5!1&awmM)c1|3&skMEl&SD{wn0OIGc2VI)jO;|w&Z7zjL!rB4eX5s{J10%aK!Tp>| zF@s@u#mYb%rXd}AJC!ZPW!+B`+DZ62X)~SME_r*Pw zklt^*&+i#s=SOO2-?Bs}ohs?cFcK3M1^q z{3Y2Pu?Xif)O*fM$9m)~y>TNP7#D|ti__GtS6GMF0I93tb0uQ7x7h&-et+yBBq;_B7hd#~BKq)gpW@8T# zUcXs@$O2+t`%D_Vv8M#bnI?SBQjmZfGZyf<-tl9;dEpC~fleBWV{oL_J^%925F;#R zQ{v572t)gyf-5++1ceu-2)Nxqwif}57AOLE_Y&54v+Z?fuF7;<0wunT z9x%%vfQdY>!HlhhA3~cH!fvIUsJRT#0EB;geLZekG`}Ygej$r6`s#Sk8o!$i{klHW z_H7E42hi44I{*&pnN?gHRDc|VJ8wM|`K;UiPO)1NAku8*WcjdAkj5;+$oKG^vBkTy zu*G5@%J|1>2z}uG655&B-<=k#&_n3CP7Tt+gkxdg;vek@gK!N7ukE+CS4{!f+d2m8 zWl#?>C9Q?E4B8DoynW2N?ekwgyB+V{So_uTbtzgM8gv3JuFhe-PgG|XDx`}6?Gkrr zZsbkaA1N@Z0VzeLd;f~TBP=#l;=@Gy?-kZ<&)=u#6A|!gTfYTnCmeEQCX7vL8JPJ= z9;52t4OeDReVhG9Y{Tn4TnN>GtvHZlQW=|Pk{g?6RC2quxf$~{DLFa! z?N0T%Ya`W`*G66^m+b@0IK2`M>pxD^!+^dHnHwzc?_oD0`MQ|WPVo$$sEED^oSEOX z-dq~@d9+gwn-zKRIB#c8;9`RFVKK9CSbs@B?&qXDYdDxH-tgt;3S{kPGGLadG;-^$ zh{?ND)_9!y=Z0u;4i@Iuq?l8!=&6s^}<%0*)9xQ2UhqU z7J{TG@z<|k>uyoXng=$KP2yN zY`1GQ|2rxnsno3>)nl44Q9utkG;hI%44!=^PDEBD|0ID8Jml+<{y4#^1Wt-JU0?7Rh6)XYqdb){YaIY}&?F;iAtTB+(%~oI2AsLq~j{3K1=mQ#jWT ze@E>DQfu_+y~F1ZfW_?*T{>i&W5m)CAxTFkr>&)OK%8O;);ndI_jp<~DBw7UPvodx zPimA43sONNVZKHXI+m4^ynJ8WbMn&iVcXrqfJ6Os)TxhQywr~)e4Q%bqbNZ7lLKZo z5wG5~doNFPh~BXEr=9(thhvGEjR*11Ks_6z+MmjPl?Az^;Z-*;`nn4YRJ)W}ReIz8 z8%on^&OdSpj-V6lJM5#Hh?6$ecNd>`%o{X68W-#RChvBj=)!K{4s0vDb$Ap}wSL%P zR-;o1{7~xT3SQ=uKxlpO44|?Lf-{>O=n@F4tE12O`%1Ig_E-8|-c8-%-7H^Hw7F7% z0Jl%oNs~?VO5UAu?|3Exl$x1Sz{95Xs*33_qA~8`jV+h4pKd>H-&3x8SMaZMzW1DW zOc0T6`;a_VSE6+QaC}K}IQ|xvxC^{Z*K1RheI``52*fTm2l}6CZHvqZSk9FqaKLvCG9?#At|P_dyf7DHRaoF)!g zhnfXErq7`DUZ@ zvLI$tBOhWtjL7V)tm~im0J{QqfA?`1i@Oci(|CfHg(VPoN37@a(jh1f>dl=}SAQ}A zbKO9RA8#Qml7FezYc3q|Lx@cNXOnV42e`{umY0sVejWpW`|+hOf$qZ>^XZz|@&WPF zjUEc$WGWc=d5(@KvXZA#8kybOi(&8{HZN*(|^5D$sOCNo&kvdWw(VFk=_bTGJe zz~fO`=HkZvQM{LS0YDt}(&ht@>Jkml6sLxAsy&!S>jTf{hu_eSOxPFCk}TA4X+^geZIi2KP#~GB14x@o@o?7_2C3_{?A*U z=U6N*+@z-dChMfs8=n9VBVSQ>sYcTBI=nQ=KU`1aUzsrF^%L8JC%{AoaH8(IH> zeNqpiT6jbDARr=w%wba=YMg4fTwTDNuQHurDdFiLR2c8Hz|Ox_;oeaiu`5%MtW z2y_$<@qnq)F5j%s9fK4WmD?{ecdssP9^M|Bw>K^*06A8LN7P}0r57!OzDiVzTz)%+&CZ{z)BER*;OP}jZ@Iu!!=GMoj;tpKi*XoVc0D>LY z(>?~vemg&0K!-kP@vG_i@VxD3PEeF3993ZU`&(KN%-x*2v%S)oaZam4#N#w`xb|Iq zm~kT_Z3U(hL`v9;l=qal@VDc ze!95ZQ(|Bg-xdBU0pvXSj$y;YZ2WML@%%Ht-n@>p0Kt$ts?@eRq0H;oHr(s*@zLjI zX0_>;hv#=SC*dp+U^W|z%lj#6nmF<#Zz@|+~X?p8eP z@}(-^4gtFB>713@l9WEW=K)kgB52mu9WGX6(>KFwQMOjj!}>k+>1ue>r+Xd+OyK#^ zCd*t!YRM(Ve8AzGFuzOfr{I$fLB&LBK{fu)eHMx;ja_DX$x+jqJ^S?dcW-tgLD(2- z+@mL6!KGWvZ>G1UKL{*`Pc=m|Hr5gA*#%r-VhRnHcClL~Uk#7;(F7jAIo!~F{> zMN3S&t~_=#$O##o*GA+*|N$Oz8wQC1bTG-bkr_mK523!2{0kk3{Rpra*aST8@g zC#V8RSd1%m9;pdx{Ns06DYj44!Tt!3@2`~UD1^!3X2(&}a zY1JWacJOvMzs;a0o-b%DCeN+EsJ4-A0{r?s2MDhhCgghna_fopgu6WDadv3_G^bB1L7a$dMh5x^AkY{39X5s{E^J z`k-C9lAbLH{0b{UmnNltlMp`3cK6e%%H8W5Jg?C|pRjrbpv+W>uf$P^fU%Sm-ca2Qe` zXgn!6Q!=Kreq;0+9c=NMjwma2jTy`gwnQ&vF#s)8g2lsR&5Dv^pxQs9HH0nn>-AE6 zh~H9+NDpZtZAEpOaQK&3o#dDCM3q!~Z;_6a@`qz%PXy_K)Sgt0&WR4|6pg!{(-L3@ z>UK{}(0iI~k#jdNb>rk+%^!H%o4Vj8&Re7~QYD2C?z-r#`21j*SeI>z*qFj&O7+aG zifz>U3au5bxYA>Gg(;o4#n*5AWHICf64uAY+jM@~69I?*B|ftCYj?PfEuNAFJ21PR zuL&CZyOe3&coj9OihRKK0ATao{Mc(S(^p*V+MCmo2;VtkY&))9P(aSS)pB;@BkW^* zf63(AB8PDqCi$7J&keg^EJP@JTd9o6bL$^ugWxGcxAFRHhoW$R?}~KO-Jd_ja<<_K z3*kyRhgjWptP0prXjG0Y)A8ghyHq|DudlDay1FdG#T-D8$iYwB(qLi&ueqZSD#B4b zY7+wrHRj%z; z{EDP5pI;qU`rFB;oWK_5qu$OLzDtrX7jSgI>)n#`(jZmEiC~-byU`mkKe|M6m&Lfi z;-fG3W%|Y0_e#^|-F4yv6L@~LM9)87k|5YFQ(5MJOvURR69kNovxEM>_05&rBE|v7 z!|Q$5(=t{k{^9jo76)4|r?(`iYt(<%x4#G7$Ue9*pPfA9vc3OUr56M6l1_Ut+93io z#>45DGYO~ec=D=dI(5KT0iyU`qrzi$1u3n!ZHMM>Ny3nL4e$2Dmq4Oc#-3==!#hOo z`V$sH3RZ#i^>V@ciJ1|_k)!UPQy*OG4SC1Nbx^6_?hv5J#B62p>kvxE?ALF6*S&H0 zF-R&3E9qy7*D*9drs_9A#|f`0{6KQ@%F48+6nNM1en21}^7Z2Kr!Tto=DZUH+{Y=} zTXBrm!@KjTw>d#y>m&m`_*7w4#VJgDo1>X$h;QrRQ$KccRExF-mRf_H`oHjX;I&^mvjQ&NgctmlcE2Yj@5N}-eLoSW`q=!ayvH}WrGHm1E|L3+z4oR0 z-gG}TlzmDa+*TV=iNyXseRVQ}ppg%z9_aK*QqFHh2~oYbYl?p-Ln5*m|75jj6vpr; z6b2o8diJJ+d5`Iw3GBC{m72v65p}FEgS+OL7u&o>vFdL{tnjlH)Xi7P$I@9Dp9EBJc{&_RT;-vIvDOwQ3UGUq? zn7){AlKWm19DKCGHLZAfr|b9Rt>~+C-!VO^bHPitNhb8*Oz}zS+><5I3~(P9;nL!D z)VXRz&5o9({mA1j}?(b@=KF3I+WD&GAOU4l81g32D5MA&vt|}m^ z12oZAk5hkm3vOlZ_KlL<&%cIw3>$>@y-0z>q_R^V z-3n^<#ma-b9(w*qMRYyq$}g>ddV1E9k)sEn&^%0-=1&-!Uz_xDv!F@Ofw_QU}Z=})iLhb{I2`_;QBUOn^RjUNy4YeTlXNgD^Ixw0eH8aCz?>1mKzGDfWx)CtZKFH8}=z5~Xa*RU*sIdd`(yTCbc; z&%YQiIzBsz$qe|3 zPEmm#{aEdJ+mW7lvmB$-PeRtdzV--j5#`VgpyK*kdrnh#PU-9p2lg;|pD!M{RhhK1 z^z1Qo4q@t#fyB_@;3q)|5m8F%e2hrf{({KSU&avs`FV%=J|QM+P(0$2XV1)?7DV%oQ3m%4XoG&ep%w|57pa0IF`M&K5sbN7+tU%ZHhds ztr;S`_gb_&on0AfK2;+r-FXeQ#K;@x0&6%<7N*^|7m8)C9sRcLp|^*Kj=I<4WsVra zKKH0(OG{6mo1Gk=>>Byvxd|&x55m5iFb~*gVD$(OJ%$bUnM}gHJh&TBz-A>rg>Q9H z(3s#3K16&NX9{2g*{bM@vKgWwJk8URV*k#yJmdbEOPnCiuSF(EYRP5{zWBAT_ihp` zLedYfDj7@-35hiFB#bvYfQP>}W|TTT;0@;U!>~$qIE2>P(m%c07W6i7o$3JzJyQ>h z1+Tn+e12(5sMP~RZOp5cStK5|^^n0!A(`)BT2f)(8@#lsU;&JIkTSrKj9ZJjKu@o^dA|rby zHvSrp)xsg=u6*nJ4}l0|eR>bBa{3~S_z+l0NCs@Uj@VVH zV5iyM!u;U0&thxz$6zazf#MaF%-JTGoxGbv+DYWwgS?;Kk4v7uC}GI?s*csf zTGJ=#7_gy}*rm@u@O5&poc|@RFBxz%`(Ck=e`lSkN#>4506fCha{8rzcU0-d>I4ez z`%CrUO_Fz8YK(oJo0G=^Dk;*wzmpxN0>i0mPtWC>wYvCR`6V(Ucoaza-&^+4-5Y1& z^lo`Y3P|(K+kV33LJl-f?$^*BLqzFV&8&Se!guKj0h_*Yt{#5hPtiW~U$2V0F%qvv z#t6^K@O;=WP_lL?n1@*5ebhN)WXPVD8tzW|Y}{O@?l0%RADhd7&Tb7s#$0MTMmFdx zuxzuqAix6P&E{);ehXw>+A06LtBMnJX!j<3fcO=6QuoS=uYBNwoxxRx<{&*q>K?xF zwW`ndyZ9{bg4t6&{BQM=Wn8~kPIlkXp@WqXYdqrY{jmlqwsd&d((nXg1Z8{O{J}B* zq~b-?uf^W^wU}j*$n6%Xy{A5zXcf*}0>b4pr(R zq??BlNp~DQL6~T?0?7R*<LoeNN5dggFj zvQQehPKX&KpP`1xJOKmG+v@cB9iNc=`jqe7=f1{)x26Z-=d!Un)LVDF9TFI+{#bEP zKdxl(b|n1urGPp-#RO)9kceCMVI}zT2WgVZo(bW;+tyQA7CFl-yj-V3y5>A7?ZYc#HSW^8-~6TS;m!ygK4e7n;BHM{)P=7!-rvjfo@L)Z%C_Dk;uhc6 z%Cv7rxeJ0XU$16v?K_eK5iD}$qabwlE8p)0R3~r?m9%z${H+pv4D2M2Ds9JSnSzk* zHMprmN6N-jQN3%$d8Q!|?GlsuvkIce{T38%E7m6!M_*HOizEn?>xm9y#4cN>41 z$KNY|(VW`cIUYau6gfHlU9pL0<<4N8z%r#I9wZgEnZOOX>yXy!lwbDZ`A6>=XgR*| zh^NL`8-iqsj>BWd%fm^mp!vaXQ}BQvUr}5lJyp^_-z7Qupo+RH0`A7E9=_A1F1c{5 zzgY4V6yefQ?0YGV!AJVvKCGyLhVZ-1sm2p2MXw!OVqK>`JuQiNXRm}(*7QynPs&%r z(?@arc9BIa+()tLo2Qhqeh;^~MKy+1RQ+!u<1D4*|~k(jMokL?vy5(2*G)R~k6UNe; zbvv^o>CX`?cLd&U5RjYuAKS}qUN0Oeu9mif$U(|2ZJYEVE_#Mka5xDbQg1B1(T59l zyeO}!lcOd@HQ$M155ki(c>u53dzb_SL84u7I`bfZq`fwr^{aLXNTAX_+7>2eF=Tl?z~0n) z1Z!LYhK7_d_mAmly6Am&RYoM3nA2MH%$zWdC!$ER54QKVX!Z%VFrl>l%4 zN#x~IVf}CC1|w3p7%J4cGH-Q43uculz_qVmtZv}z!Vfh^0%Vi&+kZHeuebDWJk)`t z8i#CUv>6!-mj)RGSrpRjV?ChF>Y^ymdVhB4XYFhSKF%fC!>gq$_a2#! ztZ=zj;WiaXW^9ZVM)RtgDAESE)+OGMvQa>&4s^m@c&k!DiDf(!I}C-jc#}R@pUqc5 z^2X8y?AeIpFkAPf5A`=E@x)f+n>698#dNIu++O0MV2RtjMcaF`Z=PR}0?kmS-vsZG#oAkq--1rgl6wa^~a#XDsH!N2+5m=x15Wf+^o77g z;Kh80_jIfW_*niO$A=TOn$skS_S;%A0LU5ie@mYiXx*~zHk@y!Q(desxd6>Xf_qE5rndYO}U-Kv!n-Rl9H9~kHNCv)B(rkY0mpFA6O0Vr!$N>?V*Pxq-#*M%HoYK^k@TRk#rjKC$1!4ng!%WR$B*AGd(*#X-oGf2a?~6AeeO87rtk7cB`BqnqXd!3D zeGzHyo^4(Yx(;kHVy3k+k!ZgwGO{(5@I-(q3I8N{n}LEP%TyA$jDDyf~K zU9Vsou~>6B!Y5d+G7JRK5ZOEuTOB_pg`@_M10jfOcH5~9+2Lu# zDTvf^&FiX7&D3QiL)}C@i>Raia^X^tOJL38sp**Y-Y4o1xzEj+^I%1w1lKry z#vx5Dy|@ZsE-AEiWb*Qfqv}`wFe(~$zL+G@)O1Sd`K|8?IUprfmx@XBgU319G1ak~ zYD;$_UL-;KpZKINV7@aLaYLPOj)^vw zt;CT|>Iy{S1aN5K={~q(R9@wj84If=f{d8<`?37}R_MqdDWVRAPpkgCZRa>yh1zkG zuVY|j8O=F3=ZL+q*xRic1VrAr=N1UGOT~Fg$k@5Wyk$_H!I-Y9m#KM&dmX-R^-W^q zFw&72sX`*tE*xp&s4H#OXEwARufv`|O70uVP61)?R6nsiLNeyhA6)nl@FDH{Gm|Dhj+r*EZ2l8v-}%D+aL9g0iNsqnjO`*6yy|zcr2`#- zr#ZzpHq$By$&eS?Ft;5lI8O;1J5T7rPK^AU2XzV+(+WbcV%hmSkCc>{;n5JCYNnkhuPjp@6oA5)SvrCGdGQ{~dY^%3^Qs#j`Xb8ly%MoFfuT4$%^^MlOeU0)OZWfD#)- ztf(Cp1OI--$mpufRMUh?bpUq2e_TPAxl=!tw68pFy(G}r|#q$Jd^A9Sjs_Ybv zq!h0_&{5m-Xb^n#EkI9t(weNd@&UdSJz@TP39dG5n4}9mLDOsOVsB32GiKA<+V=;< z;ZU=!@l>&{&m?=4(#mtKC#omhS#Aqt&YzykiP-Xp&a>HJZ}{EH1Ibi%d}suQKDap> zsCoDGlowGMg3p>K)YkocEz-00!3A^)ebvh zt@Q>Z(_Ul@=94WJ=Qzl`rwhHT#yNMu502$4kgY(kK^FkWjhRJ5XPuLR@YOJ`6E3SA zCm*NetF}I49KTuH9H|YVY%jkD2eR#SL%ltyI=3turoEP7I`*yg+OnI3vDjk@NXR5H z5fExXN_}|D?D2Jzq52rkMzr8$gpMqaTazqTVoQ!QvRS@Pre;r$kU|(AH@nw!;?t|$ za!el7Orm_hmVVAICDszB<*Q9wZ^PIMe_G{y`2qZjwB5<;18Jxsh@fGHy0MQIjNENtZ%IH+4=)FDO!UAOzdk5kthD+K+IKI`Ty8Gq#RP2H|u*clfmZE+^D1Mlc_ zrGPAH-Yrv^t)ooVHyxUd-IkC_j# zgq98a8Slq_(QJLn*mM(@7$wiTLj^-Am6e{&bQ-T%SSbfOZ=x3^KDVtmjB4as+LIx!@l_Z2HM}` zTosP>BPl9{RKGwhacru0l2~V2u7r8QY~rtNK=tO><{Bd^?;x_6>ulT?oz~^rO~1zQ z=zvd5eh_pm$)7&q|DQC_hzz8~Rf01{*DFE662}U1)#D(6)$VZ71MO;+OCFsF1-aSl zccPfnETP$-WZ$Lv>2fp}v#km$);Po#fj_#S0YYb}^t#3PVxttUGE;*EP+mv_i5eAl zlB9qOz^|Wb_M#*#(nw#;1|QCZu~9ttR>NjbM9pb{_OS zf?@m5bZiKW_9miSH+;ez(d;l6az?vP$-V=6iF_%zJ7r$19)B+BonE;vb(Vo3n|9-c zdxyLhrcIDB&dL$gQL2JUQZOy$4N;qv!ml^H+JgvjXiGhDIGdsC0b+^*5~C0_b>=Uv z&v=q(^lo<*<`igPKxO7dqI?gvb7`=bX;JRJ_Ge~S%Z_hd+vL=*@$$Iy3M>HCd z)WSuKcI~}yiDRTd-E*7n=0lKATKbt%BM2YvZeZt5jlMr#&U4KmCAz*)d4Q^z$2m5b zFuJn_9#aUslQCCf70ZPn7ZivBDl&UKjM0O^MUpFBzi?#Se{@^_@9rLQ!XB-tBaHVS zJ2l4B-ClXGsJnVydg6K>dYqzDx=A!9C%`tNDljzs@n(OU_#KO28eRxqhT z5G*>u1catarXnh|E5Fn(9vVB3+Dn|ca<%KMqXc=9Oj~7?=HBAnah!5*yVk)JQD7)b zH^1HFSsXbzQKrkrYIbu;|HT%9t-HZGf#-s~(W05ruJ`HL59HDNl(kWXGa0Ir@02k`wb@;#9inj8;_Q1OuE0s? zXY{jlOb~k1`j}esqeUA#NJt>pJKl|*6Ib0^Jk8{h;!Tgr;A0`4ds+*OvKUBkiN?60 zqCT!haZpEW+ShPKpjnx;S>#^I!9Ty7bXFpUQgn@eCy>Nt2{tAZ6fL$5-jgU+-v0 zZS9sl&$vozMRBLG8Sxk=W^gb^4CJ&?+mm^FQv3evrvwao=~BtxO54_*A_i^=Jt=kX zw{QJ$H7ss(vUvmQ1!&9)rzn4NjPEx@IZt0(y7Zh=AkRsIbXM{r$?UJ`CgJq<%VUK> zTnq>r+A}pmAXjr+e3hfk^)0VzFH>CY9}wX>Ctus?`nV29OdaouSyzNZYn-s$hJPEuI%Zfpw6uh*T=;GWHLxm+yJq* zQ+i=Pf`(6>HTZEfFJes_WMVWzcJrc)FeIC?+s5rbEMOFxt=ER;84qCp6pL2 zFcm=SueLwlj%)W@6@0uuD_VAK{%HkieQMQ;iN?9SPJ?+dD-+f3Y>48kiB~gD*G}w% zY{@xgl)A6`Xa1DBk?E4Qa$Nzmuq^1Q^Amfznt?W)O`}ff461=)%2lTI9wV)lgN2!Q*31?TjWoUPh5S*b#siTwyi`V1Be6-@6CSZ8b<*zwLtINELov0ChM*G)r>pfQ zc+Xgz+VA%Z7vkKVQbqMq9MaN|yyqCa5R1C6&bDiEZ!?(Yag~Uf;6~MfTSd_dd>z65 zWdok}<8AVn%{c$%g1CyV9tp9{$}$m|GnK`*7wh{$+1pPT+0~iPx-YR?n7LS)@JCS~ z>HCv1N?^3JUzju{{35$z|2ncd&NJ1BS3X`=>>gkM+aPyslxnNA_jud5%oP0?DXWV2 zEr(UK%$GC3(ZVxV#*K}QU0PcRWE_`1m~iK&JA22?WOSh;I*2tIzA^lxzj6<-`T!1_ zvJv1aEw*})7l_9V?D}nfO!J&-8E9?%@rIX$q%A zo1Z~R5es8$_h{Zg>SR5br~Rx?mQ0z*WMXURPdpZf_Z1{C$&fV0z#E_KZ#zgF_;h9v z)Y{U#V+W(^S*gNroO%^3Q%s3YT=|%Wi#zQ+?#*N)g8nt;28b@78mU18_71HrJY^j3 zxqX(+6xrs8;4bos_XD?CUncww1>Qg|NSYbXeYb=(T;2AH!*bm@fl`ml)HE<9@cz zTqJKUH11)tw-QGyFcsmIPwH-#C8v~&GJ&bOobtc*ID0wrJganSu11>uY8VE;g;#;R zh%vyVq(LFhx|F5iCalM|b`vE&a=q<}4M;p61__;^Q-;NR^p&?&c8~K-d2Wez!FGre zc>uO#-lKIq9~c_ezO7eEj$TZ4?|JVc?%3&r!DH~svzt}8Xi}7698_mvw>k$d0`{`W z2z%t~$Qjw6yq{_BVY2NM9za&hzkHn)nZA$nOj&PLbXq?}9g+dIKEU2U-OLiZ>o_7f zt^oT>FjJeC6)@Bx4jL|D7?())nz)k|h4@2fxG{AnnH-aEP;vM7*&ofOORJT^_w8|Y zk+V(-r;J+079F-2KrsMn3T^mzvg!2`92VoA5hQQ?50EvYD&H%ISt%eS9EQz?^hWBZ zd5Z{A_(v(q<53bm8e#0Vt@_Oo1>yn+i9x0ZX=BpPDc(#Ake+Q^IoK!YX-Lt_T7zAM z(DTeH1G(yPzBB$fe+11;_H~y7K(!Z8mAjOPArz(Iq<(*h`}1kZ^h21$nwjAeOBm$E zWGB&8&J+b}oepTtAaf4)PH7(JtksOG{Px(85>UTPkg(<|FE3{r=}Z%@xam%Zny)2# z*=4i)`%T6`7*iTlw1-rh{9q40YTVke=75#DKizNB!cg8bDEYbB!WGXXpUq+2+WoXK zGUuQTpP6rRDV8F2-BQL5K_E#ODsmwUR|iZ6mvKjbG?6?DsKZHQLjVP){{m7W0q9w{ zGOBH)Q;6^J`h+t81$Fd6;NAUTTIN4k38!MnC40;3=<=GR=F~n$G_R&+Y(rxsIJJJTmtqv zZEK&7-5@ys`~hQw&HRBkB1bx5e7{EOH`zf-T)NNJLi5YO=l`F3%#`eUPm&6P6T6WJLDFv|UZHweU8agX+{L?>m4)7%+K< zfu1T$jK_@*nm#|BPI~3*4rpJE<)nDCoL7qd8HP7?$FnZ?)Nba$PF4O`ZpU>bu`xm~ z^u5+3(|7Thir`A_;VF#bM~$?~96x&}UOjBtQdwQzLcaC3}dW#Wl zxn`0*UrfB6_a0F8KZB%{!3tuZpCbEtQSs^%J0_Y$GI)=FJ0UDi>MRl6H}(J!l{uQeC-Ldamjuy^ z=?P%2e#f}jDv)qFZK4Mn7n8m!5s$`WdFh3?qJDqS(a7OS_erlv5$x9%sv31` zu(>$rzD>>U%>_H!p8? zqKPkejLe%xU7*`y?^%~6FT>1CZ6dsE53EbNK3U8#NMGZ~4Ngkgszik)aunVDF~v@9{r!2kE45axG%wDcY& ze%d*3l}BVYN21(BBzdCGS?cL(a0hkbJes#1*@kFBtiFV!7>ZuTc}P!7`xrKk2d}jS zJBCz-)!I6NTUhAAWqSvQ%HqUvD@R2MD%8|*?mypd zP7(@Q8_*hzSJ5D^fx)U4oGZH3Y)T&ODjZH8XGh&t8MZlGf=}X2^V2diidbo&Ok0}F zgwq)&I}hl!KSo)sx{mi5A50{^dT!{naAgboq&GlIPOmzIufmgc8nR9W?EL4 zMCZ`m*ewbt_el3I5|qxXp4Lv&WY`m6h2TI-oy-N0u_fz+Mo{&URcr>H80TpMEZa8; zr21VZ`{u4i18t>q^N)Rxlb4pq5EDuX2?>R&q@)~-W-VqP71bjTb-50`A~yuuhnuL{ zVyK2On?=M^Qip5jD4c3hIB&y`<)A16Lah>e#=9o@9)(ZYW=IDtSItFr9!e0TiS?w9 z9G2#mR$r1r#qp%J7OpVd)s4Yoa-z}kJ5sojc+UD`y?f7VSAho|6!Fcb~o5h+dOw5P;YH0Vi($x(= z_^ryG(=+Fn!-yzwADm0{+h z*wxOyLL5*6nc|PYHRteZ5|_p zO__92^II$TGC#8gtj*`2^Yf)*$jN<$%_iS@&_!M`wTnfDA{lb0@vRv|*wVXsFsKKG zp*JGv#8|u^4hq&GW$6AyuldX6$n9Eu8!OU()`r0IXM=cl9s7g;>W(>SVbLXtY3;vt zNgf0@J-NGBHJ3cNxnp+cyQPM0l(DKBq zP22sxD?vJwo{sL;L>bWXO0a%=Gtu9!xIX>D@^BIB-uiT+@4<@t?c29Ijs{1Pk}%PT zoz1-`L=&7v=7IM|W>4|7z(s>wPg9&Gy|4It62lErCN9h)a!)Fi-+<}?nR}ohIW`*# z@y^tLtG&H}zS)Vt4!h7u3Mn-XZ_lVY?z)aY?@kkuh#~hr%wY&{F2rL;psogqD8DgH z>g<7}t!dyuJe9oEpw@iImoD%0jP)PV7M- z3P_rIM3TPA$cWCr^-l=^eAaM&F>a4kVMluoC zrzb}Ojc6iOdZ=7d4XNK0UBMjX7xgt!6M`NaMyg-9LN~WIyhEvy?X)Y&%fquAQP!(W zdEhl#Kp2&5dr^e1zM_7U1`q~-5D*~dMAvwgo_%7Zo{MRN&~$=9XWb*-l3!l_b;_v3 z|4smg;J4eHP0bY;qP%Zi3PqM2x?R5IEtuv%#`u*8BjXJ86w1FfN0cnQ^)F2eWt979 zC!Sx@X^ixWzVxJw^0wS_i83N*)GpaFE7afJ_9Bxz9_?D%~Y~h_n0Nyk758BVvFt2qlK| zM*mCn9lWu3q!VHEtY%4QhY0;87Z6{JMbHly=egm&ZTpZ+`5idwLmK7F89sD`Gfzsv|?`Gxp zIWSd0!hncDY-bw$+fDVauD=X&vbWD-^f2vr@0C<%4a79$Gg(4S89fa+R+%;yB#HXx z$uIzy^=?N3rP$LPTswcCBmy0rH`GW{J4{&IzHz5FbrBJNL2zn@jUH(sG{s9+=km?p z7xzW%PeP)}S4keI17e$trp##HEM||+;wIC~(avu3{zPtV*Ggm_Jab|n0gdqDf&p&> zQS4G$R`Mp+i zvU=QFZ)i(5;eFlB_tealA)MU)%)hcAMg#Nk@USUPMBedMu@Tx4ct#W?0K&MD*Rj3F z={X#yC7ZUbI+xKI{%X#3M`r`1vZrXNL@^bknk91|I-a!y9hN#6OF2W$I9qQ1d-OBQ1!=B<_`jxTJ`Dl`)UUS13FXCT5^fa=PVMW3}nMisQUNz!2>lK~r2|V2yV!a|% z;x6b>O(CFR?|he^JZpST=IitddDr=Y+r2)6JfVMZ0h&@L9UK};>oH;RoT2M6TO?k* zj0Bp3oS{G=N|I)VXp^KI=<5#`S9igK8=MLh-k*fXHjaY6ZEmnW!#sR<*W-$lNMK!J zxZ_*v<7v5d*%Pw~^t}wEkzqQGkqZCfB)MD7S3&D~z=A{9(Q__tHIe=>Pqe*Rpf6zgfho{*;0`yI8! zT*~VaCwgZQpiG~r<3{dEjY#c_Z3+LAPYh574{@*_y^@z)oT}Lvfp)L!U-9oIeAbY9d}`;MKplnR05Gk94+)#ul#?+wp6pm#aAi8bNoP&SKy3l= zBn_X6Hxft{U>%th;2N5kP!m~c$kHU@l+D9rrQfq*SlJb#vZVIM!uLnq1>QZZrPbBd zJ^Zs@>lTgkuWIR(II*OT3q5&VT=e_*5=4mkSz60=LB-ejVJh*>`-m(^Sw*8g%pK z%{&TGEdv8(i;Agt70ig|txjcBk*w&n;lyPT64t2i8Jp;g*e1f?KI>pLu`o#r7fC;# z$KUxifdVil1p*efpEkXX5>VWBpdTk)pHCDpR5{1Mmw9?;Y2f&9$F~3b%dmbDCCVhR zqvJxtEA4FCuB4Ka!)$-E|CV=q9~*s}BRw)}-Yqa`h?VW1Gt&}&3Sf?D*@fzmEj4-x zU;>8z0UM4o!kk$qSLvZG*6cfHvG+Lvq`cD5hue+D=6rSh6Wc6#N_7-{NE$3FcGwgW ztt6R&@Luj~UM+soWI&k>ViqrAUrC#9>CqFdFDkl%L+%9MQ2v9%!>pYq>6fZH=Jbty z&U1u=O$#BkYPm&0(sz^@U$e)7j0_GMQqV*v>k{vyzOmc+c9(EI-77<1U7LLAnQZ4; zBk7|3I&oW8`@VZC^7Xz4*8QPJ)R$#Wk)8h#h$rjIlnqL%W=#^rO8M@}a4eriHxa*D zAFpa({i~v&ILn3vMNKi24OK7{-;w^<7*$X>^A@NPlw{5pevbQ&UL!;oD)g&JljD?V zUv9{mu{s|Sr_s`vr~M}DUS1jHMGgY55xEdYCo1~%z3r_Zfr*EC-a0z8E#}E%=R=zw z8XKqCvDJ8F$Hvlp{`BWRvYKswmNDtThd9C!9af#sTX>-w< z8PZj@tp@5HHn>gelCv#;A9n+rqXvgXnF|Pzj|}z~q}RQo*<1AM$DcCTm%hBFP z`$iyimc2*n@ZPn@lSG!56F7yw)u}BmUHm-UKn+kz=88HWLB2y9=6~2|Xiy+i0%a86 z%O994zuU9w%l;a~g&|0|Sp4kYz%tbMXt5M~GyDnP`JDN_Wox=~n9JRdC#4?dch+h2 zDwLf3aU-XR)mFl{P5+z#YQPQDI>v6r)W%hqLyt;4p~Zo3O#4=2&Q}MI{fA$A(hKlR zouB^x>XBL$N#i8wo@WI;A71X9^}e}p{=B>p6^BvSrls$-EjcewG6B7XkwSDrp*Mm- zk&`L$wBM+Fn!|ZFcp;W}Eqi@QgC=6hUEVM6veKQeffGH~iPT z6#sCn(lu?S7Zx3K5!A@*x<=DxqYq+#Vp7(lcqp8@tVbd}m&`47AOPR?7%kSqRlvU%8B0Mgc$aP2S zF%o*q^#~)1SLIK=AXfmRp9O#aEvKO&UDL5kv3_;~DFe0*tkOY^elMUMUTiC)1=2Pa-T^!l43%s;dK1@r=p|LXblQzqq( zewbuM_I&$ur zjMTsMk+j{aWA{U#AIO<|Ks3QeE?zaabQa5QRLsL0S=$9r#U{3A`Wk9ygTF|rF73FK6 zegmNh?~(tm8`y#D{x5#v^{+P1ay<@3v}3L{ZQ5?Js%YFg_y7JO++TUP>8m&@HqPaF zA%>!5%4X{|eWx~D36df9Ax<0(@F_u6Y0vqhtX)aM>-B@R;32Qdu)!?@d?sckXXRgz zLVz`Y%^$F7=h24C-Lv9f26{98P-QCxH}WnjzGL)$8`~KNg9BD9|2>ug&1YCA`To1O zrBd+_!8$!r|0K)~A4{Ja>~8V@HS{t@H3tzcVsIQ0+xZNJ``sCVk845e7s!SJ5MO3J z`fIZQa?F7-7>{ZJ8`taM!gQg$uZq1xS~9q+H^lEA+fttl3tsp;S<1-_xv_%N+wb-t-w6cx1s;5pg1p_8)`d@`ZacRBbpe%&R|YF|w^&$T8^H@hew%KBFj zKyLp(BG&i2cX$ht(4x!Nnkg>XO+t^f+)A2`1p3j3ATK0zeT@Y+7 z^S&R>4F1)o?dz%(OA8EtAMDXT(}f5hxxyt3{O?z>)5+!94-@#m@B{wHocLEKAC|J= z!!RS?Vo6nSEN$R(v+qUtmdD28_w$*+qW?!k+KevTOaC}r-jt54Z#jR6q9FupOGma# zok?T^2M(AB3N<%pg~q0(n-*6+m(R)2P}60X`*Kdn$)dmEb^NmZgX~qMCy4z1g|+E( zVOjaFX4lGurhcoQu`*o|#MQ@AzAR#Cai&Vx!c%Tvz`}ppg`h6=Ri_BSKb?ROm zuR>f_{Nb0vuf_T~E%-GN7XRH=Z_aw9pRTo2k@FrKN8c1B=%qe}B_cb;Csnn|)ua6)RN>0l6 z&Chx=Ts0TBl&_9${x`ZSRhJxSRo)}1EB}=ya8b4O75hPxXel(>$#v2B=p7^8S=osY zazi*Y{#SJJALr{FvA>ebI-h+Yb{(T1n5f&gOvzczDSaAyo@efNR@X03y4;t3sW|C5 zP>#)8{V#WJG|Vf$zG|zN?pzBWFyI3Y=~1Hjkxlo9eE~%7$P#Nz&9er9 zy&_8H`@bjm-~Tk;&yC%peGj~%lMh9Tx)5dKDiCf`D27+hh^ryEX6+rk`N`S4%gZSS z|Ll5u!_m!G|M?$z6yZMoC;0?P@1P6+RAt&KxYe;jGsd{blXlet^8 z%c}Of9(gavk41)KuJ!(ZYCr)sJuFG;ndQ??IKiu97%*dZb=SoSu_l`u8q8d ze)Mf>)18|;KqFgg2wpi{7J8%lnZC#g1D_0_%~;Bj+x#ukzA1ycs_&73Yo|gVcxP}w ztQnSlDD2C(`#0co;)&n*@ry6rM(A54*~f~TS=hG+$_6{US=}i&y^by=Mv;k*C3GEb zZA>b!BPCweFw3YMl2`t!+|<9?X?`9Ig%mP(c%;2n>uZmk<9OZsIh1g28F4F6IDy3t z{*p^A*M<1}|LjlZIYn(Zs-=ym62;yF3?B-sL*vV%7gF!3I3rB7>kKk>J#V@P@-?xp@hg~^w)Tm+^hF<$kelA zzqI`y=AoMTrv2*|r{lsG+aJt*`S8>?61>hCS86A}p^+FZfq&E`GZl^TXs@gZjb{l3 z4uZ>&T?v*we6{^_SuhzNY(@jGczw|1;7&Pu7v6%#vF-}U5Z`Z=KlI(@e)zbSiQCHR zzh#k|kz~GW$&RnXEk&iXu`c#TAih*Z#6NP=ZJYk1+$hHgOYkGgBC{`Ol2)y5L4gdI zTlDJSMHC&Pe-iR5!Tohz<9UZebFV+sFKRvF2zD439*Lt{Jjrz_<1ExJFHZvcCvs;> z3)L+hIMdfweaBYzd>p9>RWfkp<#u&vcDa}qSfoZ`ZOPwJzoBQxe5q z`mwB7)-QF|rdDoDOEe%&1#mM*f)D6`JKI*E`VLx5)jQI;E$wTH0 zUB6AW_ty@z@_ZG!Rx!>76R$~82fM7s;?ZL)wbACL{%>+Ta%7jFylh|@x%v`}w z^qJ~-_jv#bNaK0upnnXWJC9xlr=?S`q*_edX2*LE`2vEJCDsZ~S^y{6g~P7xCilUT&ug~z$i2%#yGEjaynA~z)$Rt-x~gcWW5|1Ljg-5T}7-KTVcp~(92#A zEAD@B{@+ONpcyaK!pi!xE93V{S`uSQk1iTB`$9$7UYILdW&6j}N@D z#vg)f?`TsF$2QlFNh?F**~GWj_CCu`UpMSqp8paqLiDv-7dc!-4+scRG;-hBmHRBY zdi?=UzW>L6&y|@L(#9f#3F`w53#y{H8jQ<>v0ablZpuTCpsnFryJV?*$zjl>GRjT* z%8b?7gVaSiW@2Q)-Hjkp6#G#$MwzhDkZijrJg_)@uGO@^+C$g;9Gl3+Ofk@hEJUm2 zrQU;NkvLL9YvJ9$w+9e3kq%X%S|&X=Z`EL)W$9|piyn!-Ltbp1F02i??i=waE_dcF*>S$e^^xY;dF8Ah2nQ7 zKUBi=*5L~u9(Fo#PdKBN{9cGrgP^VDdM`Riiw;1(QA6B;QbF4v6?3ghWt1MeW|P)rPmp1p|A|2|Hc ztc>bQ3VZ0Y<^0KVO*H-2zg8{g9*NoY@MweGVu#R@gvz+1Ip=dJ0rKgGV)n=N(u_&{ zQ{bAxxtVl6fR0V~7rXO&%p-JZnYU=kNJ>i=iW71iMs{}gYRxwoo^y~`HI7-F&P%@( zT?r|~!9F*`Wf~M=590C1Vh??=Tj~E9{w{tbX5`_$U-b-M@k<8T4$Y+t5@pj)PyB&H zRFW@M6r>z&`Z38%p2H?V@ldfbQ5}hn%ql{xQ|uIB3r-8hlDh~~XbG>Aer{MktGV(o z@>kt~Y2|njDk~jA*WJ%wx56VoBm=oHrBIR@kfN;$lS93NqsD9yJj@4$Y4YDUp>6Jt zlvAL~h%wRdX-(`bF~IQMA;Xj71k_i^Hx6b^0|SqxyqEf@RXIJmb_btgM}2R3{{;0L z&v=82^V+*ad&hI-y;nBvcZx{ckD2b6iY$D#A%vI@XAEC5vGJZP2@8A2Nw;iTOfYS$?SZm3Uf_c+@kjR=x5Q8FPol+xWLf$S^Z{_1{H|eA_g*`lKyt_NF`cU55+Y zRPI!a>b2cSEmuPtwz&hUjv89$WD7l47j>gk_;B_LbH>{y9uzI;!-BkP9zT!C^oScO zB$mhPW`Jti$aYS{F9ge9--aZLDjHI=+Ts~4p3tlNlx}{`^V(Rfhu3g(1W~vP-SrfG z-EK~}_VwqN0jmL58~c7hpE7Doe%w8BKdZZG8A>|G7pT%5y|igv;@yZU-5ux3t%D3gHwQ@Io z7udT>S7a#Kr#hf=Y1jGtF>$jjb7n|+$B7L6 zfWg|0$zx~l=d;tSpNiP?=Bkm|%ukXRr`6K}N89rB`5dyxhj;Xqu2VFwJaU{1_oU+( z{xSU?8MH_&i~e*t7QScf&fJSX_oa;f-~u0>_wA3dVuHu7(hmW(6U4OTU^eTlr6k#nCM5FUb z`4%x{DhKPOv*U@4n8GvM*`0P|)!!POpRuk@{v77?`W5oIDo$rNh^K%&@9dJ9+H47nM=4^FS>LuDt`JyE`@F6$)QTx2*DH zvC<~NFXaXRnN$%WwyZ>tcO!O-(3DoaWLnEEDS-z&a+5nX{{g#FV4JyEvc#*(u3YSQ zlp|3EEWu_&*YqkmT;~8;}?XoS2qn~^;a}&2u^~;KD3lF z6mz7>C(^N!nYb6NcrsZOiXC$irOel0rp>deKQzMmy64KhmBqJpXJkODs{Td9tIO-Y zR|2lev}Qw$h#iOF`dF1#0d|gw`_|g!4%Rg@Z%#2tk+7K(yVbugtf+E};#QB31kB-I zCh!bSAm$`>B(^V(ico}=?y*i;E3WLfNhpJc07gXXu~R3ZLofq!tV;{BeeIabSFX%U z$db_g?pg98;)WMqE)9JhbCinv)yOXBhAtUYQ4+C#N0iB{^R>Z zg}oy3T8)rh)6A%?yX~u4djFk9axQnmJA}P1B-e{+H<2`@smCao1|NFW%%w4FutY}) zi~1Q;C7#X?54+3F5U7VIW+JT#&e+>64Tlq%qL=3OK>4bOErEH!FdY*@xYV>{uTMO4 z@NM;aQJl%(|F9|30Buv67w-|Vz7xgn(H(Al*Q;fNz;N{npV=~cybHY?Fw(DntCV`E zQM60)td^t1=>6xkR#QYJ9%fu>*LC|RB~sys)q#IrC_*1r(OXf72e?}KUDM{lj>|`z zqB{b<44zeT7Dsv>tRIsVVQx<7zHBJt% zM_}h^12g++xd_tY?Hj0}y90y`)xqs;k@TPcp0~x{lljiSDR*s{f9(Ie$lY_LKoaM? zq-9ie#l4o6O2e3%*B$oZl|bDKb9K`W$u+z|>ZM!w%HE9oUk)Z77!m0N@p1V3*c{F) z69q_SkHWnvLt2j^-v10}>pvZ{j|`af#)moI8kObW=bqa@FGqbbhoRjHgQz z$sMxy6SXG4Z!ghpEG)U#`R!r^ZZ|_h?cU`8+zl^fs~WM*hj;`q)saZ#MPz@fVtB*L z9m0Q*CTQRFOWI}?HXkfQd=aSavC-GKJ`F?Mg%w7Q?rMOV8oq4W=j`?_nNpgeD}e8a zd4+>Yh^P+L57agS8jcw4vN>f3wazZOCrlbQ{PU+tKRse~K@Ep#!nLt!n1aQUzz4 z0C)e`)oChf3kv5Tcb|N;zPb?14DZ|0wxO8XZC1f2~cpoFt*7jxx8b$|L#|Yahu8dc%E{$mRq7| z*CxCF-OgDkR8c^LZWhJUsVB)$-nMR=!Ca-|#f2*ke`= zZTkL6(GPs%)w* zbUo923p(BkjaONRY|+QNtwS#p1*{((w_Mt2_V(s&WXc9&^nJ6oZa@AH74JQd8a`jE zO06S7z$G1ntrx`p5Iho-2kp}!5N7<(Q5dsus1|A~{m`SkFATIl zG6#dC0&aaCkJInrnONdVo$M*G_GdXC%g}U+CDOi4sdm->WYp9@DlC4PVw%H%$0Rl%3xk$S+83~NZN+FsI}#02FKJN3+5Io)OA?+nEn$AT3( z`pVC93>-=Qi-8qvzNc1?$<1t0jlST`-&nw^8UG6#;HGrnOp$A}dJnrZ0ohUPWb$D}yU)2WePl!uGt2UVo)xDi>C`~+ks|YUaW`@6M{pmA&8P>p^tAPIa zZA{iDqu9gt972;zNbVJaE7zQ5$#%`H!v#(cNxT!U`cfwQ@PR;xn~SQxSK#7_R0zgw zYhq-TD3Y!2wbo0rwZIj?{0VUTd~LGC9bI}cgDa2?BSL~bj4o5|jiGm~*nc!+lA~*q z-YWhk`9UV}*ya}2_H$-7N0i8p&2511X!q%|Fsc6an=Iy$n?5-FbqM=> z=~VTM%ZnVIns{ydOeG1cuaeIWiOvZX{A&6F`xbRs|Gh=2-beva1e>ix??}pN0?;uB z3%y_)zVJMCffI6;6m>}qYI?QnMr_EdDl@RZowp9D zcX=$F2IJz`alq?OrOE)h=3r0%jQ0sB_*AL4c+GE6!>ja5#QMJhhx*BQVjgSwuLQ4GXD6wTnKk74n7U;;39d^Y?Vb~^xeph z^A-UGuO`3b~a(2qG`L7q35@8FSH?beR%&R{yghzdn znhuq6u$-AX3z-$-G6HrM1{mDul*XRs9(R0!V^+wZCrq+!SA8CToi*Goux3gNC6|kY zv9#2z#YZs2;9)qJXpV==L_$7UnK6s}SWm3oBbbD3DMyC#Iz@)v9rBIqRQWt-xiIhz zu9wm+?LJV3>9_3&`8Oc3u8`fx&iXe`Z96pQ=vh1EVyznvmX&4oi5nK{T&rp@)Wsb} zv_?#%PsU$64#6(d^vToDAxKO0KkW%6Yue2|_kb7caBPN&5+>_&d4V#b_s@tTjwzx3r)KWST^{uc24 z(=B;}wP!tVb`AwsydhiI_MFY_dtoi|VmGp;=9a^D<24sO@%eoQa{6HP`c*#n8Y{eQ zeSHRUr`mL`Ej&EQFjzjK$sE_vEzT62H(N8RI(lN^1iCXu)%}`t;*{!^Y-Cy>(>#ubN7Cms5PD3J9#BJ(N;C` z!uE>y`IgCsHTYuV)4O~B{Y0#!RbC*@0gU^1eH_B<>{pyV;-_)@)M(vg#&#h>>>N`)DT4X#*cG;oeK_CqJ67f4 z3{PdBACY!&wbC-5P_acXa)+QFm3HiV7OCho)vMitJ{vdVmYTKWRbP2KYCrrpY+sD4 zexy*8ZIkwNv%vi=k9PhWc8!&n!B~dTj81eVwV9bM?hfw)n_@64o7*pXm+16ex7vWnQOdoR0pWW{Ye;-?vD#9v6R*}r?QgdBc&eTHK;-^CP?W#enxm><$ zBv03gOc*y2Xm|L)`LNg}NHKW*^~eUfetlfZ{2Q!Q`!g_+MBOEk3K5S1y=1z0<|i$f zpWBd7@pqTEzv79v{or9eA!(mE?D0xo(p&Upe(z33RCQ!MMVGqyPb@9fsHq5I+{y*H zB*WSwW5AP|D^pu}Y06vM%E?6N$cF*1)~s=dr^Rrxcdk72jmI z6Hq+e8+bFn+Lb47YM5PY#1XY0qSw z_}VOe7Kw!Co5E%iB-t%ySKdcF$oBzmy`NtpxdtW-i*Ts9XV%&MFfUEs{zxUHqM7JM zTw$`!^L6Z_MV+69T64{ze0QSbo%bZiw$g}-Pv_J$tb-&-wOWhD9z)<-WYj)1iYXg8 z|7IbX)5UC9^?uAui1dm~0uVN4T@&Y^7ltu3RcKRc%p4cBS8`FkDm-lVza68g$+ALc zuyzld58*tT$n(H;{WN#slzJ{)Y^S^U4j+D4wN$i6+@N)SuvWlLhGh{s(1QKWJKg^% z)b&BroFUxynJygTESip&xqZt#Evmi#N=<5cF>Q&gK2re+g@R**`2%&`WV1!o-!ZU+ zxFsjUgD0JaiTF+vz`Gd|%zEFq?+W%(ZP4lk!b#XH+ul#wsR z&hyEPibH5bKbN3&w9OhxvoMbl5IqQK4?s0;49fPuy{>f%`4PYjr`UOOp6&Jz<6C7N zxth*nrEUXih}81D*2$)IOO8t-9aLd8$!*sAc-;bkk34wUped?WA2?^fMpMh(m>KtC zZRQs>URJ!Ho#wN5(_K7h`8I&E1GuJg?C7g;%%ZoZiUVi7u$uXKH|8HOe+t6R-o@I^ zo=_t{iIk1tVa-R;CyLR+PKwid(^+JscH;TB=k!$LUR}(==HFJ%TvTdCh|bhT%_JIU zq;VVds7Qb2CRvo33#ouTN3ot(D+AHIkU)&Fzftvu{~5ow$5UVQvbQuyUW6}y@u)S#$}2R}=}DkcV%RnLIvO34m9+_9r8Wkni(Ni%H{Iq>?J z`ka;K-$lD)?~(JZ)r|j}!Ky~f@Y1TXIU|FGqi|tqJy&*jNFjEi^nv#rV-7$VAz0_y zS{nZWG+3lWN^Z@gqcC{*4z>zo^nSeatgstS#e)i#w;%LX63T(~5DRj{N64j%AXN%* zCiSPa%E}%u@^&6SAM{-p;@8|gf0+lVPt&s2T^6H3?89`H2>|u4z{KO08On6kg9%~H zljIka>f;D5leS>s%=%qao&_*NnKrhvi%>Y)qhhwUp_;ykd;Z8>M3l)!wjRN)Di`J4~xzx!>A-~e*Z-=T)QQh-$=wb&+kl}bFpHfvKjFqi4pZd^T=$;u}pMPe)Bt-;aZ{){u;*QYSlLLMDe z<|RY*_I6k^(Adk-UF2QMtg$71j7<_=zqn5G!S^&fglwRb9rXSpk|&A#AB79f=O-;S z=QSh}_Qqdrc<3Q-u(!tT{@H##*n@h$?A2a55l?J2;#)QRS*-<_UUna@0doiYgHrnAwNQM8$GzcM!A+sT;CsxWvdaDw+uv+8Kq<|yWo)~3hA zeUi(NawD#P5C3BEgoW)oskEDN>;6mK2OW7nd8hMLEyf1Y3{_l<3Ac;f1JYQ?&T;*f zRz3~LgU8CUF=Ot(AdGvr2$ddkq^~*cAKye8O?T`!xRdi4zA|FD)+5 zEhsJ~?SFs70Y#_9DtbV#BiOzTiI(ls)CX99lIT?W@_7~m@Cg08&{7>X!W$Vb46{C+il1`xrDRVtFOw(93G}{=KXOs@I?V7Yx%&C1K3DbJUP32>c*HZ38@e)z%do;FK zfaQ&*-K!yWTXVqSu$q$oNu=7DAHK-0 zPkr@58diusZs0jcka)nb=O#Lc4v6#1Pr%zwx}z?Lc`Zy6YytRW$XK+VMpXk?%rUOp z&ZOE5GFNrrRtBn8Lgy+0kjP>232R)!oo>v14NKoV`xnoMZ_ja~9`$xbY<sm|LZGo6Njb6wYlfHaK**_9tX7_H9Hck^%_z-k5TmKXO8s2Y@fruOs5f$ zUjE8*yCR3->0pgp4?3qUfR>Ao!MLj73HiWOZ~i>j5(6_x%wIRA2VSdyX%?i23OR|K zk2_=sRaAjh%MGw+;y+?La2`MDR?kZxf-MtS+85M&UK{#yj?=lzMq=tRa;FC+wrT2cc>oHf@Dxn~#kL zVSAbD&C^{%4B3|F7m$~Y`>uy}qL@oW{cq|q z=lc&0v=x2%yqIl1*XM%I^)i3iM~0-E6pdA0vjPhgQQK{n7a!ok$rpwR>RT~EdE2Ef zQ~EL#9b$o)E-fVHU>HoQz;)TMJJ5o}cps-cev9isOi>O@%?L}5At1I|Yeh^fR;F1| zbW5tLpDk}OJvDsxM_&^Kx?`eAY2oor1=S(*WA3%@DX%ZNln+}Pf=b`R!}Qk^4n;V> znnw!kWbcq3}8w?oDg!i7t@X!%K2`;RX?KlLl} zLWOs9h}HF{Dqvu-?g{MImw*58Awk)M@a~eFz^;gR5Q>^FIu@ZX5xH#vj;Fpa21|{r zUL->Y_-3daJepNIcP^oO6GnH0_~jkqbk49=4f9B~5#NNzFAe7=dYR|1Kci+h0#ZfD zA@^N^F?cBwmCRLRwM#lj%-|S=mLMxpd0I=T?|^KbF23x7t27`ciB&Z78vWbvd67fq zb)olh%1-rdD^>RsJjvl6%}?q#eL_UIecc@ zEsZteLU)1ua?+7z$nldc>Fh2NV}$PE)%_B?W2gKb&OGj#zk~n*tEoIlX7a`c^|bJ9 zxWcnnD^G~HvEXx*5Jvb315uGgG3#`wfWl4(`^ENaL>9R>(d2OI_imKFnzS*%QtU z^!wW23z^X;r&N#GFtOv&R5o-!POcC%4v+Kwp4*4EHOFzfPY_OSWNyzij+Q>1xmCsH5mRUH)t}VwLX#aklWo&cHwQ+B|oS%y>*)q{Ok$I zosk4Qdw1aOmIKjq&PNBCsl79fT9zy4Kwf=9?@*gMx-{I7PPnN6wMV*h=CpEAA%p-t z3{2HuyX!ZXSCpuJB?C$P!GL#)p4o<>!GhBQeL3UVi`8T|=v{HtSLsp9)o{_KCdN7* zZ+3ozw}&cMY&k}KZdvJc7~a}>`GET`Zx3VnrPM_*n|94~)_iKD5d2Fx@&?*)By?QZ2-w5TYP z;U;oY4%6($y0Y==JAU-2mbJj2fN=x|&sDX~#|cQCJl7VqK6}Dfh4XPC48a1PS?yQ+ zfo?DcEbCUC>HEFsqecGzy-e9~C>`dFr^xCZHh6@)s^Nx`QT1F|`Z=mZ9QB(Q1KEIN z8*9Xbu};WI=O%=b>`iY8(eeEcg~y#FPBz)cuqH|8NtrYL&JVZ2od4B~f(Tg0rNt$x zu2u`De4j(`7wa`Z8%P|>Fq|ZaF#qyAcP-CnF)TJR-uy)G34}Y!ukHiVd&T@5zhR+% zwZ$UZ)xniC4_kDMbjSr=NiP-N*F<&cpKL*gj%!`dU=PCcYFRY@f}+$7$0U$CMT?&y zTY{!w(8N*C;tclNQz3Rpd8T~}K+_Wl7VAfhvn-Z9pR$};y!?cjEpSiF2=zjjwzWkt zZul>1mx%rRu^fXHxF6O$@h+6@6qC4u?lmGG@%*u`#UhPQ_P7q%{D!qU-W|N&+Dn`}% z+8=f3N9pbN0k5w0fI4@ zyVw%hu($AOAk+qlqndME8K<);$wre4l6j+sy%_7JP?BrXoYSu(({e%D9TyF!yNIwo zn&A-5mn{FkbB)(0+>#JE1qzGi@J=x!50Ab8W^gs@tX&3LOP*8p=p8MYD@h(33waQ- zd$X;NHBY;`a6tLR%LklB01Jy)ZE5c)onbi2nuy6CpOU5i5hd{FWa>jf%q%?DxN7K+@|Ti(m4>s24PB4?fc! z@u~pJX&7-yJzduSO{RIxQ27R3zO}i4l=ws})$6sXifGVAE=UuJQ_uCeKcRQzdm?)F z@M)q2VE&R;xj>&ni>fRwHn|6I(PX@GaU{h)GUt13uofQl8WXvP@3K@s8{Ln+OIFMHWC7V2J zFZ!kolW(ezv4fsXDw>cobO*yX=S~4IfEYOiD>;o}zpy{?D7SxDIjn@1 z-??hE_%et}d^~m3sks6=B)Eb2VF=<0WYRSp`fB?Skl;0_|vc7Enek69LvE`}e z;oadJ`R{MiFxI$&ljhon#oKkm3W}!--ER>MG;o<9TVnH72&RPA4tZQ}vy%=e7n~=$ zpDx(@2{)m!L#x*PjPX6`WuN`kQQF^+XR4noP0l^Y_WAyf0&h>tA~~Lf{rwVQsA9fXt*ccsfICsaf_jI4u@K(L8J0Ih3TD z^|58uNBl^4K+(yx6zki5hk+duJ}DDL4i{8uxO`@}Eb3?j6hr}f#f~SymI}2SE6y20 z6oVlRaeTpx`mFTbX~D*06dN+uy~(9H+DpQy+7kT%Sn5~tQ@HJS9u)L%vV%%X38)ou zKBhPi6}{c9zREb+I|=j*(2YvyY;fxn%)(;jxOj3&rJmJ<3(KxKg z6S_31`4xMIzP{p2T@OfU zZz@#uc4K|29gaN7vm~8R3kSDLxj@V;_AAP0iTpPYX$V2t7q?G0OK0<@O8`dvzG_K{ zDBSL`+L-e*^)_{OGZ|21!el0?50^!UyuL_R0?H|i2fL3T=vL=J$v?+cDFWqyyYNGj zue+a98=E(mb3xUyAfK>$MjO&9qYgke#lIA*3dsgMutE01&zYDkzp9HpNECf3=QoB_ z8oWj91&(SsZg>6tRx{b{iU(_#!B8D3^6U~$IEVl0%(Xs@H%kgq0jMjh4fD5du;V{0jRr8GYv<`WJ~~_}``j zN9Ad$ZSMD_0H(9mSVd-=)(G^BjzRg4u!?{|*y2^&%|+H%gz%V*yrhAH!(~Jj z7D?BAEb%ZB=Y_Dl+`#xQ3HacV=!{~`+_@`8T?^7B7!HB#hEy$mxM!ICErQ@^PoZ^K zzw4Op+V7iL|HJe|JZW)Po$tGjcfBd6g5|;HEMU}KI)o`Iq9xwW^mGWubgURD8OK?i zLRA99lm}Wo!Dn#c0kPe~!?r9NvSYTe=8pDP7bkP}ezx=E`+<9t6K^SC37xu5_ zwlA^9_&z9Eq|;I!Og(}iiUoNv9~{z=r7J;u zbfN$!;RIr!sr>X!$NRwlf22OUi<#0&eW0pmRYdAIA-JDfC|40Ah(KC|p_6X%xj0%P zH9wq-9{L$WVuO7$mrj*daEBAk-*TaY8t&%MH-FXu>9XMpnGJ=pg0%%O%|^i>{T{ei zZ=cnV^FcHs;sZEeg0$U#*So_Qdq^&$(Me7D5SLGI(DUhQ?2`w}uz}022Fh&qLk>h;cgisXVdsg$sD+YSBf=~n_bfIL=4ahJ z_;Qs$dqwmG?&IA_$K{K48`8pObWh5_RluTVQVJF|KY0E~znD8Bcb_%m{plcMIJcG_ zE3KJLNY1?p4f^kf+kHTz5H>{qlaC_cfe@&Iv{l)~E@2g?Ar-f>Tq*aZGm)6|>#-9D^2K}?jK3d1`P1NayU#!=lgQqggPJKL9`rEC}b^EyOT>2Nh^dcqe+d$(y zGOY@_Bf!*BqIIpYEmaRNe`5j9xjXsFbZ5e4rJn2~58Cg$|8bfqbUfRBo#3Y@TI-vk z0sF;qVLFm@)zC4Hd3{0MR33UwovYZ~Z50^v&F|<^_}{+ynf{jgbXWSO)M*cm#3Lr< z0}4aRK83Fg(4dwVq*8k$=9aM_tXjQMEXYxj;6Bo`E&epm9RBqv*5fi8$g9c)X_etLspfb_E={?tL6hUmH z;5QTg!-A)wSvGRUId|f4@*jM?qN1ICW!cj%ogyk2BvX+M5GEz`%16fMz@GdEJh*XK zi4&1;@Yk25y7b1Q^8X>d;IrlLUxi2AlGz1WOBY>dSz|#>L1sjJNjYTT#<0fOn~33~ zxa2Gn4f(JEwpSL?efHdnBTee)#E2Qzt*bs&0&7&G1NQA(zYoV@h^@I4WJJpfyMvtE zl3=nubQ=$30DP+}Q9F$6E%ac9Xs#Qoe&p}&#>O>#N*DJkX0K8<7L)0XP24Tu(yZ;9 z)*y{wkSOEk)e-t-owG#vU1b4VW^P-Ce3rLk$O+i_^X5%K7t7jd+AsPB}nit-8N~?#;o3BNP6|Z%)1G zWaQ)ms|zQ@?OyanXyoRXnpv8>sF-J#;;At?iKrxT&2-G0OS{A8nx=b?Q2oelvpfa% z@VM8Sp6A5(zZ}*+RtImsg>W0X!$~77*>&zAeB`6jn$cV(mpCnyg4-Rhz*i2y*>EO+ zN`{xKfd=3dJfg{HdnL0=6wO%;2y-B9oKpMI*~u z-Xw3X_K%7q=iD-ur^z0SU|XSdH-4!G7jACG1;3zevo0Jo!InrY!;5!< zm2kaSXc){S;nJSyx@+Cv8$@3wALPF~RpoU!&HhDo>s>eENUOkR3Q1M!#inqx&&LQ399 zr8lHfmC=1zHDxaNaNMqQNgpIRy>RHNR#0$#Z{2GZI*cjpI-_meaym%~76IgfFjN=z zN`lq@1PzY>i(z(`_bb<)Pda>an>+AS#76$sAPKtxL;k7iu=nFTn~!+~V~diLw+A_n zDm~X>^_I9_gwe%h7nL)fO3`-NOUeFJtl+*M*#pO^+tYikEp#AJOsV7Nmn&-JnhFSl-e?>%w=JHiok`pF5 z!Y@Pw*yyr1S(3Aq6s;i^j78I^;wL9Nk9<5=%RGkQP2QLcGnp^vAZnnvymP67O=gjm z0EhgBS{r9QW0IwkS^$lKj(AXDAtJL-(#rPb_%06>#xdK2#g8@+mAQ9T`>+jJlr z!^al~Qb)B~d41&y{=UF*c_AYO&Cz>ExPRP~&RTVTTb z&7$QWjz(1fONujemU8W~z7>sC_PrNHjaO21uIj^IJN%F{ckfQo}oXwPtY0Yj#-{rR+=3Y%utSs`61W5E2}lJj>1& zQoka+f#r_`<|l2|Wm&-0#Ld`gWB+6t4DYkeQAcg5+3etDT3%H|iEO6`GUS$z&m)D< zjp11HR$vtTz7TTG+hqJ@71}?0s~E3gP*}4H{k(qTZ#7@ui|@aPD4T<4Og_8d#uH1X zGb-Qju^Bmb8`x38S-$eT_1@7<2wt&3tAf8QkZ`cBWb2e?Mq*H*>HZ)YA>4@GHy2xW)jZ3?L}-`)hBhh;uMZ}GLK_rUvP41;##DxBCOWHRa>bTx9~ z56pf%7tMnyt}gV30Rfwmzwed)&jD?46)eY?NEcIke4 z^3QD)JcuVy2*_a)kW}-|07PG%g{S34n};~YX5Bq&C0>P0N^%A+a6an-u&BW+a>24A z{`Gia=VL4vEgOv@+!Tqssn7XIoqjx(K)p`<%9s?aA5GL*ko6=G%I_TEGb1vJ?L8X# zMiE!MA6AjsOz$4D^KWR=PagQZw`je^kpDGlqZ+%qW6YGBWt0M+d@?CSV{S5RwkQ`- zI^#B~_2XGA$x|% zHRu%rz?gJK6yPw$ssT4 zXNQlvMaf6|!nQ19K#XB#51whPA9ea$b(%a3`FzLbtUN9_Tl|9@z&(7mmWrxe5>z3w z@GIyU+9Se7r^Jqc)<#B&SI?n)dz*l=`0ZnrdgJ0X%uzQWRf83sNu$zPmwnQJ<)w0QA0Wi>;Y9I#C> z0OzkeV9Kl1_w?wIf@-EY&$81PWRfW}Is1QIqvnTTm$k`8L;^Ux%p=_q(8MN z4Q2O)oX$LBiFn_#N|pE(^tMo+RxpQY05Ii;RiAzW`c^C+}0T&`FaKVN|jeALjsIxfB0hvJ>URbabO zmjtp52bEjGiPkI6-w+=`w|?M=eZQP##Y~@cCZhRz@tV(W|CXC$Ydp=pLS&^-&yVDc zXm;K{>c<^ox~L=2miVeW=<@bCA1*3QZRP>UoIzshBhBKSgF!3(Fc+yOIQ#=*DFAw) z1s=C;)RheCk$i|G80v%t(ct(Dbddw@1TYB>z#|0KI}q&B{v6Zs=QSBA zK?o6^EL;L%i6b0qinQVZHe-3cNNNS`7O4 z`f31lJuy{h$qgf41L6UqUxQiNafbwXrNmyck;QACtGQSmi~8Pta5d>AY@mBnT`TEE zLI^A{23eVKjLLM?Ap>6h8AsHO+sG-K`EK9=OIKQy0MisHy)rTiRMUa;v9GWZnIg+dh?W$k2pUi6SwY*n|+k3vjp+ zJs*Q^dBMcLK`M`0c4zzlEpFb|8Wm!b~H|m%E_zI+DcN8s`WTsMT%c+0_pW%d2gBkllgNM+r zZ|{p&?S#z@RYQHF-dRQhv{q7P!pxGy&8Z5MR(j+w6hr$~3Rv`JX%}kz^g&PN%55q zjp}0S8M_|YcquX|WPMIGxbH5wReZ?j;@E@xnzdWlC+lZY!rp@x!7z;P@GB?euQhzi zy~YSYUtjy;J(QV3g;5ox>i*1B1pe~NmN$m>4#qO`!p@eL8GC`@#wqM4^}nqpr3^gp z)oQ=`vZggKMCKq2ma(`-?QMLyY`U9{XM_|^^)p6l!NfaZyZgNA^I0|Ka}mvW+-R%E z0MQz~8eUgA&p7UoICHzT2%LmT>PW`(QZ^(;8@QSG_={G0s*BhF#*EL89Vng4Cs}jo!k-*Z`bZWB z-nOOy=wxH_EY|nT4_Z>sA&avRm4{ii1Qqj{{9?>mhEfG(X79p49bc!ouSA_4d_KT_ z`Nm?35fe?(MK3q77qcV+?Hd0U7*6@&#Sad%uEtJz%`eMas;M9Y%mmZTQ3(qXCX>1( zuv`~vMwsaH#1U0Ip~;FCOz>CcD)O}!75O-!mg0Z(oC&SL zwO8e-mNiq~Osm&jKYlPQel-jVIWwatXQ`-(**=|5Php;Axe%Z8HIS^d{*gF+8fI@r zA$0qddS`2)g|tXH8G$Y7WwB<^=tmfoWVoHoK=EJ&$?lJl@lUb7#vtp<5DDz~mt0}= zIJ9dTZa2hzR;tWfgjNsc&FNknjKei#xk@wNt5(<}3&HsQw7DY?$0WFRAiN$fQzu^23S6P#|e$Fc1 zP%3fM`dLr?=+VoWcpPot1Ux|(qudR^ve$61`mIkVV&Z}l@I}z<^^yW~4KZ&EVG+x) zrw@p)HY$aQN`;n+gAIm}?oa()WQ;j~Co?e&@UyX@dTVt?JNc)gEzgY-q-C>GUm$r^-zg@K~K zr*ocXemc@#Apr8OCLONw?tA3aIi55x(rQ0iqn1DX=cs+|yn3BmmVOGk+FO1&##TF$ z49ckiW7~Hbbi(P?uEMErF;)y?o!Y?|H1F(`f)C7h{4}{?F_4-{q7!&J`9o^DLykxo zjH)2bDjp!@!!b_=KY!1PNhQY^WLy-|?(u3~9f>{J@4YXc0Py`;iJ?%S+Dq?ygx@r} z*!p6t2~UraOiE7VcVG}XH2JVS0HUMaUx=aQ$L}M6<>IT2_?Ssy5wqVbe0;@J|7yw1 zV*ZF3AR;P8u^}e7Kabq-eLDTy5ypSpU*LWR4PB?=xH_H)m2vMaJ6mH%A5^|D=5gWr z=M+qE-^}=PEJYq&&y;V>ZL^)i=6Z=uCJ2UX@smh!bqmm`{5%TEUC_dPs`IghqO(DI zcaGAIHRLa2cxTiFZZ7=9brzgmG$9z>WgAZU@k3jY%XJ#>@*^3LwA= zmK?H5zi0gqw%Q3MYP_^329zx&qrlAx zKn$~BM(*OZimbZVnd9IJz_s-O!#p$IeGXc-by&So|UZK+Q%~o5!+gz_sO>ZhQh?K z2xp&c(95?`yGC^h!5NMC3TqP2PsgC4XKU`UlN!O+RtJ7AtM5%Vu6P^I?8f%KB88Gc za-+?@L=xq{)I&+Kgdeu^0o*qpT&W$^E)?yvDP`_fHaxd3S8H)R6x(F|-&nXrs?Xl{ zU5o2G6JNO{u8~@#d{nC^CnD9fQ=5cqKn!Lpk}6-ra`s_y$q75PSeMsqZ=QqjPG5G{ z5^%Jtl88NrEO&`&{(N1yMS1AM_Hi_ZIBSwXp$B*gJm~_2CJXcD1W_r0g&3d2a21qj zWo(OMQ#g=>d*?Si2+*;zg2Uu_LMgZ`d!9^QLt0_KCVG?|5tcy+CVM^-??anQhG#oH z(D&heN`zC6tJ4eWqSc1s8DWZpZxC#;OlzWq*InW#maXK!s~eO`|C!^pJ2GhboEnG@C8SwAP;BoEk>oETVUMfSRv ztd}JKD{&ya_Imdp@RGvyy0P!6k85$yyghA@qtxxAb75Q@yb8wL&&zkGeivqngh*M8*T=mRpBS>$?BeNQzvy`m@$5_|0^uwtyY@ z93M4`?X;|Nr)ZNr)+Y?qit{4!fk~%1TrQ&6#`?W&*-gbln^@ZwaKAhAuG7oY9QUYk z*`7rs&TGI@Z`-Gjfj^wDuc)M_YZh&!GpO%19r(y1iYOpsY}-@l_VBlvGp%pc(rgV*ImLIb!~kx$l4j_%sd@I^9)OAJaF-$0bT0vpT= zHn;v73ixvD#^P?pUo^8In7UiJ;B+chzs+dt%v zPZyqlaoEyyZMYq@e3NLX22#G~9Rr9hD7SKkOu50hFT0ZkLh+%Ow7r>+I{B|eNA9)% zStM#uj_2d#rVJxgqWApU0*bNy^QtDiW)d=n4Udq|15LxX+H}6QTPF^8uW4EkTl=WK zJ6nS#+(UeDQI1G39^?K$uHG^r%BFi9UO-YpC8R-+lJ1gFQl%7-mhP66S`Z0o3=j~O zl8};amIgsUT5{=zrCDIv>wn?>Jooed-Vf{-&RjEd`kXm4hXT!eC6kkQTAn6#w-LJ6S5ZpSytpKFu(P+T($^Il2S6AyoPC{Wqn(`PmQ%X|B*5{Axo!+lW92IB(@~Dq93U1% z?Rc3Q-@#K`2^YL#CPmBknH2uwow?(lIf1-5kpYi|d+v^-U73Gqs$RgR7Jj}@L{F~( zgj@mVMRUfUj5dRG+vVqR^)cZZx$*7q6T8H@mp(bD4ZT9M-msTi?j+I62B?#O-Vg-{ z&pLEwL`mmzyXfPc@wipY@gSv{72R#v6OKdc0Og@Sm`}!&7pWnhfD3W+(enqX(6^T7 zuDLS7|3pRt5Uaf)7OxVrR}U_wq|V?fvHgRiPS}xR*vA3$4-{Item6A1Y=q?#_BA;$ zZ>Hf&nix9${R3QSYw-1JCo-O8=uDrrj{m4`sybv?S$@P>G{K1bDt$t&0K0Zns>L*S zUy~by^!xRl0|>vKw)L+H1G9^7ee7C(vanprzX=`89$8L)F%gUoy^;VU&krri7ZLbh zOvHwq-!kR(KEt00z%Em0;xo99;a&f4Y-Y90Kn1T}lCNh| zdH(Y@H)o@#YJ8B1>!Qm2Zh}maY?042nLW4MD!4M&jkTSVkG-B^IuViknmYJPz%M=q z>XPg4y+d?MU}Mr0F`w*3j+5azvmNVN6?pRf5}qa5I<@+-WsZc!goig4%wVBiP<>;ot~Qn|Wd}~_l*gCi zr;_g(p^o(MGVyBDp<7ro$Y{)0#zg*Q55E`aQ5uQV(==sb8_WTFHr*GKmevnmEx(Iy>G)?KWBazw#cX5(3?Tr;NfFRtTQbbW)lgIn>JM1CR4 z@_m!)QQox!Bxat)(CN6wmycYvP)BDT5ra7)Do__|cV6oQ+zPW1{)j4k(9T?(ZvTYu zH@7^bu)9G`iB4t5_B6oBRMPv7H?J+qI8?B|4yF1f_gQ)NJahZM*%=529fba&I6S7y zu88zc9foM@&ylA3$MenGC}noEup@y)7xnMaKoJ&{cHqlNs}REx_*C?{Wy@0>8yQ>c z+j}{sPv8#48~f4)GC#fo)nxNZC9W+AU;Oc6gRPbx?ve+63=h_h!IHUn&PEPqO`LMt z;g{dCJ@~$vPtGqr{jZ{E-Nb|R@KC|ZIm>rw`;qw*B;xE8LpI5$!WG1s6QuB|pqBZe zv|TEPK8QdanQ*(@uzO?erueeUmiV3__coL~`lEhv<@XNckJHxI81a@1YzE)s($1#Q z%DxCvL6>e~0Slr?0is`9k9AwczQ=iteilsVB&PR0;kR#1ZrL;ZcO4{n5PZ$R6>_Fk zL$6l(Q6h(VH5e(t$S#Xyus{ya#xhA znliKF|Hj*3mt#`QpH&!7MX{7wLGxGI$qc2eE9nnJ4l>H}6=%mT{5d(UW1Ir%xXcR@ zei^6Rl$hrSa&~t}B$nWxOa$ipE(YJ9@>22Z&n^1c`I|`sq1EF${%5>n_1*@rGq^75 z3OKl1pJtV*u9c}-$`6eOHMhf9k&$mij>8cz27lZx{a1=-!B%5vvS^OpC48gUZT|c? znW21&Uw*cS;twh0*FQJz5@x=%e56k$pSpY4V+`^`vzdy!$1Q)qR= zkOdT5G{PwGw`xIHYAyPmR3B{ke&>k|ZsZ-bhA9ac`#_aRuymJe&X@V}mpT~ln|hZs zba@CzH}350H)hKGhya4*#^o@#s$0&R#7gN5_JnzBM?cPNMq@dott*H&k?Hn;Ptx!D zw29m8>&mg4nkTORD}}YR5PG<^do%<7-X8!`^KEzKq*Gq2+4I2xvjHiaw9KxN)Urz)1(p)x^ECa-%C%;_@HKU5Zi;NOo8L%WY{SkSMPtm)hmlL{Zvn;fPpSL_I7S<@DiuEVr11=Yeb- zl%-jOv!nnQbdY=fH@;uUX}gVpiM@d$PUy&3%)iOPCNq$gBfDS*C?|64OS#M>^>wX+ zpMR*Kw{3!Tu@MfT@>Lt_NbAqHD4ARw8{|~pbpaKq=#NrpJ|{A#b*QI)t0|z`#z04% zOv1Cx(Ml{}Nu=KgO`M^-b@kvfS=e5RUeOGFB@O2cN)|rgH4d||Y zl@;E@l&NY|Daa~siDMStuJIK|e$rMq)EVb;o%=!FNKudm&LHqw`QMz%D@JR318&Nac#m`fg11<%p5K z8}4BXxip&3<$7{6XA)89v6!gL2|?kad`w4_dypdo=}HCij_&X9O+{#xQ^o7Tl^H$l z1Dfl6s5wplzgr)GEPQEOQm0zpl}${5;-7P8WY##JG46be`m&?x^Yq6+pS({WJot)X zHr&L2IM-Bb@e7ld_u_qyu^|Vl^>Y2yF;aN{3{KC!oR;n*UL^cUY(RFaszk|c=IL8* zQG%I7nBbb9SdDbEJP*#hb#(#;yFN)<=yV_bH?Q6_jqs3DQU$GYIUf^a7* zG{0&$PW*aj$J5=+>aBdnmZ`)d-UIp*nS26h+&B-PRr%0Qh9I&k{J16P7y)X5*TL@q znd^PAEKw9&9rYu2b_!w#W8VLr)Y6X83E1>00BF{wl~B@e|C?Ic|BluAymJZ0)3Wi2 zoao%=x&JOVI5D}BR16q{&qylKZDbtxcSb=Bc1rJR_(dLqNuVj*<#ZjUdgd$k{De_y zG^0HkeGee-#a7MxN?x5`MrrZl`;CXXEBjN{B*C}a5V1RnOW~yOh-n;^L%G=zHB5!|7zzn}VP?zIObOrOp^TEuf!P_&Np zFS-0Q%r@3AR6KH9N90tjkIu7UbOgOzp3$YJ@8oS1fymhn$vI~#E@S~xDaX*KaIPQb z-;~|?Td;mNsL(`djn_?n-SjA2LGx=b?&PE=ts#B?>mx9KB!%E z{}rbc2Z&pY^?^vV_U!BSbdsBeIct_HoM$t0z}} z>RRQgsc-Ip-P;Y>Mj=|vNvio?$z!@;FWiPEZY<4Pqc!&p2mci6NC5Y@eA)|j1T4k= zwS&}<$8ENR@Wciiq9OOu_xMKQFM2R%T)qpeL}GBIMX{$+?=$DR{@%d9h}fLrw3B68CT;N$o@Yl_xxpja3fmMr6m$HR75q!-?X-+ zh%Rei6g0=MU>-zy{wc0Fd?N)A^znUpW)P;$)rcszt25()N)_82$WU3o+S<1@^IstR zYqru?r8(>TIdF@(x%9OkEv7ytLYCE>G*#e{a@qdj?4ubBii4@XvE*nl=Yjvv zH=u?@Bn&JWZW)%z*I*p2QF09|9y5dXvRtDqcq@L2cGGGmEo- zt{knt0h{Y+Piik;{moF97L)=EOh(yJ--YM06d(Wa?4jUNA#gpP3 znOCMD{n!`uJyAi|=qE2T8%J$4pmddEuk_SK_B<+@s|ZSX{`6^M5Gm(3Cn^IroW@Q} z@AG1lUflnQaclXPUrT~i`HL<%g!_Wg$jU!^n0m|7Yss_B6N9zS^go^8Y260Ic2R6SJtEFj6`~5!XZUibh+H)S7XZQj zQymbDKis<{MHO&+OYb>7O$5-ncjZ;y00{;l|MFn#@&Xqu`G-Vu7cE7^dO5py8C+5I z48Ik+FBgjSpEGT>?ndyG{*UzrwcpE)Kw=+F&2eG&y2l)p> zU=y$1HhC)3oYLckj|Ns*s_%mJehuk2DVh|v;A`C%xW)m~e{v-dDYQnL+m;_oFK*kx zJGSJLxBQbQZ;CqreyVZKJJf&i+WODl!5!sWQbb1=IIaXOe-pLN_>fxEUI-rdv zq9FV~i-q8{mT<N=KS2b~TJc6H{ zMM+Ve@Hi|Vx#4#J z(^SjrtefTU2COdCyp3CJj@OFp6d(|w4eEN2PHBP2Gaqtr45PWvfm^^_`kXp@*`rxI zSWHXrV#wyLG}u>C4(zS?_iurklQ;d|r?-__olBfssU)9(L zN38!a7dQx%7XG*KwUVE^Xm|!=Zp(gb(+;1H@8TE}y+;V5`X5HP7F>|Qz^#CGmRm5Y zE36!a?WNw1dyETvx57&0+L?{5^e@}|ALvoRuU%|BE!|6sTd9HMze#1cHy%^*SD?hB z9sb9T|B;J-CeyeUi}#5c@tbW@JQl6+_p-5~c>LOvhgRTP|B@ zg?;*)I|9a>f!wWTuQKA6u36&R!u<1lZAF65 z1cSR2cqVSqXQ_2cmiq&4#eCO%jR9x<&rb(rpBnXn5n)~)+ZA8v*iObQ+=jdYy0_YW zEWB_a*T(37KX}3T0*PIhpt2RDK4FS9cPrM|7gglQl4ym3%=z!n8H*5@soy(B$8@$3@PHYs*@s)E#%f(Wc%PD9Jb6M$S;@&oBmZp&d7D*bIPx|tr!+^8 zqu00eE?51B@-%f$UsRGY2Rs04B0=@#OfO{$YJ*L?TUvF$6q)zvj^^XK;{DX7YK z0~X++=T3Nx4mTh61M=_eOw*qF8|pf2L~1F>f6LRzWwfO(pLl+wd$4RF4EgE`=ciTe0)gc5e6SPCp}|T;FY5zqxo1a#A~mqbInfk- zC-bcLhXMdgnpWZsK;^N{c3GYjm3q4R*d4pP8u^=-Q;GvT=p7%7Fa@EOfA3J;&;?c^ zGj6-gB~Wj0{%N=0+*zKA(&}yn!o%Q-D=$b=4QJ)E7BG9~LEr9B5LKmj=DTnT()=u* ziomG_c=S1p5Dx-7UUcPIYdB}|c0wqxHu{W~nn)L>5@Kkn#>aJaO!aKH-;gj0y$dH} zdeosDlex&luJ%QBxjR1p$w#nVl>-fEj*H)d+&>eW#D2OuKcZH`%lo_yv3I7Dlaq6$ zWm&5Kv8`MEoy*GlU9Csv<~&garqGEe8>VVk&z@cfhYN6KCx^)2r#7Fx#Z(F!cprQ5 zq)tal&9J&ROGXRnFQZ_33ppQHvhOL$(hgc!l*hB*<@WR~B)iTJVQxNtbHw-EZi<|Q z?{xc0l;?Y47HcWss*COa)ki;7?j7D;Qd>bD2cnoQIWgMhD2`w-5_ZA`@ESrIjOFxX z^5(T)X%zLn9J%mvwC|~Lu5v|YY>Y~ahsY_>%h>R!rwWDH3`)IS`y~_A9Y?obBSm55 z&K>QquXwFUUS^h5vpMXDGX7y2ShII@jFqiM4!dy=(w0)W5-AQ1t>o>as5M}AdZx**S)Ka92c0?^2Lp?RU5#Ge*U8Jp$K_R(&u zl}03R|F%d4!tPoVuu=Cf*;D1$t-cd4Hd2X}GgXE08>=?@>>e-4WF3&Rj~sAhsP-^G zSq8p{_N(W&1b5XM?1X@r?U%5OT-gJK-E#` zxxjqwvZ9ze`Ys3p4QcWyDa{@1HMI8lmgU$MpuiH3l%Eb&`a_}UINBW?P%8%mf=)6& z-oKpW$^SeK)3)v}y@B?bV%T^FrJaIeGl-&nrC$FL6!BVEyw=HFs0Y!2<@2p$^0_8= zR$l#mL0;5Ghn*pmJ%sG8#OlrbQLg;2%A zl}694-v{MfKDoc7v>QQ2LoMqd5=$)@_4MhpYf{kk0HT+y$CwrYUIbnCX-(wAVOLMN zw4g*Ua10 z2gH)x_KSay=~{iO1RtltRvv$JM|h#meadrUgC}(lU5{gYbOkd9eFPM|(Vm01$XNB+ z1KB(&=r@63B-I6n5KcHnT&|@`y7G~7hU)S&_Ukl>XQ+{C*^e)qoga$|OY=k>Uk<00 zH7jpVmWC{8hNP(c%Dc(e^2KH-L!l#_%xBBK)YhFVniuqs0nY`^GqRns{N~lh0&^f} zbZM@z1M4^y`+W^RgGgj|Od8nuog~hN@OtgDg8&iyX}=@u@md`7_{2?Bq|cTcr1?8@ z+o=M+?J(}g*Bf85WNCE^u7Qn*06!>$v+R!y+4(CmXPF-pRU2nSSL7*J+W3r*@?+)V z^GLov3B4I{4Lq{+Y^*xmvV2s-sXs6FcuyMnd&ad%xy|g>)+_wiuU~I8-Q>^n7?epS zm7YyhxPg-2<<@T^0zE6ucX{1-)3y0uPd_8$_$F<2dK;b5@K*}V!`=aJ+J=|AM>oLt zX|3eBuF2|!Hf@x9T$UVRCrOTR=v364|Aw9{Cb;9&!*Tne%?nZ!5x%rIx3ybXJy5_l z5^F&pgS4AfhV5Pj%ko0g-X;c{FCx7_0${%msP{aqJKD$ zz_}8o!=l&5AILjmH+if=EEDxQK=N8%A6Og^d(}hZAeu*}Elym`sM$)La#wzx#v3uT zGT|-WQ{tOn-W;WiJq&IPPOh{I#Y#v)uY+x(ysE1lFJC%1I*08io86!|*;XNE*ZkI| z5H98H8*GV!-NOrBMh%Uk@4gJ%lrzE}o5+@WisHsQNqqV}2CsErOzDd9V#&PTb19N1 zpM=b9B7c|L%FgA+7kx`*ynYd68#(!Kj_xumBkJ6GLRF(dty>UzyRs z<(~BM&L|M+jTWvZcg^?h9URPa4O%CXy8P;qZgFED*O=d|{5D>2?fFm`w5vs?%40j} z-STC1q<@xQi?9M3nmi`^o%rkHvAmaQ^zR(sCs@jl|X^A`}m#;7}M~dnubMUMu@7OI1KJ zxoZ>_`E}Y=D{NG5lW!`bB4EmMam;o2NsgvZ)a?Z+Oyf_8L~Mi2MU|H_r)J1{8H`U=t@dh5bCvUvG66J#8Y`f^ zm1{I_w0GE&6Q0SObB;8}X}ygub6M9!D;#%AIflU>?>Pt)r(m(i1mCn>9mZX*u=fr5 zFpNI?fwe$@VX{MLMxO?%0rTkGu6BfTAf7Jgl{O95R11BB)wIv%yW#xd*0+xq;P@oL zWtAxWn={}&gNTmf(=^xL%g{IP3=Iu`C;DW%Z6M%sCMP+LpbFy4E#@)exVfy|@~Um= z4K-wbL9Wcwzu#wcB6fSUbR)6YMBd+gkU&-3D!7bZ$m>FlR@kL!&f1v{ka2+g83b+> z`2o{3*>@+sUvOsTQLRRSsskt6^b1Ww%sQ7EByPtzti@ZA(L_oxtzwr~N+kgB?}hw; z17+d%kIpZTm%2H6nVd8_Jf62BxUX<^39htLAoO99UB}g%dLvxRgSkAoSI{zQh@91d zEmh6ju=TT~-#KJ*s-RPtC5*%#7H~n|Xt?N+`}U@X(Q%#Ypr>Q<2TG0_vbP2ebS7~& zgvS{grIHXr8>eqR@6pj6e<`~y0}Hx}-rP^AwA%-dUwWWqw2+!3=p)@Pj7AKZYin)} z?tBY{aIInf?*q4cp#wI-gHFMyE2_J;!)NiozG*94S{b@MlbmQ$gaFeFup(^J9}?u&nnbO1hfNA{cy!4MM^W>5&B2-Phs4H6+W_nuLTV(?L#80M>hc zfiwNbTWcJmujeclk%f{Fb!x2dH6(D6-A2#FPtUk|l5sGNoe-3BG1d?IBE8SUOvw7Q z(><<;{j-GI0)D^r8&1RNHhb$;rVR3-I5@3SXgMuYmF>ymyqXEBj>pEHMvD_QDpmK) z*y%D0OyyDq{Ygn^A9jwS&myl^ojj(v(e{~PZ2FEMJ6iM<<3$LqJ(cm?H~o0qjD;@K z6v}jUTo54T{ROlU|BMiyD9hoPe5g2HvKsyl?%lAp2se^mv=A5jNFq>-(7)K*f&GqY zQwav#dmGM^jM85^yHqQ1o?iv|9WO>~v|S>v*tLX&gOWE@>wvVoQ(F;P*`k=vE= zRjhOipY*k>Tc50cN*k|Kpdcus`3Y!oDyB`8eT#LR)DpP$!vuVc!ZRXGlJk)E*C$DM zKBfPe78vS+eozf7UF#jp*;*ydpE0WIDu9HiXQf2HkF3*+;|fMP;dd_cpB(!{fR*_J zlbMCao}b}<5egKyl(-dOr%P zl$Nbm<+^2z*{%;ireo(dV!P^g-pSyby<`-{H{AH~NO-pw&+R^to`mHl6YXCtyK(sK z*+z&zGVA;Ir}hkf98pVFKP*T14OBgOS(RgQVWv5|d4G~8)tczF^U8hk(_6ABlsR%6-SSLS@|$ zGRGI-yN*r5F1Gjxu5@2%bx2%viznd~x`A#-;#-jc8W%743T+Cvq1>IS))w}$dJoOt z>3D$YpN^QgI^6Bn_@XLoJ9Iz+m^eW|3Q5289I4pFQrArU76rXMTm2o*vUF+MA#0Ti z321!t(PJ|y=jh?(6;cSNl?o$Qe|r1EZPM{Qjl(z8-A5)xD^U*PvMVWIznxJIV>N`# zQfkZ^T$?z!U;m*x72nBoBO?W9-4Ur__3JRlF1YsKFiE(4g~wNqv0=~AGS{rE30aaA zL@~nVfSwrQk3tO3YdH2Ljba+428=)4!wdw>-$aYgcvFjFHPq6GSP=|l_!F<*rSN?V z@QGem27WgEHj`YXt>i3W&j0EiDwz+RKB6MnVP2^m{G6P*W&mWU<21{gc8Xa`gqX%L z2v{3+IkUZ4W_>o?eNoXlZ4ik{=0T^kJlC0m>4v32=Ai0K50irg&ZKB{1SD9`kvDJ_ zZt~2VCpwN})w)cnL7UDPrz`ApiaOy&LU+D85Zsq8>mY?`!0Rx0seXS%xHTilMC6jY zRA^iv`h!4TclgFfATJzmrW2kxfP3po%rblquN5rhI*|RMi;q5ozsieXszZn}IM)t6TDULF zJXeoKtxt}yB?oo&X)jd{eC@ocLhDSPciC>(7sO z3nl*@fk-bK72d{tHPk*^`wDj7OCEHAy;Y;EZ+=3~{8KB~#aMP5y1DcIt~CX)D*{oj z)Th6+C&YLH&BxPlQ+K6^8Aon1i7oL44;+l@*;_ZsTU0^D-ild-o8ZZt$DosV`+R43;n}!M zfg2kdK6yOicC|DKd#L^twsYm zDC#}{e1g~X5ABC)_H)Z6a$<0=0iBI0%hMkW+>o$YDLfvvbQvVTTsqZVHKoegQi2L! z&g85TCSizf3~Y9-HF0gQJZfm)CN7hvGJhFEo}xv_vkK*;$o>{B>rsWm2fC_%DkF{1 zVojy{QXSi*D5MLfHcY4C!V+HDC5K=SMU|cV#abEK)wyo2`7U^cP13ybX@&Y6UF&Kn z3T4P^@j%hk6<6YLZB8M!vaqo85`^fNw?0xDfvR$msy|0F{xaFc?O6Vuh2p7?O8 zZV^2%Mwn8A@9Y6P_CzsX4y(;j*3zA4;ROdY1aIOkLq4x2q>Yc_5WmwM+D9NJo^mJi z<%M6JH0(8xFEOEg!U=!pEN{3*m);U|+rQN}FyKW3_#aIX->=3^I)M5(z6&}xkk%bn zIwpKv)CFA=gA58`E&ZO&8_5^FCuLzL_IZt~bP?WL)4B^f&qcZT$yae=NYivfscCRo zNx(Jq^X;-+^%FYK#8ehQU7$mU2nU=6dMYT}O(C)x=eeh6XpF{moq02>^g_z%*wFjc zguObi>gpvEpY09B^MipwkKY5bCbJgcKtwI=aaj}uq3d*rRZkauSVk}bDkyPIw^>ErWX6Y6p^V$05p0%n?l4|NBAxN;1sZo$Ee5~}(b&G^Xb z)UwP#z6xvMmi0E>9F4h45fznZUZB*zNt5Sc>P*O zGQ*M-ng0IQAHfFF2LMq~?mJOXXRi*1CBzI`{Gz2>4Wi`gKY9{6$@QwXsT*2E3~AZV zJFfmLYl@xlJr$CD zI<);#-HQ^1)WSgP_!ZCi?MBC@^s3HIzY*z)dtZEm=Y}+?)>WT<|I?_AAUsxpvV*5LWIHJE1%#Xvw*N4zDo7aY*9^DwWm__B7x@D zh*uA|c)tZ*T}XcbNI?d;gMk{;WaG=8aO{o!)VNCK)K8z^O!l6~>l^zO0O67&KfC;J z$Q(=kNs0X8lK$G=3rR!48~h}%+<)>C>V7>QgT8+&>T&M65~xIf^$}g}i|7$?Zxx}P zcm2eH;`b*7yCtJ2&@cR-74^Ry8G;6obi(n?SHD91;!b)ZFlrugQii8o)juSny2K3d z6CUCRv!Uaej$9Rg^{ZXs1FP;YP6Hr?HMMpckU6x^A9_gAKJ9Di4iwEyC3SY zPD3@t*W{J74;wm#uT5#`Dch|?1YA0YA!f~8H+jVJG6 zRog;ZE$wSKs_r`o=qzoC{{p-jjhL$o-81xU_P*M6J0QbEbD9bsVdYARlzBAm)u#hO zBw+qzT|ebpr7M!rZ(+Vs_*Gr|jUV_;dk0dSG)I=kujz7+;3d^z6Q!>TrRNaumnhtV zB+p81uK@@!GaW!W@9hRxCI6w))OQzVVW=~Q9@Ad0C=sk*q7a$5)@{bJ&;_^*y3C;@ z6JCZ9ZlwIqHSDY(MnnM~8H=(9|8QvCqQAa4)Q|#HghPIrx7j}HP4rpS062gJ~x zA~hW#y#W_w2S^^w_F{2L7Ni+^otIr>g5I%SUy*nVkkVi?rvoL$ia{4oxW^|VKGH#i zV3?a!0L@R^X8Sg&2pm-!qTiNApTkK9v+*@|RjzVJ5P#0#u?i3nA2KWXjd$lB&yW>^ z6R8GX@av;PfmGSdBN;*kEWh8TSwC@XM`Af-LPD*KZD-uz^lO+lsN*?w&ej|9?+<)_fPubY{am8ICvgxt2p3m_H1{lcB~Cv1xlnph%~z*1 zaz12xSyoo!W!(JwCUE|SJxXm6zTvp)B^ZQDv~S7>Ku=R(4;+rI`5a7;a|yvBoEYc( z&zF8YC+Ot_pYPDHMQt{TH}M@x5n_xw06D(;P_c;;iazzOn14Wxm)t-LU|c-Nu4qfw zaoxMyWd*)*4-zn;iP3M7f5|oLF^Swr7e}}muhTCf40{<#&8&d+uTZQx-=i|?3@WDm zzI?_xK8J|65fIgN05qIcK4+z}Un`|g5ccT1fwC`4yUo`Ec4&!O1#M=1Qv=5YYYq`> z_aGzZ(Ebr`!)D>%wI2DlQ*tzgfCf2`J;QnvRmRPxuPX|7vEr0(AIhZ$NBUdv%}K&r zUt*%2Zhq|rdipSmup~Hn(XvzqhWn@Ztq&JSk%KYlVSgeE^9gDkA$|CpB>ND&RcmMTc_mf-dg@b&i&OBgPgCw%0&6R zNcSKDtYrs$4D*7?kzMnLb5)K#-utF^F!iFhI54Z;btYWkvugvj%-Zs*?7@+0q@gf_ zj;%FeBb){J{ifQZ5``sG*E!0p%yhp0ADq{k@Jn%^@km|oz%&=j4L6#cHvWNwcZ6DA)Oc;$l#m51H_-rsmH!nYBk}BV({e7J(Rn(5yMx%*4bcl z7f5Gm-F--wZ(~(#q^leTVTW8yCRMV*9P8dAN#sSMaceNH0f1rH84KpDI~+%+CV&gYPXIR@Q$D%ucrWb{^}fozC<&KpO2Ip7KRMu4 zk_@4UHpd4mtR1;qd8Yh_p}^by^;MtI!SGmjr4rgvFEN>`yZzADdyrdU#y!wx1IUxw z*rJcpbYjD-+bF#Zf0%{@(nhYj4ffqsi99G4Is z%+V8I%hA!Pa*nEMrH7%#CCEwpn1V7dD8qzZ?yZ6i|R7eM7{dQ6F^b!%I9q_F30NZv*+uB z+k9mm3vj^z@Wd6&O+5dsSnOLXbb^QtB)Q%1mC8V5rZ*QEa@~2o4Qi=xq^%6wp%N)f z-7s>p-h$J83s2~O$s?Im$P-NQcPfq}L@}0|ShRaQ50h?vDKbS-27QDVpTIU}kY!6T8*|z%_FZ)?_bPYED9F#)#-Y0o1d7;YUWm%2{pg%Sp zA3p%ZOH5TTH8nA%j<4)&g_?_f8Rn~kcu{5KWW{?{)_2|WFuD}34EWY;yPtd+Fw0w@ z3~;>qvwwg5+yHz>=dtzR?V60c0B4ci+^QEUj0Eb#0}TAwzC#6~WB7Z;=VO>223Pl{ zFLv40rD>JJx2l6y4-4Ze+7Xngsot|y=`P-H79w3U8qYV`J`R(ta(cY&JxrI*_M$~k zhO>#@GpwlAwqjG|reZidMm!^<`9(Th&T>7DIre3Tw{2i1W{Bzc8@z{X7QX4E zH?S)-klrlMN?wC6k|?qaTaS&*f$!9V=yz8)ELC}CVqELMrqph}FGnK9`ryjmK;>fo z>%uXVpU}qmBQc3(05zm1w11|MwfHTtc>yGvEsX(o!+mPNS)5t7=ucPpF)_}9@9Pr< z3S#Lpz_n@^MH`(WRJt;i56cVmKb5E%CGfyv>FAZ*ooP(Y2wY>TAQfiqep1973(V9w zWrI21ne9juVZgsWu)JvJYr?_s#B|@*sSUUuM#S$YCA{Wuu6&iyv`6+`7J1Q7tjWAW zwSm$J=B%AYpP$>uCPK#c}6s?v{2m zX~1hvta}a+1Rbat7(CIPQo6=9Cm(Yv@rh^Q33}O8^LJ^wwAC&~?1KnhnJ(JZ#Ua-@ zP0Xe^bMS_T;74Rn&A@eC5+Pdit$M7vpN~oYMjB^r$KI`wuY~Nuf8y#b&g)wmTS9BZ zlv;uNoUYSKWiCN|c*C0%7+ZM`efQz_1hRUsYR|-yrK134V(?H%Lam$f;rY8a2mF+UyPW5wx(p=6(usU-HrWL1jI`oxwpXay@RE~Jz+^SUw4bH- zH=tjHT&U31eBW^Q)-G3knFmNJ%WWxee$-^1?F)()oDtAUUJc#1&q`3l7)`otGT^ly zAk~MYq5xT(+P!3I@`!5p286mfIY-+Nd> zwVYzdBUK$^4bvNMY1T0{0_D9Vm(_Q3pm`=+Hn56isKe&W zcWW{TC@VC0Wc?W?nbT*W-lG;!y75~mX^w}t2nCk7lzI~bC$x~e6<_!+iaQnsWDfU3 zX%Jj^LJB+R)giKeDGH`!4i6JEF0`KRXR%@v-sahR@7#AaBzj1;C&2vtLVqq$U2GDd z@@wfBs?bl>!m-KmAvh`%Y#C9#hFSO5R_pvQyZ=`>9;LJVELXBeX{H5?4RBiBzWmqu&IJoen8#a*`lA1@hy-cM#Uq|H|PmR$h!*VLyEjPX@y`EVA+z!N@dQVpcm1GI|NF#<{y*XpdYvg;i{$?(@4VaMs zBMPQq^fu3{G!?4{_VyZ9fmZ0`=a6jY=jT-p3z9EExS?YtfU|qK8Fpg7hIuNKH+|9% zgkbi#Lv@ZIb_JDlzUEh!!ws(DzzKNH_tCwEDG>GOc#{cYb;mb--OKb&(AtN<7hNsQ z6vQ`jlWVnm0GvT#y5a(yrop2I*d`iU`%peB*r97clYdPuk^r~)&81?U!d5WjzSPim zP~H*rNmu?Dm0e9J=KX5g;{~|wdD*TKcSG>?aB$LUT8P4vcH*3IUESj`^7t%rQmb5) z@8JPlS&|v+Yf;CIJxwCu%GtTfYuV4JD`T{a^Ig5qQ2b=zAQlbUmE;}65D@5m+fTIZ z7w|F`sI7kyf}-&(yR*AAF3Rwn)N1Ptnhp31i9%~L3h!{2# zWl2>1bTg$Jidhl9^D_OVr2H*v5~?FpL#p7MbGR>(gM!$n!SEgSyg02DD+Qr)z1dx$ zni`__UbZrI%i&^uR5MEm8`9x1gl6p{Bt*3Y2NJA98{x4v&ht0S$yzzh{tVW&N+xol z%j}|m&(KLZQ3iggx~Xb#muCf#rjyS8+Dn0%Jm21^<{g@P0xbw+wl?`u(U0m*TF?3V zBNW&q4i4dI;khWL;zg@X+)=$n7QDU30K>POs$diH4tx^Rp?oh4(Ea?Gf;235jw$hy zGBD0gbJk!&X!`-|W@e4@Q`!YKP}wb`fZ-NqrN`f!H0pZ&*)Cc_fju5a`!n_TgQ4X= z9()o+^A{OD4$<BsyJ zFNvjkAvZMWU-|7x65)nslcg{%gMr~XG{Iy!t|aX_4TPEWD|vuK@Z>pyp4rdY68~5* zAp`;ZMEHAY?e6j>Y5s&8k5lEaQ7;)h|STc%=L44p$jjAT^TO$)4WP)Y9?wcNv5j7 zuO6J#8WLma{&u7C&3V^)m)?zsVvu5&fcs;TG_#TJzF$Lzo-6``&PLAs$T1eJ&lG`W zZJX_5aj&-?u;{+dZNc}hCQ+_WjJq~3E|?x!)~^7+^PYfVLFpdB_on%if*JOzD(B{N z5sn<9Iy|2Ml5D532_OP`yz#=g8Pz-Xjdl*`O9qw(VOheWsa0p&G3V9L8FP#ubOYEay`%r8i6i(k&!m{WcG{ZuYK~Baftl?8^#na;A z=HeD>)C0b_g*fbiEd<^0f0qH&Lwptz+Y$s#PSZb6RRz=rErvOi3HW6R6;Wq9E)VZm zD9chu;5)n)Z5eQn{$8vSIs3J!c(`$H?_V44^i;bmu=7`+$$4!Yj;e4Tixg7(cu;wJcd<&NQ_e&K3+x4DQUjQaL5P3_~E(0$G>oNS#i#zj+1AeRM4sZ1i>9K zK(#``V2$C>c3{CD<27f68(Qt6uUTgz;>0*+qT-Jm_QZDJe0Rpm&+j~F3op+rK-`JQJN{_h<1hu>B|RCluk?xF(T_l^8w~O7qhN0C_SAH3`E3DU|7ODI@CC;<1Zh7I^06CGtjc z^4#qEIStqzeuqP>*;+F?_U`*Moi}Xs1>r`$PhdFi?CM~Q-i7F@4t3F z1#L3Ji+E=ry>*?9{q=$X(eGrpqZfI5NC?CX1s&q@@xjh0Ep(x`4e@7VXBSu}r}f9YNgFh9njQ@TfBzccfG3HjGZ z8jpKw7aR>&?`7v}=j;?o_oI8mqh zZTyF84Wzid)@Bv%PqOorSnkTzI<=tiZVuPQn&gR6V>~fp;9hy~UdBT^{4kGKnq`U- zU?wBumVD43j`yI#yB)T*wY8z8)-d~#0u9f(ij%FCj{;w*bR|N#w6fxT??Q@K$Fogz z3_P>4@@Vevmc_ZNJ_b$jmaEZrvqUJc6VK{LG0C%UtL?{bFADy(%{`kwE##Rt+d2!n zck3l91!W|tXdwDaR@`Fds+RC&!-0BY0pAZ`BcspAg#RhXe*vsKzlvk_cYk#> zC|sWRd4S$a!Sos#Z4dBu)?gg-vnoQtC{hHK@hO$xTAbV^;6nrKu9KpwQ@ZNgu8^*a zEdP4>%_d^E6Q`ZkV(1mr`m;4Hnl4{wNP4J_ac;_nW8x{;6zcr_XqVO|)Sgz@*s7-C z4@`A}KL7Zsy>$9#K4wokYb{OOtW)gtO8Pzin(IDDN3UD*2Jxc4H& zHv7f+y?1EYYAB_IH6uuOU}Cu}>%N)#@g=H|>zc)ItNPuHY_=Mi9o&y@8- zEA)0=LwS~RCUE4DG!4*YjkByQ$N<9tbL|P-Pdk2Qw(awl#N*{`Ma`17 z(}9V+YHMxAL=~8n2=^Q6b8*PnDZbw7r*iej`Aevi^+N1POynZpHYZyw7$ce`?ScZo zUEkTo8Z3v;9Ad^XC|l8ALw`y%i{KnbhWE1o%@)b_dX7x+&mF`UzBf@aE8MKD7(9UE z@=|HJ9gwI^(h;1wM?`{xUpVu_QOQTwsY;XfOMJK6{?kkgNGRNpqj_*D$#lrSR?(e7S8NA2<_rYB3;N;OMl2L}lQg!QDH?*8W zvx5w8vmIQx)xz^Nu5Po6pe_lp1abw@TAePs!k0PhFJ8WoIM5tfU$l{7MCj5=_zYy# z@!`HL5Q85;lGji4#5)t?uRD8q7841r<C( z>c}s_i|8fL1p|(A-$i(IP^+ zC33Ym3rLu+CCFJ+llxErT9(6#%S=+je*+Y+1h;O#*%oXi)&FDN6}x8-CD#H3$4j?+ zi6@WY3MxafnQKc6eto2yw2ge&N-6csowamMlum!4A4iZzIP{bn{G$*0-3sX&gXcho zY1dSiKk{Y@NA!5=>gYmWMg(Y41$sX7?iRo9SWGp|8bOM6;ie_7%4 zOa6pj&=)T+{;yMl+N&BHTtiMvrhlo#s3EPV<$b!2YEgddFd8dydmt{VI3krJ*ex`r+r(P&Q9xf-s11S}96xc{)PD1SD zPw8nD-f`V#`>2GY{%}`Zkg1Qq$NvM;n?JxZRBFEGzcAR% z#}Nc|nTEcB8`zrR^^16qNDmFROT<9^ZF9O_ux7#l#{tL_R1*ajw+!cZbuHD+9w1Q&}!G9(ko_Ve7TVPvMK5m4sU)TOnDdg^5OMehXLeqeG-_)T5S@{cNeu@F3 zJ8&QHs6~{OT16xNx=aGi@`_}Dec(au5%Syb=w8O!26Lmv<1FKBr0>cTEg0rN^+E>v zD~qIMJ&5(c^Ni7PPk@cvUIG(86%OtISv`e*_bfz7Z)>JO?A=bEd5{TE_o2uun{TK z7HzBf4h6KNn?60omwyO4I%Wx9*>Tw(9gagbm^~gBj|Ip9Hjujd{r?R~u!LA@or`Nz zRC=kIi=Ox}QziR(1Qd;)3cNRLvBiG9`WFUI*S*&*ddp_{!&&xlOhnE<4lW@zzR71l zzBO1Aa=K|qfs^B(C6lphV){*8Svlw}@V)C$OWSj+8&o=epd5sZ)~r`*`LkRSzCeGn z3*aA>^HjifsP{3`r#)pdKq80xG%t)=HJON@JDv}Y*DLP`buFtmlnZn z_}HQn!y_dKwS}i*6Jd&{y7Z&mp@Wpw6SCNoxHmvY=o1PVC4}z1ha@uX%jtK%G#7*7 z9{maKoUx)urtWt@#@a43lKs*b{WCjTV0zL3Mez51K0r)|nWI&Y3nY8@#%ERB9(^DV zTv{d+-;XPI9b}nP0mxem{{*NUxIc|Kc~*U^t?g=YV&wJrD(eleJd&!ov=4j$)l6UH z5qs6$+TS%ezbxitv<%)`8Ov`4mvF{+>OQQs#jyv%Gw+1*2uAjwcDE@H@}q1 zg@sl6l=|LFk*AmMa6bW_{6VR)H@>YB6|Yl!`UII#dZP_EooQS;_7+aM6+wQ;o$^Jm zyWu7(hu_kVwXH#_tG?PgQ;#m*Bk0xKB5s4sHB!A#;Wo!bO-nn{5k;kK3$a<#nu`|Q zd4zhdZ=LUWiCmVC9E)5ENcyT}CVF3NSd0_5JT|w1@tbwCQ6JRWXm!ZbpQ5Z$*e1;T zz+z48QD3;SCiaXyM|0-tvlp&r_0Xhx>pv82al9_9Eunl9TKlufj9n&427^33bXAwe zY&JoUt}R}!o24?&x@%0f)K@pYE275w3|{-yb^l2{SO_ZTC}4}D2DCm#nKgQk6!7RP z?`Ocb$`V__l$4E_6}X?DWFxGRY~XeFc4Fh=J-t`=n5$rU1)Lc*xTYyl?M2%8+0s7e z6sJv6g^dpaO%_`Fm8q8Wp4QY>I>GH~;4m1I-8%*Qsx)ZNJ3Gg01niQ$e+)$)>$0@-t(Z@qGO{=hKk z>PS4gj8|pa3ifXNNmqoMAi1ezF}aU@E2~$J{0J_$p=EpiIj;u_OKT-KE?FBpqBmq8 z2e&9MqX%Pel<2(=MD`6@9s^AR8>JgBDEl_Y>4}s)^z~KeC&iJuKOBDa&^gzBo(gN~ za5nOpHPAN+(2GZHY$$i_os0!N!#Oz6hy2Xyp90aB3c@9;J5n4}K$bL%F|?@Q2yT~j zR0P)%GW+?@^~cXaKQ6q%w=F-x z4VD*~%b&;5i?gOKiU-O@5?3ezFBk1A8HUXCZ> zQG{iQ41oKjz?b^J0T(G$005QzttL^l!i}Nm1t(Hy)soE&4HC5Gebv!EXUoZ-p$%%` z`n!<4I1K5nuxgvF%FTbcf4o?bIWEn6Gl8o}ZAdKw^6(MAY2CvbCwgXdF^chI=`sE; zoC;Uw`);+9nD=7baYD~p$$I1qT@PygdBA3z%QMRUl*qj<`vt3u@_SDXkw18p?>Yk4 z64J3uD~%RX?8f{^<0pIDKe?8Jwyu7NSCsaYx?8i{$U|Jq(c2$T(wP zF7doWXNh{DZ$gDS-Kj#8mCENme~%Gy+SL1EJMb6orLDdD>iX^<^;1tXZB8uN0@dG& zEfjV_eDV-kFSepXIV*)-8VUpm)$lvQp-nCCALrH$YA>aez>S$V{f`&9&nzk~SQ^PG zB_4bNf0y2v_QHLLj2d&WtUvj}xYbaMM$a1&SweIOjnV_+v6|j3(Bmd`knhF)%9F(z z2Mm>b!8V@xFUKtX&<|e1C>$EX_ZdX4tiU4Tvj$|r{_bf z4$2{4D_@p$A6#V0{r;j9L2r6zDL&6&MTGI|vZibwyWC2ID$xNuQ(u}6?DYPy-_>i| z(vJRla=6lO2ZcXgV?=ZkMH02j$GILBxR`W`Cmc$FasHNKJ2tkqHR7#9RCqI`Q6?tf z@zCXAizuUFb-jR=3&i3$&Q5^o)wFzj9XxxlK7Ze5#NXcOc8jvxT(B&S#>V_~eL9B>E92SXSRj)4ESsmtMqcnKQ#?~nu(=oU zYe$bV4VdRrK_fjqm_11x*N4zV?pdiFs8KKe;*Hmv0zQRR@?m~pqRy3jijGe}V{NMV!QLs-@%5lq#E*SL<~Q3>uU&oo8%j!cN`4)b7#@tb zZ48vgPKNnHP7MULPn6^Timo~^m5;{?0H^TzWd+*NCsU_lxV_R)KNZcLKcpCI1@V#LqMS z33I{h!$gA9ihvE#6Z7464moll_Kupwf?$}k4u^vHXi_E(nI@AJ~` z72_|b_S5(#>QH%(4F=s@IsT~}AuA>)H#hQ|&)QR$tgLpK1yU-%n)rwcy70B)5@m&i zEO+L-yu7s5EIwLlX5gEP@TZf2fq_6lOdZK%6T#C1VOql0%piBCz_F7{R;G_>qAc7Z z5IrHGz9rYX9gF7^OY+wj7`1PfT@@OGvDO_*%fGlg>x$4-a}!xNh*AbQ0&|^Dsxon4 zm35S7p{kb`8x=6lkv!bZa&hJ=X?+I6!b_wl>cf2~eGnb2ZZjk;QrIo*CKGx~1mKjD zmiE5I3@QWP|H`bUi`tp)#J73K8wp%gR9)7$IF=v5sU~Vj-Pww0mK2s-_Cx0~peTTN7s21VPmsD*a$hTGmQLd0+FK%@93=TZWP%Bf z7NR`@Yx<)ChdO?>_=CF2xB;I~F)bKM-4EG4X|{1hHo2D>_BXL-qzr0n+OU@I5-ip7 zWDR5C(Q=MtCY$L9hi-MVI4Sk)9&Iv!T_DnKz2;qufvh-|n-m`?2?0(RBHG(IphI%4 zFjY|OIuL4MitB{7B|PnaGbHn*_VD1+>#PG8MQ z`1x{gM@{%tm)njyR2;#Ch4*ai@)#7GEuG&pRThI@@I z5SL><5ut+*-rET&%1}nyL92F<$roQo2mf*0fM*r!{Ae=WEH!qUfxi$pMldGgfdOb& zhrG@$SEhx&5d;`a|H57p$W($BbSC0QaRNY=*Egz+u|xfKVI_X zi-Kn$VJ;4pRoBR7>wLR~Qce;`k7~S=Oj%sGjg>jPsFVG%t`U4ID8KyhIRqDzE{*VNCYRYFK zy#VTbor@j~IVkW5J6M93+>>SCXcKhYF%C&CQiE6<0Qd0&arw2iP;y=Tm4x`1BZbxL zLD8Z6gBM8LsxS{t*=|y@rZ`xI@!tYo2aUhi5Pxf*vx+2TI?_FQ7I+4K+1~l~o?IDy zkD`1fg`Q7PP6(}EGxpOt_|JcB;k-}i0~}2cJcHyBpTsDkKK%P3WF_PDmS~>fJ24}e zL`79YbWhyxI7y!EAI11bx${9~YWTlFYxn0s7e`Q+%OdKKZ}4$q>pLL`57& zjl%tvj0uZ7L7wjJZ6}wxb>s>G*QP8HW`=GHp`lSkc?-ny(0xp%VvEm(ZvBXE6d^Qe z2lHr#u3V!;m27{w1mY4ref{4iZc2 z+05r&DmlQ^D~!)DhSd>-YXL2}U{XRO(Mine>gq2aRfUlc>#uV&(&V{;4^x-985t{F z=YD>Sh@aPjNpp|a6p$9@mE4ckmD$a2X;>4Ac?MU(HiC34 zHf{c!Z26s*^npS!-GNtLk*IDbN|-*|^E@XC1v!$>!>#9lUV|4G@N*1^udlCPi8_Kw zin%oR=05I}0L-UuEos?}nqiRc6XS1F8JHyRAg9k9t4~y_D8FC9MYsUOVuTIuYLXl9 z_BXDgLlhWt$%c3WXc49ATeZ~@0hfL)AIpze(?wW^<`!-tbW}w*;FUjvcNkJ1E*mDm zBmE)WLIK{j&KL04b^|$He-;}wu%*AS0sWZVsj(LwHLKzi#{`()uGIw#a7f3g+u3$1d#|EQy(93|RaSx%%v~Cx^8qy+{A> zbeMa1fbk5f_vD$fcTK;{xVGDkfOrkjc@&@*?blgR@l0211Yd)0qpR5lSLEqrT>ab$ zZBgf@a3LpTpzx>ej(lv{hT00JxdGf)K66R-? zTXch@aG{(!7Qq$?blR8_QC5*UCV#}aJD2wTUMp4G163hr$yQv=otsqce!tuN7P?~Eg?6{KNYb( zhez#(Ajw`6&OjVzlZzW>*EV#1Vyj{~Oy@x{3x((RYJ`qh$xn5NNLP%BPG+hOO~etA zS(Ff)UdlU{bllkS@$quo6RyX{y1SQ0t36Bb=dr)g)~5fQFTlBbsCe~il3BoaEG~{H z{^;T|&aqKxE_XkL!KxvuI%$pd#<(FqDs*4EQgE3pGGwTH+xCXyUkf296(^hihO8&J z0R>Nlt_*((`)&@n?l+mjXaPDjNBd~97BzL+G5|skLlUCmY^7RqQ>FKn)Jm( z-HeFsp%}a#{KS`+Bj{Ljg73WH>h}v{dfuTAWPd|^XPfTtk8ge_Zo8qQQ?&*PekU`P zWsU}=vme8*w_%j1L0xpx1!rj_?Xw00#7bSvldK1Uz0(I8#;9?g6TiaVA9p#s3_*91J*&K3&>D=l03dEHnp zLm!h+rI1lU_H@qX~3eX0u{si-b2oW`T9gwjfzgC|6Rr{u6R|s zMt_@buVmdvn@z{U7;%(^9i(6!1ZbI3gO?Wab3i&6@(Stz(sOGcrP!4k*(XS zYjvTaP}U_2=!`SKHx2xhd%j-*WX%!BXL0rpdynjr@QSCJtQO zj`eyw81WN}6c1!O*Axj?cS^=y5C(trX1>oE|C3;w@0N%Z))>1#Cth9M98Hday~}Zf zNY%1%x!xHYsvu~EMtjpx1z5Ei2~&{Xst^o-FoGfSE$4rZNpGc*S(6!K4y313J;Bt| z5szt3Ot7ow#5lYt47AOlCh}zgpCft=7dKGpy9o9&sRPVHmNH;`5^QTU%oO>nTiV&{ z0cD`O->&9hnR`fTYt*0&rsey95>f53P+(Jfr@zMmoe^!}mmoq-38;sII}veC3&_V!5rqkXXP=81Dk_ zey};Fs6&Ak848ao;k@@NWrMKcjkRarVmT{W(YG_gk=)IFEAwW)w zn-BtpDEkrHGS~Vgf~7>Fcuw#K_19ceb853FpHaDnr@v{~o;@Qmf%qoCtK}>m&lX)4 zh+v;joMTkxG$W&D_78aD|8O-3N_0 zd2@Gnr>hQ+{|OeEf?ft7M(Y?O5pH?=`4_#ZlglC>;IWC79os2AK9%))S{P9t>RQ)e zp@+C>RuxJX?5tl=S_(W|%Uz#)89X`^iCK3)!e)D3T*D66Z(zxk{*bxB6^zH%lE6)h8& zYz({i4g){@5;1BDR$O3FkItLwuFF0!-&FvHk1RgZMw#P*;-bvms+tC1G=91QO|h(GZawA~w4>!;wigiK=A0_hA)kaWaZQnXu^-n2?lYw6}BOUwJaT zw`+^Bh_LEwSJ+u@_ziN~U*%E(h=>H-Zk@!~uG^A+iVrXc6!FOiV<*py%2n*`aQ@fp zsc$W_By1LL>fK;_oXl5r)(|N=b;sB++Z|Kuey8sw;;oXg2}kweomx9Coz^hIeYHj`4*Yl~ zh*)dX;d;R^+z1^#gE`6o0nB7L3udBkrxm~s6CjR7PM==8zZO&mj+!by;nnEmlK@gC zz}z9Un=W8;Il{{A@Uj0AOj!JI%g&&JocIGSa2|1MLFZ&0LtFg!Ed|pU_ryk`5Do-J zzbkbNW^nH^8AC+r^OI-b9NJ9|Ni)Ro*NCRRU5PEjj#~ZGycE2hyfs&KVh*Hn0EW+r zg$Hx+U2y*U*I!uB0X3NKEKqX&YHnEm<%*3x94rCP9E(89}WO`#oLZ zn5wcZ_}|Lw!M2M0M?|?s#swVEZrU)``-yO!67V_!-1~(svNQbNaRDqLz#lOUChR^N z=0Np(l0gy#&KRH3j1ZP(d5O)Ou$6#FiNkyw6D}WAK^17jhoHZTX-W8kcX*n>J-n@e zo7FQc*n!-$>9PqlXfgo3IKWvmOzJ3@&_>IGC0PjrGG4ySH%ufMPGWj>&*S)2C>m8` z)DAJhOmUMk+CYTcLz#bmM`r4#IessT8x9ig$VnG4-uq_uVw>YcZ}4?TJ~Y-h%W1nBI(qS)W}qWqO7Jj*NEmCmfCw3?ZfZ0=v%E2xOt&IxgE3yu(5oZ z3iZcJL3TXF-qHQYaJh_vWBXN588OGX=Dlsd9$-*z``IJ%#ri) zS!t*Gv6>>UCvG*~T;9*Ty6Obd+uj8-dulKFo8J9hiwA@8fu;bbqQky#YJ&o6$RLdalF#5c7?)2jl!0rny-Tl?XeO==dO6PP2LRyCuoA zd3z`Pokr9KMv{hvjTH}?xa1})Ta^T<`3m+(c&wW;>iX7WPs}mgaNfVV6(3{4iAu!4 zex*56sh4+sG+!zG=UpVvW=(upc+J?be0{LC4YNm193yb= zVfZ}G>z^eLEubkc&%Fxy4x5zRZ}d# zajW;Zl|-K0{Gd9_0vA3|Xi+$^&q#)1D&5FwKpU}Ff)pth#)Q>dJ$!%LMVd?J+UswMN`K(vSi0f`ZNQ40c{C z#=LX9ftfRY!n;90p3q;V@;U+DWd`fg22hISU6$l*f^b90E@Ekgd~7`nTok@qSc(O=pl zmjBXOa1zC;-Ax&}Pn2|%@bY~YI$&plU(X^UXB?6uXmSgQ?RRBoD>Q#vG4}LIv__p$ zdOwRiW|s(J@Mn9j1@2)BAjuu!n56h))-ZtAtI#o$>Fi9nf;B`ic30{-CYw!+7 z)XwjkFC>7&IIcb4gQM}Mf_{+0Cp=G2HEydH!?`<1kCiYNh8BLwn*2|nKDBHOz2a#K ziIFP%Jsu}%*s|RV3*Uj^{P)*|w(HHUa1R+Hh97i@c?5!5RHZQo*nJ0Cy98Hqc{|eL zS1$yK)eHyxrt>4Xma*+g6$(63LIg_(tgOq?{n8WEN*e0*qunoll=64r2|3#}t}WUa z4BWdU!*M0o*fOHaTi~6-{(JonlO!!~kJ7xqT*WjXT{GCBv`8aUY$Ns-CW`J3C=m~3#YR915*lUlP9ao z`JF}yJ!|5gF;Z$8kR~~~MxBAT!aaZ&fnQu-{R+REPnezU{D3Mb$Zf6rnZViB_U?PV zhsm^hDcTku&0R3sE7bb6tJpD;_FGddIM5s46ErIL!EK%x!3W)>)Oy;c-aM<%h}c2V zy=(7ji6r%biQY2{(T)Dx3%BvYB6C>Rz0Fq44qH`JdV1tIJ2cYOW@p&IIa9Dy1v}aH zdGct{T=LVW&kTwwiv~a-Ma$ZTh*+778Af+%7B!zZ|C%-wgWLDae}Urdi{FJmM1k*t za*~ridYivx0oQ)oc^FUK`puptZ+Mn)71d43m|)NgG*zWyz+f5$IM2xVl~R5ixh4+a z5W?4$%$bpt6ifBY(w6S-lz+rIjpjxkd^95=+I)=li-&VxGm)r1;w=db0|&OD+v%d? zk6`nCnfpSG3Ck3bKYq?!2y$C{c09)hIq)xjhFieh>BGC&e;M3bc_1zx1Sa2}numwX z{uazOBjPm)+nQxg?5A9QirqUW*#6QQ6D6dI)eVFjc!jtgF1{L8$X+tT361UCP&BAn zdNO??YEd89Y>uxZkklqJf;r=@m6YxpWgkN$G z%OrsGI25?DCEn?)YOqsMPzZ6oU2nKkl4BH59BW>hnybR@6?8kL;Z1*=jBM-meoQNH z2Yfj$Moxt9MD>^!`gg6f|BGOUWUwkuGWx+?^#;-6#r475`P2a+?oX zlX8KtVScuxHAo7LdfsBfvO2XEv$!}_JpALzLDz?$h?#FwyhnfLsIWiv`12S zO(0DYN9TCU7f&r(-m>aIfFx{=?J}Db_FpgW_xYSX@<81Vym(63^BWA+efeuslXYft zIs$Xwqr$he5S9rXMG#Jh$P&FwRZ#UCkm*5A3qPh`rT~Ou{ah{u%ZQ+*iyWUaVcP}( zF%79Eez#0qmhobp8eq)8&AZmLyP3{&S&QOM#CzT=&67vm@Tm3ZLub(GG$8n>mD78D4vbtAK{uv}!ypEzc9K6f38f|CHOkjFb7|#RcJYS%6 zMeNPyE(1L zd;*`7f=u|#wq3&x%$NaXYoh|{lYJ@(P3G+4(GcS*9P7OyGef{wGnNc;A2_qud3*o2 z|LAC+R?S2cUOfrir|p4#-T5ziGdjk@78wqTyk%J{1&Hn=wC1~}u52smeyF>s*&8mD zVYzo3ax#Bcv}af)C!+tEcEfUZ;BS)cUif&l!=6Aar=yc`l`0r6^2nf@g}`&B^6Ib) z{Jby4)3isZGa29g6LIO)?%kwy(UIUk zkx8NV-qt#G!^pQ42`!1ahsa7vr^mm=(|?B){)BiIRirbtLX~@gJ}dq_Dv1j z#MT$#a!-g|v*HS%hYt=EUXHyq{Na9a{*0bJ%B^HHAT_02# z2uv{rwYQVn65)r)ymwLjm!ddFOGeiH4S+|{lJ>dRUiNtJWbr{7e6GgR`4f)((QwU+ zbg30`wYzmpM!L2*wA+AX^y%hIkyx_e2b|QheFa{<7jJdX>|THUs@e1j@|>acz}~9o zn?1i|{tB}B3;Rxv?>9^pE{oQcNJHx(V+#MAN6JQ2>C%a`#%5dz>^;&wZvx`4T$6 zyAl40xlFfh@IvQX>7m-6WD!(xqCbf~5YDaMBHwHR!Q!e8O-VHHRb$e2*WZwT#Ceta z=VIx80f=Eq8)m*MJg|zXLn18&9xzd%Mj!kdL|{heJ#KzOiSJM z2jKg8J&OxlDZo2%meYGt+>auJtK9mBuZi+%-_&)vKb{`0;)hLx<$MbH6`o25t1E z;}sU#&W%E)_B1&EEwF?1iYXI|MBzj!=y1?}UE%w1q^S!ndAvO@AVeOJV(mp#sFhh} zK3FQ#{-CB37B|sD?YQzRyPf?@S)`eZpc8UcNWPCg^ ziC+uU-p^>ZeKzjNfH3VP!dDMo9Ykb6LweHWg_O(Jp5Q#G-!0WYT#~xjM9Bj)-q>k0 zzEQv*dP?zjY}|9;=KSh_mgmo$Z>w%7_j zCMJfAG{wJgYfX|YNm=y|jI^c<%q7@9o}az9g4B1e*K~^oQleSP9VcDVTF?fkyxSi1w&l?wUqr2ROxz}vK~Un=Sem_gzR=2mdz{C?fRX*}_R#M{xF zd()n(gxc+16R002-{nO2Q0au5fgxI&g?;`G%$dI7iBeNZ4ug%y$TBB^)$MCIDmNmZ z_sT{ECOKl`r1X?E?K8-;8p>A~dpU{fva}6|H1Q6_xh?HsYTT9=#p9NM(GV` zHUYo(n<(~WeRTubDgk!c_be*Rps$!|HjjKLNmA0`_CU7jwi*;FE-r5Tlw=cS z#NITD8D_zmY;E=LD_W3oHkxm5y|$Ky&H9!tw7by4;3+>gwdU7IYppP2B@Rqa`aa!F%BIhwEJavi@M}DR%FsvAyJ9y&5H9$1l~e3W z*YXMC#f##YF*zmO9AEs{Y}s&c!PyP0%JY+tH73g9PA1gHapb3D<7cqdoRr_CE7719 zWN9$XFyjdv^7uZX5aoSDxhV(euUf7o^xg?xbIDLOJ~6JOVPE`!@_ohR;vEV^3h+(v zzIsn7KzNr47ZySU&P*x-8MIXH_s&Vxkfn36QtJ|n~pdA!BaI*Jwk$NUTFEQ@MMW2r;1C!Hgq`I)m-&Cb{;eKmg^>}2&x@LKK+2= zrzpm?k?{WEYN}fOcAy%sE!>VaaUeLOyJN_JXkbrkJNV)*qrAg)~S_2(d@P?p+Ov+&k7ixGpnRThAf#CJOgm zf$0Oj_%{wa;!euBpGhci*;fg0LPM}$C^i;MsqyX)SZ6*QjGcW_)?A?dz3?XjF?isc zqxq+MBJ=|ZUB^y>#y5AX$MK%Ci+8Vlew4ilUYMKN2tW(&Jsinewj@C3$1+%|;SXio z+`E0x{oYY*|Lvl@$DC@0O@0i<#oWB-!iwbT%lGSB1k>5nlezT7mT@ACREKjr=;wIm zQjJ74I)dtc5F0to#s&JCZkY8D?B4=pD_5|mEfzdPHaTbKG+XIR>_r0MJRr^^w4{JM zj(W2@_0P^ctl=6QS|)rw%UmGgAll~l{g1B`-S|~u!6UZzKZL>fVH`!n`#jqOz*Yr7 zdd57X4tjUPJceKf2f*GSZn;#8&iMt`v#rsa1)QXm zPNi~8?T_A+^D!xR!A))+YbxUc8YzBg+HHuxJ%mt~tSx~w~J`|^usXB`NH zxe${m$DVhD@Ah@jXlrMWd35-w%yRA-8fouefmQw_w#0f;Rn7x7x^p*e`e2QELaw}y zvtgzGU-wg4;|jvxJFo{=gA*fT_kk7^E8}tl%~HN&Z{Fi9;&@8u>Bw`5Ug}-4kLXcI zcKGzYRT#Eb^Bdg_3&=Y=$+%>Z`eldS`qk6M`-u4^1cl^YfXSOb45emdq`P57*DSqk z;PVDqg`~NQF}STlw4Sn-IF#gkQ#

fWyXHoEBUsCi`-zoR?^nk#UogemAMlmqMjBy zw+gYzR$MhIr~}e-7A8Fx=pWP?osSyAsb%Yx1@exe>L>W}0>vkq6^?3UBsl1r^{YR` z6n5a;IXEy746pKphX+NhHiI!9h2;4omf#3g9-LC%BFDVyg+kiec7zvAJk zLMPJE)271Z2;JQ;35M+s$;$>uHyj?6g&*T~v zqc~}ja@FJIy=n@@8dKC)3#}TW%d(AM%dJ)@AMOzlj2)azr~rRFLkg`Jh*J^QTgxop<*d{WDgpOg(Y z<*518cPtvQcK6~(L!p!b$KBR%FDCzoZpGVEFz`?1)&MrC=&IZJGn2`q!tD&;D`?AV zUIJHNtn;+L@Ds{)6-sVpz%^RdeEZ3pMX~l60*bnCt01R7#*Cwtw3uoB_5Bj-;!EuQ525 zAy@AMO0hqrCr`AmF35B{pJPKhn?bF2q31~I?t;g3OK`tn(p%vibNlPgCz^TneDMyN z`OZ&qphLYXmSN=9H>*z6rG+R?)fs!{w(U%Tm|$F5v7gm51mVAT?6zDD&sc))-PbjH zXwlYt2CqaB3Q0xLJ$$@HU@4<+LVS*5xhnF&47QP~p&f4nYhv!OjpW9LMk{RS2S08o z5qnGK-lo^FZn>gRm=pf!Ds>l*H2l$U%i;0YbZhbl2FIOt!~zO!khsozd8BR;6w0+g zGh6G5dTyOaTV7Q~g|OaO>Y$DIe%@Atm%fkA^VS&qKf2yCtf}V-7(EFAsnS8EcaV-s zkrIlCf`EebE&?iDL_kV{^xm5SK}E43MS4wWf&zktqLk2k2|Yl_xrg8Xz3=^WpYtKl zd9rh6c4u~Wc6MeqjC}diX7eutYj$dCb86R$EeDRXFJ>H6ppaWXckRKue7|y+EU6b_ zB;Y-+T5_K$&-CTLNN4x_@&(d1@uZU&v5Ywf1;fK`@k*Ci;MxP?c!$pTMhvxx!(S6u zqZ%p1aqq@eg^PzJ%msa4;7p}VeRH&AXnUm{aPoYuw&*XdCMzTEBQ^gRD+~~Ch6$gS zDezaZwn-lXgBiy&5_{7UVN(?!@J(~zz^%gPXBemaxn?K_?+iZ_uePSy&)F+=vE43r z;A9;^4e`-H^ylMH;Xm5$m*a+VE$~nKYY5v0vX2Wq{`s$WDaTCSPe~Z_O=d-v7v|Je z_;s0C>?p5e&L!)1qZUiO&T%i*MX9`T9>P52)w9oh>|vJ>GK|L?pC~Am^5ik4(0^JM zzwcx6u~JL_Yis(w;MEyHTwJThC>DjhYi1_0Dh3;CY_T_6SzVoiQq+?j6UOlb0yTZ7 zDfjniR=29AxFEkqyAjGr;LrmiS@#ovhJt2P<4!i4I9Dhe_k*k-wG zEwacpJK6BM(K*X__lzcE-6_bK7Cv;-i2|G~TbGE$Z07Z}ay}A>eoiu_)u^6xaOH^m zvi`mBOMVA9Tqf!04I2C*j2vlZvu<9$vPFBJr!XvkB^S|_algNp>7KYtS==KY%+~8P zhRLdyA%oY*n}Zq?-2><$9&fdmg5aMFwzF$U72(cVm^c>1Z}4dv+c9=Tw$*Q}j>$gf z+xX{}MEsqO!ij*c$K;3P=C?bKy9u%deAuGBfOOQ7xNF^tsxP-!Gx=(6{$(Q(=Nxfo zW>5P_J8BqNgnVvM{y0pWEjl=C+q46hXD;(8^X%K^!b2@9<=p+_!A>?#QVKm|$0o|( z`=WUES(#nJD{>Qa^lq{#tC3YqBp0jY&8jjJ1zPX%GyE#dbyp~&JHH3blw6gq9oDd{ z^3~-TH83gRz3U?A_~Ft7t@8Z|17X3JlL4?WiK;hNIhzRTWTP>BHzm%7qUWD0F zRSu}>E%-P-ZJta`rSZ0M{oO6>JY}%reD@Q`g6$MMlFPpGYww%rOzW+`hzkerA1B@y z1*2s^wH~a8!r5i&G|F(fke%dH%&K>2|qdbg7MERl(n+;+*+; z`Q^7lt%k@qk2LQf>%L)ss!(4~{f1%#t24lQ@z`XP_5DL-_|D1anDB~#%(~EvPi(FczCruK& z-(JkG0j|EVT?NFsPO2MYg`#io?Tc{IR?`;iE0P&vD@z?Jrpo<|J}V}zk-|xR~)r)vHY`nF2e%tts={wNZba8%k4$lN$Z~SRo>F&GiVi6eqph4nP;Ws=b zupWv%{X1{5{U91+MLc_z?AEt-P3U&%{ae47U~`=bm$zP_wOa} zWP9wo_oLCNOmGucSk<$4Ln_Xo)k?GPCLEDoenS6e@s2TJc~fK~zd+;F4YioQo6n;{ z54}=RZ~V?Xb#4;z!)Ev7U|*OCDdtiY((t9ejosJ+sglKceIe}MlNa&IZmCP7b5{B( zB#gw;`Tn^Nc{)drJo^%E=;4Z(719rKW97;;jQMrR8UASxTz@a5P(_fqk|l=sPsG+f zT8XG6fDD%=i&J>M1_EF1ggvzvM2lMeS-M~n|5D#-)R2_sp%i5%pl$8D)qxi=v zu337Te5qf2Qd{@Bn<8zlUZwh7etb0@V5X>7gu$^ z#dg8;2tmX@o0PpMIJbF`RGyl$CuZ9kzWr! zs}|oWJK7Ne6$Qy${PB(vj-5A2Tr_|le>MwZs4TeGMqRJDS=-#tj^1a5SH67uK3_=h znAg-#?v(DzzDKqM{FSCNQBr@IVa4>d1uvTPzu^rX9OGsc-hKU&Y*Q5~z+a&GKysf& z=Vud@>R+WMr8mce7xM;mb{gJO!me|_82bA;6Fs5sS3iZ7;)d$|lT??8*w#Ak@Tnap zSmJMl^$yhyrl%@))PqtV&tIkt$@q4ZzCk{#@VwIqjUa2i<9;f>DepPtpm4{zIu0B3 z0-5s-`&Hh%z@l)6i1Ira`kL!&fRMY%`rsQE|J#wbG_Zezsis|oB!<7MFnX(Fxh+f$ zUP~dU$#ne9(eM|qe5WD&<$lcAoP)DelgEBd@+@BJNvHZ~ja79Z&7YwWRsE|ifn_1g z6#1>3NDc3M>pVOakB1)dmb7-i-F#%Nqa(zYlX*Ks)Lz(dr|Zo#`41L{3=ZZOstjAm zUKZ4HoI}go$15HxisL&e;m-e#R%?70@DGk@430GFOyJoTcQsA@41Ua_bo~U_vJf9= zTIJ(9*kG4O&wNxuf4$gnco*65%X`;h*#GmI?{CEBO>30Fj`t2lr z+8WXMFhP=6bym)guE)uBlYy*}?MoXdXJ2}oD^ExQKJl0wqn5*HyOn9w)}3C|0~3Zlz0h0@v3L6G`z@zMvg!G`WEa# zaHm?lIg|bPW!-$AX`O|$bk`td-BlptvPJdTXlAFTRQKKtF zG*c{Bm|WlK2A@4wb8YQ&vNXNfD^KKL9O&3TkKsEkp*hN_kX&eH*A7peB`UE3R9V}8 zYu2Cf^~|&h@gdUiK$q>pfk@2Aj!z?&ot?cS-t1H0<^F-$bG4@eKRkK1&2g}zATV9N zP!L#!b~Lc(zmuVV>kPnG z#LK+k9VP^~FzN1r=QYyjdeUk-$|suC`*jZE=;X9xVnHy@P~S#YxDWmLL$A~_YgZ`h z;+4-Bf!+~hv$wvodvwV~y?YFQSSBv2JQ~3CBRm;Q|D~|BTwrXNI?e*K+)r|*fgN&F zAQFgG8cIsvkd=i&J8yD(I7u0o-EyQ$iyjytn9+&}fT1lQ>J21l1McyL1z;d+sKJPQ zliu~2@{`f~?@q|?iYLP}(K)i>i)lv#RYb9eKWg_TZ*;SGCIFWu;QmlNlMUR17S`7@ zztkTQ%JVz;zA*X4fM)MJUg5B0kEw_sqhY$4oSDBz0^lfq>P`cS4yY=`5u+^7 z2UP3w(TW>gf?IA5dofbl-nYmPufg>pYatqDsT7*wPGr2T(Xn}Nr0i`CJ9Nv;L|=xj zY&>Ak0e{>d_)J+j^z37jDt>2DoI`mOq{uI@vn4oKo1gVtke=e*<4X|#C-TnR88-Kb zo=GwowED+sIy#-l?zlmUx+6uvp{$gbtVjWwXQqGYvO7wyAlrRkjJS-edf8Ar()2B! zz|beHa0_`%1@^9WRC0yro3y)KDoGTi})e+Jv4!(Nt7c zo_LG2zIG50N6J7tsDcG|r0SzUVE$WcNhZzOh1SDgy#j2s^NG~_kpReyQu>C{($Zq1 z;Pwzqwvw8Ksu*8#)8=O)BvCtdbKy!Ef2W5ayMf9^O28(6i}p|~6ywP6u!SM)@^;@KNo$Cer=orx&`@o&gIsPNQI7m`TFe0Mi2;B% zLL=$4fw$+TbhXeQG+fVoVL<`k*8t{Ow{@07Mseu-I78BJT0kso+j-48J!`%^cp zW7$?LtDPh;Y`NaFN$$nY)EpUC{EG?HkRggmVp`}ptVD00EAIEYs`ROt-JK52I`(wU zw&Mo}!26uOmxHNr4s&k#INuyZ*>aqH#tYt`_Zv&6b)NZQhj5Ie12ktD9OX7Ial$P- z0crRT9qtN0hL6rkV>&s+ON}IVR{ww=r2MY~mCxkknG<7kjdB~G1bO?w1VG9c#)8Or zy}A+)xM~9t=ZbW3L9rCIWm;KbgZ1i9;*f88v7)aaR7N4W?GIHnrvWRyp z{rB&^cC0Xxrrb^*G1#uC@}JvH3^c+Sp$Q!-4W|<$@7r#SS1eFkJQy2dEmGM$1FL5T zBNjugJcG?EW0W9v6z-mcbc3~JCgRj*Y}sI6Qr?cSI0o1N^J}ALUj?{>vaHKFhQ>O2 zq<%Z{L!lE@E%Hqa{;S&_-hzmibC%4E?44kRT~2=c?-UAsq;}&TK4RcitUGE$v)Ma zeig@=+iEd8kK0f;A4*7BHvdt4)y@WcfRWB-a*Vnh>`w=H%XjG8H080}-pr*m$i9B8 z!AQ!9ih#lk3?4Fh+1tAqjR(a7wY7)}I@tSrXp1_4KP{}c6Q5b^UU4G^Qg|;d?&=W%EL4TjyY9#!z4fUD@M%oUp<2zEsNR8p3gc z#c_iHz_Bc2lIyC7K(IQ)xrvr@8(Vdw3;#ZmzeY~dY&+Q50p1067YTfIh6UcwhvV~- zA)ed7Z?(W!uCa;NNL&&?o72k)PN}{7XkKFu%(W=SIkl+-wK%E&G@rk`zd|1NTO2RR%5?K*0xx3e-9;? z&W6_Q&0b{@y~T`PICKi4aNPC&)H(V2a*?HW!F0ZYu^rfjOj&#l9;*nTtA@GSE;q{>1>aXB5SWC1jR*m$JDYLX%Z z7mh6~qjsDRyz;Y)4X!|QIf>u#`e@K5g=1;C$c%UJ5N0n>ZQD2*8}#j1Edpf@(_DB9 zFSBi-8uo7PF%?8-XlrW3!Hgy^rJx@?O~zNdNJ`mMJ|9^RV<%pX_U%CV8Aw*w2KcbU zEb9=KdsI^b2uD^AUdx5^xkHbRt6V8@)btPj#Eg{~#(Z7i@FMEzpUf+hm1;S)4t807 zKt@3}@M#iQ(+1R^gz%ghClz|uN)CNQF5=MFyZZ{P;H%aNoyW)yjykCbm-W@d_*4Fr zKBIu-?0qMo`W&d^ZL{a=2(GX?-gIT{7JDx9e6}F*p?YbLxzQcHHPyuQ} zXuI}#C2w>H6UBJFQXQqh=UMNdfzQoe2Sp}fbDC9;JHY0sgj+Ohis7@<3{#OzMS8fF zY)YKOjQ6}%Wg}@!1nhjqDaA${f$~~D6pIn7y~tF-C;fGZ2Zq4%7odU`HhikZHX4+t zo*YCECYzg|Fwm%bGHK~UX?&pxl9XiswIj;SV*b4aL9`UtE?>4Z?1f#=HDzc)v}B>E zrdDS31*R{&J8;b)x@&O7o1vU@P4TIjVqn7SV~~1fms=^`eo7n@g=VW zK&V}5O?GB5)V4baPd10R`iGE`GE za5Z1Um(-fIBiG-}mIpOc1n0_kc8qRqj7@S68J?`YeJoizswQ@#QthwXfR~Mpv)apFEHEkPq*5b$ zhzo5;4b1-45a|uLNJM`3@3({XjYY&0dlT`_H1rRe)p4;9;-1C};97-|65=D!hr8dvfT;4|d+b zoTY400vSi^mr}B}1yh==S|799KI_UVWQR2iBL-QXls8cXud6ujSGkup@n%ce{P;v; zpXmJa;cPVcE}J8F^6z{+wdV1fd=zj&9%uX_|Mgt|$M#L?9{-(xJAaOiO#SFc zLuWKPt0Xi&fYH;BY`3sa4)w*QC3XH&g~}q(Fx-<3o~#(g%ZLWKP&!UrzqjxGD~40b z{vu)E0mj~r^6k9VU1qeu0l<^j4NVy8O5Uab6yUf8mSFWhb>j6yfZwy9Gr;sO{2@<* z-r|sNCcdmi+;$j#ei2^bMN=fWb50vz7ockO)sUXWyfV7$OS#5^8YTi?wwUf^kVYgz ziC?=m_oc`bin3(Ax1{iHyR6J5Z_0{`@X^CUCivr*#GMsWKJ3X^acSxKA#N;HYY@t= z14@NM7?|0>)?$I?7MNTVAZ^mGk84S%#Ce6T)qa>TLp9AIFW#33Rc525AziYz5(oI~ zO$`h;tYXzq5(jWxxH2Y9!_5AWa2N71Ov)i*TjR+Ow-};3Qq}D@jOCc{@81Il4x(Gt z%x(GRfG$0HKE4`iT-SeK5>&aA(>sq@m{yKsi&#{|aZjXe6LTlK`^vn= z1kvn|W6qzZ64vM!7fkL)h-f-8q3l1Ar9yqKkCZ;(;~>_#a{WtiJX}*)6$FgEfhKLB z<{VC(dXOdH2^PiGPC0a(n?!t5c4Q+A_BQr9ItQoWu(~#XzOX6!+d0`GrdAjnEvSpw zC@d*q)-ykZXsAuqg`ztrWVZC|_8{$`m;vTB+dw$X`q{Xea9 z6#r0@#;OlKABp$5+j|*8QIhD3X8b4tTsCA#S9=z(R#BlojVOtokm9f>Y^cj~nBBin z>)gdH=#W8=dGllD7Ym6!wT<1r<==#FDHBvp_Mt;WS7CSliX-)MJE4(StVOxgev3*= zi_d{lE2Y`c_~Wnx$*61JRNW4)xlHf&%;~V27IuKd(Q~B3SI161jyNXM6?{M8>*LZ% zd8yoCRYiO^<%{ZP=0BW7c#4LcFQLjme2QRrS*z`ICvBEE&{@Ufe2%Vu2K-}*@H*Dv z+hx4v`dp0F@TmTt%J5vE>DnXm1Qq$@>UB%%A0JfTC@N!0Uz!XOo5asUHuvXpy0*H> zaq1dfSoca4b_Y`O5w=r_REL74_vCixHF}yq$Bv!#1DIs3;u;1?EBV-l(M#y=t#EqO9 zo^hiy?*pVy+k(vPc&S47=pbziFjH| zTk($KzW?FKQc7|8V_`5T2Blp**|aUB-`h>T^z6^O(2R|=h&(0D=Ya~rh;xCIJ@1&e zB-9bMBBZZ-mzy2z+yqr^D61__e%g|MH{Mu7{zvLv(i!AQ1GekqA3FWNrI`Qr%D=k@^aJF z>w<_+L+MWJVHhyjn9HF56yr3K`x%97*- zywhLaRN~3kD_4BBc3DOUrwpb6hq|uZHn(JuZQi=etx_s^M5X*{TUhQWne9_rv6$cu zdO{BUkCLuTmz`A~fumg;^gUs_U*#B&r#vk?=L>Wc`Hb$@`t}dfh?8S%vhh@s|3;v+ zfm}FHS(5cwQbjgJ6*D$Q=k_B%d_vV>-l(FWFl6!NxvAYpC9IW#WepqBghCrIebF=Z zuk6iK)g>($T9xd+y5bZPLQiGPAjcZSD+oY)GrcDS$=XU2&#PI`o}dX@1RW>ayUJ$p zx~J_q71`Qf`JT65xKZ`QAW6bA5V#aCt!`TH>ATJCZY`6<>*23L7hm&z7<|wRqMeW*%kco5nCrh#5 zbo$?sb{mf74hv#82hcN#NZ*MaV_90Mad!e+TYPqVob+OAY-A^FSrOxpy`SD2!i*jz zakjZ(eZ)4$1ny9xl7h7FpVDh%JvP<1tfh?FC>0Uh^cMhT8|^zeYk@_?mfN9*)t*Dm z)0S0YfE6&63?{92RfPJm7!~B*=YWv}5QB8^uy&-GIWWMCf-2Ohw;);{-}ziJ3Cd6d z<6GEbe-UntQ0htLfX-Ixsd^?rv&Gt2fO;?v-n=FI@J|8{`PruvFBTNXHnusGBIH5; zdTR&LoC#OGdU4+BZQtTwmb%a-3c~oMF`nR?jVG`7Uh+@vhq)%M{w{Q0-exC!_68+4 zZJyanNCGPJXHA$iySG3>C@Qj*wfh6%`Si?YHq%sbF79P@a31kxX!p`_l3~Bk&#s(6 zI4DC2d%_LYk;@qERTW+3(%eQ_rrt4Wc9|?u!pf&GS0#a+y+bC{@6B`fa~^n0Ki{++ zUTQeNMrZ*%-B4w5P`LT=%z5^!9v(DPq6pzu_vk3Vm}p9M;g8THY08^p04-C`$2WuV zk3G93rda+^g|t506dH%(P1C;-)^3xj@U7}fD`}{_t(lVUy~BOWhabmR7_VBMC1Qvr;5wb1Sp{`vFq%p%Y z?+@&<7yExX+<+kOOyR9asGXT-4<1Ps3qG)3CRw^AJ_(0cmFq^KJ_4v=Q?g1pYUM~C zL37#Nq7guu_h4@sl24KT4>V+!v#2SoRaD^jFR$ERl04TYTR4e}il%SLy_hk*b;*L{>fI56K9bS+2Lf_e4l15 zWY27A6Zx$u`Vh~B`+O9nuHTUHiQKuc|07~x;;`!hTK~ts?psnWU9wLdpd1Z4%27ie z6rYb+zUz8=@3*FkmC%2;ntfK3NY2IZKdhc>A>C`oS8Dt#9!(rUz~xi;d1>HB3mCy7 z^+@LVx>&bU;C=mx!y4cfxriq5Lq;gK}K)D|=HLEY0HN5_$MG zU$m;ykRHX|XIm$m?HoWi0V>DfXfz8SMOK`l(mOU&#|N4fVwBLZjmx!Xa=n==m`4Y6 zpy>Z|?+uTx)G_vWuur$FU~WPOR>9Lc?wjZrGuHSSOb(!#7`ZAImgd$-CE^fFcu$TX>cRl8i_-tr=Uvp`p|oRj+s(KWZ0-f=%Lp@^f&z zopz~oHJqYkzhn6)nZEdMB7>s=^a=bWmN6n_B%H90(W}{89yu)@taGIuLO-q(vs)pVX zubV1g_aG&ohM*q7!OV`K*P?8iW8Co5fYo}$_3p(E%jZF$b$68qYsGKQp2gyUv{o^~ zrVKgwkv8F5jsFE1oU3r^Oy0}-S6us69qTZ~W9+}A!biuznb+RaM_&)#zZAF2!MR}! zo<@=~TCX2*F9*ZHjTscQ0j~F-_k0(b^x7tO*1_2=Hf%&`l$YO~_1?3Q<5(l#^rb2X zJb~AL?7DaJAK#$>G%3U3CPpZ9V3Ww`(;9xFAB5aAe=%|COzYtQcrQphUZsJ3fj-V> z$F{gUO=A%qdRyW+gjCPnhO|l&(HiY5EP;-eM;B8Jqff4u4UR`-9d{%$;*LnHm{YRy zaabh+601RvX{naKdMBfQ-|J;3RYlVP#_OHFES21}PD-vbr!eVddRT;fN503;&NG8f zPp+=<|HR&K5DGP#!gCQfOA9D5e~*&Zx@n#u3TaK(Ci;t0e#gN)j<r= z=X~Iek=d&IGJ=mKbtA<>lXe?yamWE_fF{^VqSN>p^FTuCu47g3vnew2yrO@8I#l57L%^;J+;T4O5MmtHOH#k^%R47^tP7j9>71fs z?h}!gL}BAoF|&cGNV1WMc6IB)x{!kT8Hzp;QAhJq443k7(PKbp?rd{0Fw}x=#8-38(d)@#vV1VhB_J>Omx&_IubC~)QSr7Yk zKVrZ=I`q+3dz0@B%}w40?vkZr6|nvFiH>|8+=sYYCJJIkSmMr)<@KoYu-TV#sA)m5 z7vUZijYbj!%0nAxJzzfE;C)q1cbhZ$8tT+flsI2fz}-kkUp<;;pGShyy8IRbngP+?tYA`z-Ckl+}N2CNdSN7LG>M;C}<(_oHdJKAoKF#8!F9jJGat zIvVMC8_nQv!qB7Sz=P)4gQy!``??7^?S<-1Q}-P(i!@C)C>C0F4t_WQm=hAGldD^a zX}|V9iBys?(53~$-63)2WNPX-3*~G0(_X(Go#z`ZjmYJZC3y2RS#~0K>i->4S+;no z-`!5xPl-k`(m_)Q`|2`v0^6@zXGeH+^m$!CMjO4=XIl&_=4Tk~#7B~puCTRpDh{=+ zy{PaeY&xAHvEUrRjem{u@$o_CUr_lb3MVkZZ^f!`6+>z~)i;{<@uz%~}H*>E;M~Cp&I8>og8fSb>eS|vPG_9|)tSWF>fw*n4 z8NIztTk>MbLcigH)Jmy&f15N=Hide856%l+`==ZZAw z6x)srUkY%`?6Q;9n<;VZBn&2X+WFw1EzQ~aD=Ugu8?7sk;~)p6aB<+v7nC#e%DWSY ztnS`2k>0lBh^(me@>71z!O41}(bKCb<22>@fnRf>zzVojJZ#^wuL=7-B%0y8TXP#f zWLhZ7M5Q?FQc~);s3f735WqcQ@tz2MtK~nG$b%1K)6-}82+~>yTmn8<{m(D#4VT*R z`5y-i7Mg!|_2FN_oIwB<1aP#2vE5zo>>J&q?kDSYU#n1vG9?f*@HYScy@7$G zSQH;M)N@ei025hoCZ@`DI9D4jV8pQX5<^tVUsdp?X%`^R?O6TRpBl@#e8d7mMR1{- z=}No}bcI*9izE_xIN{{#&aL|he=v>MAq9fs%%m5ES zQEr_4HTH|oZ&H`{xumC(;_l!(G~@$$CsyY?o6lAj@r8O-zcPSBHQrGRAH*EN!il9c z@`OIck`hTZjcU*<8vVm+qT*ivRs0NI?6etnr_z+r`NQu;)x%Y7^gjJ0TaF?n4Alz$ zrNM6bFU?ZqvVcT+$!8a*${1=?w#Ww+z!WVRyR(aI!|aou)3#S@qg@_*nKBXL<``lGtUM#_N zEBn(aq43kVoPy@8Dt#-)O529A+=Nr|@Kx?&Yop{$QPDu%!|rpO`9>l=l2LqLRVu0w z!eI_y?y>h3UK$x89;BU;1s-Sl`3$g@Ty`5^>0RT!G{skP7roI6uD=B?mqqx`lp9jr z!{Y1TaZIX|xBtIDsJi1Eg2Nd0ErV+Rxv=~w;GgWjeC>+iej1_;-spY=(VhFHGG1EJ zE28&V*>_1&lQg7^%NVg1@y==H`)iYw4&VyDW1~P-U8g>(LF~qEUjZHDsxy^qa!byr zyZkECKwyD(@kN6aQ2!ikGo`rWjc$X|YP6xFJ)FBU{U#gQocu$Z*Dr+}cVey`fpd*la~=5h)ybiv zr=N!mx3+*nG6(m$(Qmrc6(Up>A%EQnXKZ-Ui5+l?EwMDRw_Om|li?rVRY|G^l$yO~ zwWO{tUD2me_HAoyd}i73$p5n8R2-DiX>ULcn-n2;a>!cIVz8^da-m1`V%`bOR`^$$ z4owrSXWd`~1o>t(kbZG@cgpedjrY9f@~=t30^8W3)j$ECl8K=(p&7mcNHKN1*yGf4 zF*AeAm02r5Egby%i@)@dZ1|#4Qb)rM=M%9>mQ@A52n&E!#xsT094k9Uso1V8R8^1@ z4$XXbiRZH34xOdH?r zX5jBimo_P`-_sgi_Zg(GHhVfJLT=#)n1U(q1q;-M>ol5MS)D(1g8Que(flKX>t*%T zIyxMkA1iwOVGd@26aw+6(vXpsDL@>|4A$hbm9x zq<*OS!Ih}s{7f?ghwcsjKFufV6&dft??%|vfk7%UC)&WtbWy~|b(fx7t)}~&d1C^2 z_8BE~Nrp5g*`)>_Nn{-x_oob*fr|bdYq!qeB_aRDV%Fme_q>>s|Niq|b($m{p2mQ; z4<7C$PgXD?@1^rZGsWW68^GDCL-+{v$D@4 zB_0o}7P$CSf$VB{?XKqcp|F-!TZQ8b#8F)bbn?fCaFCY`h*iJ7@-{Qc$o?`LZQDxC zs^ia@F2eU-*^UN3H#_gx5G&Y|_J99Wr^>J(t!?m$P5#qE!%@4f8Ht@-VCn*yqRsI# z7_8&WO23PVjN9_{(guvb3>>n}IrxKJ3(#e)zDas{@!#|tuFXh;;4-KncsOd47U<-SNB%Q3 za;R^3%U)|2%5VclV;>S0vv@&N$sEY9320qxAw)H`a`O^vj;AidHGz4s$%nc0ug{&kh#r5^A~Y#4lE);qD?E)T z^DP`}H;Lzd_t}1_Ivfc|13gnit<7=TM-0i>kelowK?W3k@9Ck4IzLUohaF6EG4$Cg z?Pbqp$Cb5mjut&KqjyBtjOfBvMy@iD&fmcPtO4(}HBAYvA}RXWKf}SilW!Ccfuyyw zZ%HjSoOrCaEa?C|mQf>3QA}~}EYPA=pZ;8sCj1D%o@sRTZ?Z}7dUN0Fh(1>dC2)7>&m03eG?!|yiir1r1aI9Toe$ib zlG{nNZ6AfwVS1a-Klh+)u1#?kIq7`nmfik2;gh40r?U5dW{@hG5u*VhgO_;&S9&7W zn68Y|;AH#xyu;Bs6sZDVrBlQ1eR}6<-UBAdwKSSe;Y%PzaUeNAHg!9mNqTH+?Gj-f zb;PkY(PD$x*r{jMmUG|tAg(f^5YR#3hO1dD<8n;qwzs{PYziGvh(qDGW(g#}vFY6p zrrAA83%O~=yqe}!u)8CF+GkuOiz*vbHVGJKI9iMVc2?dY?wgiO{lP`PRVTB!3RmP? z!RK_2zPZ)VPJ@Fh)|S7m^G7y8Zl==}A;-8GKnCvPrw6CJ~T1eDT+3=gUC{>76bdRDm>k(;W%$Et|#LaR>6uA_1zHTJoS45I_W`1Tr10AE1`J zr#`xu`8Qq7&=1Nn+1Ps4bf^gs=*b706BM!<2lTh@-=}jFSZ?lSzyJdP$!cAwv$Q~A8<_I6AO;LtVYB7`3zwog&It4-@6ECBp9HvYorlr3!|ETJNsr5w zsCjQ;BH(B@0t!EgZ)Xf31*_Sh?=w8V+XP#i!rLG)7sl8Ym4wJ|rymC%rysO+P2|v# z7IPuD&^4-)4<;YSOFO@{Y7l?(p&0QeAel#0OpGV*3Ed^gr-+`Ft|PNIL*ZyeuPT48 zKT50Xa7aCf;`=uUS#E9wq=FiK3?mOZ2ZM*D-RM*l7&xe;5E9blXF>-haC9W*+1)m= zelbEExC6soBBGoFX~SAJ)krgWzxnm<@0`b;C;PeVlHtJiOkoE+GED9F2Epkj2|XbfdIlP#7oQQ@>; z{$BDk=P5F@Ww}Y%o6Kb|i@7A{?kPs>v2@606aFR(OaP&5aG|FYTnK34lEFUas;;hm z_Ue)Fqj49e-iNDnJCF@0L1L9H(m8X`@kzE7SADT?^Eu#3tNF{^Z&5Zf&#vnABoYz; zX&{gm)EjU>2Okv1;57oSUWwDC-tXREk!oOgV0#b8!hO48&w$gStwx=F@Qyt{0W$_i zmk{s)WQ5vdf-#{gr}Yf8igy1(B?4AXO`*^6vKlfeJC-HjgU*1vz2H_i{TKH z)RrA2TpBnp>O`WEWO4wZgO8+?n3TNGe{Irm<~Fo0dN*x}*3bRwZH6q4wA4zAi}U`- znok1=U|Ch$4Xo|bZXM8*{cHgO zH#v@p(A%;&9Uz+@)e}XnDqqruB(j0$mch%(LS#Nh3dQ;}OF7(+zy zH8O_neOH%z>Xe!H&F?$aN)`-YAg^R(MfOg|H+Y9LT$0&`a7dF+#DQxnMJ8lD35(#j z+G5FASPJ=Yz-NH5mGMOE_^~U?rpWf-q&x!@-Qj9k`I!B31J{(~?;g74^|^xOx~Kb) zQFD{T$jH#PTFw(SQQk~1B-D{T&~eqMUcIb>B~Vo!@m=!btC9fw%Vv_yFiP@{2|y4g zJ0MZ84Wt?z8@P2jk-^w1f0<$@z7;eFKD!~r%jw(v={`Az2Fq7xvs056OHNT>xK|?F z9D?Cthbt*gKE(qH32tEpO@@-nZ4RrQ{p>sypAS4MDuT}c$+-f7RGv;G$`(@z42<#N z{lx)Hgj=0=40ybyPS{S6efId*hS)D-kLlN4gxWeh0y=VzqdM9)$;#SxiJd=8Bv$r^ zUIYR;K&YIL&rFh%<3L$~oX;1=`TT}8n5OeyQqoU$aA8=HnkoVgY6Ao*@<6iQYby%! z!P_dIW^a18?9GuwxXE>N_B+9Pc_UK7^K={*QfLX_t2lwcL*iNDdKsp@FhllL#ePUQ zw(!N^?}~F}Yc0GSHBu2ixBI{-=xz?-xnoqu@t$?p)AbPcu!ENl9o}I<-U^OIOo%0F zIL%rBl(E<0Vqr_;x3K@L^61)naE?jX!lK)hGYofnG09R4#qmK~TWIhHIMtEaxv8}C z=+1*&l}}t(p~@D9b}gqAg~WiI#>slK2(^aQkc*D|3-ox&9jZ@6B_c1)THP}v7265B zvvgdvl;kL2N|m(9HA=y_39_oygBBmr;ro=~w&B>cV*qN~;Lc_HXHPWOOvz@-ff|S& zev^q2cPZqQ40t}rjNjrW9pzxrIrf2V78$`MH`_^3bWUSKBkeOH1}zH<<+T=>k6TQh z9-lPKK`VF=DPtWLDwf<49vbX8HFtAlMN9P^!D!FzzW*M>;9qsF3olGS&8fKxcg}^xHX@I7S*n^KTZ5S`u#l zbpThZI0&dH0V%C37T8D+4``o^$VPsi$mwYQs%!7JGjNl%7!PQcUXuVnW(GWKNI%}+ z2tTuN%&Ha$hW-SetBDfA1dhMugc(SBG!XB-ZKiScAJ{)yKhc!fH2r=<1}&|A`T+*JBLGtiOfVEk@ds0Rc{`9dRnwn_5WZydXn z@n2$KXK?vk;ri9pQuRbI_s(w#f-C~C*;jl^;;PS=&HVbb<>;asaq5IHcygc@LH+Ma zNe|*HtrHXp3R{~-54Jx{uFO=;pk6LO*26+CGs?**-{|cws>$!D~ zuP`6U)h1q|&QHtKGq~A+kT?{J9AR(7KEfGNQdMq7?tSYgBJ>a9BD2hf27e8og;HB? zj|_^Rj#fBWuO4uZp7-ii`Z2~7T;6Zi3#P%CX@>~myJj&?ht({jYM7=ktx<=E&b1zL> zC+s6J4Jt9%;D(8dheLuDHImF*ndv-kC@C%;`)>W^&;*aju|bY>%){R$$DbL~u44@( zP@G`E536Kap!F-*Q04a8S0`57=a~=F4~*(3u4{vz9s)O+kmGWbIDid?#JS;@l^nKp$kY?}qrTWd!pRn{$JHRJ2sugC;*=zug-hpX$i= zxmzPVvv^LGq0e}>_&hS}%jkeoB?@n^{Dk0Gow^S1&U!qPG&_h#{Tg~UG@_(wYH;bY zQr)$QBe%YMn%{1Hgbw?#OF}=bC*Nf9G|(q=a{!NNL8vI>bYI?YpZ&h`=XkI3O(@6} zlel?&10=0|Z{B>ui1b~SH48I0H`7gE+}~alhLlw5%m1w9{j)~6wo9x&Bod`^kQa*) zqhb3JbbRu@{xbEDi@<~T)LMQ)u)b2-j?xzud=MxJ5=csPemgkbMnU+OCq(%7S7;FV z1a5TkEow=bKDk~MESGqQKKkPNK-}FVOi?R%{_=xQFBW22NN_Mn5ZZ}O-$znah9q@0 zJaL9Dqal3d>YgHUdE=IUm%N!zH2G1W$|XX#_@mH_94hky6A~DBPSDg;?saPPhYx?I z>@Dvirf=U8mX?%FDZ3cm5e1zH!k}7%KWRdBbw%!)S?KsTU#_dxG%%2iY@fDWm3+fw znuLX^G_GLW;F?CWn5iIf8S2F3V5wW}p)>ZGedwF!{CQzVb6N zoLe;jMx~dfphyXYN_eTk-Lf!?Sqf?#;dG|VF1J$ONIo^qNUN@n41bP(K09i1&&e|0 zOGJj6s;F8VnAg>Me^+1E3HX?Wxl+u=sp+l@dmu zmnin1Ca*G0-rnUf)7HI{wOwT`KuQ{X&jj9<`%jedmN_P59f_BkxfLG4HzZIWGEWX4 zcR);|Ceihts#DD=hulE$f4?|v9_98LEaOa*uEeI+9psH_X3ZfiniV99*1dO(UzajGfNBES(=|c@1dI~EIr(`PVNo<;b*ZL{kTBoDmaD{wpGY;wO z1QC7u)&F`ddJ6(TZrdD;c&!Hk`%@B(JzI7tDH%lAG%IBPt1qJP+ELPiLFEN6l`OiAuCa9{$$ zQQ@fn!9v7Byun3FTYUG_Z?KmT_{m*Zaq}%nwv!FZdCiadlx@&sMGc&e!9A%{)c-wj z;N6vr^LF|{vYjSz)P9pU98iM`PIUt{>dsenU`ox}f!`qaF^{T6;}Tby|y zK7aKsTfFG>qgnk!D{dlVgwGBq9F4<5tUOhwr(^hkU+W#O=m$x5jz=hpi-^BKboIPk zy7QLA*J&F{rJM18D9rsIiYS`@5e&UH-!o(~MSMe7P+j}3Yc8~WwJ;W{%crj*@Z#G4 z=xdJz{u{Pi|Mzvu-`(uHVyrI9L~*9zPvIAe?=qncIM-W}iA49c|9?!RMou**i`Fh_pN3|DOT- z$~JL+U3v;|b>Of=(&nxmcFJh!nxIojOLc<0%zx-F;Ha#A(;5+X?)lVDQ-bD&o?20H z1ZW#D?E$?6&I^nVGavzaSze@S%B9s)EWKtfobvMHr8h-)`?Q}H?2!pNrPs0Z>l>j2 zVCaJ~5xRbfcMm2=2uMv0T0W&}>6J8*N%OY|C0+E;R)`H$`JH#{@qat!jdx}}`5r8} vG+vY0>t!%wTtf`H?ZCACjQP+1|4cj#&C^ZZe0#)|!T + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public static class MMAnimator + { + // Adds an animator parameter name to a parameter list if that parameter exists. + public static void AddAnimatorParamaterIfExists(Animator animator, string parameterName, AnimatorControllerParameterType type, List parameterList) + { + if (animator.HasParameterOfType(parameterName, type)) + { + parameterList.Add(parameterName); + } + } + + //

+ /// Updates the animator bool. + /// + /// Animator. + /// Parameter name. + /// If set to true value. + public static void UpdateAnimatorBool(Animator animator, string parameterName,bool value, List parameterList) + { + if (parameterList.Contains(parameterName)) + { + animator.SetBool(parameterName,value); + } + } + + public static void UpdateAnimatorTrigger(Animator animator, string parameterName, List parameterList) + { + if (parameterList.Contains(parameterName)) + { + animator.SetTrigger(parameterName); + } + } + + /// + /// Triggers an animator trigger. + /// + /// Animator. + /// Parameter name. + /// If set to true value. + public static void SetAnimatorTrigger(Animator animator, string parameterName, List parameterList) + { + if (parameterList.Contains(parameterName)) + { + animator.SetTrigger(parameterName); + } + } + + /// + /// Updates the animator float. + /// + /// Animator. + /// Parameter name. + /// Value. + public static void UpdateAnimatorFloat(Animator animator, string parameterName,float value, List parameterList) + { + if (parameterList.Contains(parameterName)) + { + animator.SetFloat(parameterName,value); + } + } + + /// + /// Updates the animator integer. + /// + /// Animator. + /// Parameter name. + /// Value. + public static void UpdateAnimatorInteger(Animator animator, string parameterName,int value, List parameterList) + { + if (parameterList.Contains(parameterName)) + { + animator.SetInteger(parameterName,value); + } + } + + + + + // + /// Updates the animator bool without checking the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// If set to true value. + public static void UpdateAnimatorBool(Animator animator, string parameterName,bool value) + { + animator.SetBool(parameterName,value); + } + + /// + /// Updates the animator trigger without checking the parameter's existence + /// + /// Animator. + /// Parameter name. + public static void UpdateAnimatorTrigger(Animator animator, string parameterName) + { + animator.SetTrigger(parameterName); + } + + /// + /// Triggers an animator trigger without checking for the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// If set to true value. + public static void SetAnimatorTrigger(Animator animator, string parameterName) + { + animator.SetTrigger(parameterName); + } + + /// + /// Updates the animator float without checking for the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// Value. + public static void UpdateAnimatorFloat(Animator animator, string parameterName,float value) + { + animator.SetFloat(parameterName,value); + } + + /// + /// Updates the animator integer without checking for the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// Value. + public static void UpdateAnimatorInteger(Animator animator, string parameterName,int value) + { + animator.SetInteger(parameterName,value); + } + + + + + // + /// Updates the animator bool after checking the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// If set to true value. + public static void UpdateAnimatorBoolIfExists(Animator animator, string parameterName,bool value) + { + if (animator.HasParameterOfType(parameterName, AnimatorControllerParameterType.Bool)) + { + animator.SetBool(parameterName,value); + } + } + + public static void UpdateAnimatorTriggerIfExists(Animator animator, string parameterName) + { + if (animator.HasParameterOfType(parameterName, AnimatorControllerParameterType.Trigger)) + { + animator.SetTrigger(parameterName); + } + } + + /// + /// Triggers an animator trigger after checking for the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// If set to true value. + public static void SetAnimatorTriggerIfExists(Animator animator, string parameterName) + { + if (animator.HasParameterOfType(parameterName, AnimatorControllerParameterType.Trigger)) + { + animator.SetTrigger(parameterName); + } + } + + /// + /// Updates the animator float after checking for the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// Value. + public static void UpdateAnimatorFloatIfExists(Animator animator, string parameterName,float value) + { + if (animator.HasParameterOfType(parameterName, AnimatorControllerParameterType.Float)) + { + animator.SetFloat(parameterName,value); + } + } + + /// + /// Updates the animator integer after checking for the parameter's existence. + /// + /// Animator. + /// Parameter name. + /// Value. + public static void UpdateAnimatorIntegerIfExists(Animator animator, string parameterName,int value) + { + if (animator.HasParameterOfType(parameterName, AnimatorControllerParameterType.Int)) + { + animator.SetInteger(parameterName,value); + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMAnimator.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMAnimator.cs.meta new file mode 100644 index 0000000..ff29acc --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMAnimator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 40bf0f52ff177a14cb3250ed29df8285 +timeCreated: 1455815326 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs new file mode 100644 index 0000000..c7eb09f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs @@ -0,0 +1,90 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public class MMBounds : MonoBehaviour + { + public static Bounds GetColliderBounds(GameObject theObject) + { + Bounds returnBounds; + + // if the object has a collider at root level, we base our calculations on that + if (theObject.GetComponent()!=null) + { + returnBounds = theObject.GetComponent().bounds; + return returnBounds; + } + + // if the object has a collider2D at root level, we base our calculations on that + if (theObject.GetComponent()!=null) + { + returnBounds = theObject.GetComponent().bounds; + return returnBounds; + } + + // if the object contains at least one Collider we'll add all its children's Colliders bounds + if (theObject.GetComponentInChildren()!=null) + { + Bounds totalBounds = theObject.GetComponentInChildren().bounds; + Collider[] colliders = theObject.GetComponentsInChildren(); + foreach (Collider col in colliders) + { + totalBounds.Encapsulate(col.bounds); + } + returnBounds = totalBounds; + return returnBounds; + } + + // if the object contains at least one Collider2D we'll add all its children's Collider2Ds bounds + if (theObject.GetComponentInChildren()!=null) + { + Bounds totalBounds = theObject.GetComponentInChildren().bounds; + Collider2D[] colliders = theObject.GetComponentsInChildren(); + foreach (Collider2D col in colliders) + { + totalBounds.Encapsulate(col.bounds); + } + returnBounds = totalBounds; + return returnBounds; + } + + returnBounds = new Bounds(Vector3.zero, Vector3.zero); + return returnBounds; + } + + public static Bounds GetRendererBounds(GameObject theObject) + { + Bounds returnBounds; + + // if the object has a renderer at root level, we base our calculations on that + if (theObject.GetComponent()!=null) + { + returnBounds = theObject.GetComponent().bounds; + return returnBounds; + } + + // if the object contains at least one renderer we'll add all its children's renderer bounds + if (theObject.GetComponentInChildren()!=null) + { + Bounds totalBounds = theObject.GetComponentInChildren().bounds; + Renderer[] renderers = theObject.GetComponentsInChildren(); + foreach (Renderer renderer in renderers) + { + totalBounds.Encapsulate(renderer.bounds); + } + returnBounds = totalBounds; + return returnBounds; + } + + returnBounds = new Bounds(Vector3.zero, Vector3.zero); + return returnBounds; + } + + + } +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs.meta new file mode 100644 index 0000000..ebbfb7e --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMBounds.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b6df521b0f2978740a4c18dddde873a8 +timeCreated: 1455815409 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs new file mode 100644 index 0000000..627d021 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs @@ -0,0 +1,98 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// This class displays an on-screen console for easier debugging + /// DO NOT ADD THIS CLASS AS A COMPONENT. + /// Instead, use the MMDebug.DebugOnScreen methods that will take care of everything + /// + public class MMConsole : MonoBehaviour + { + protected string _messageStack; + + protected int _numberOfMessages=0; + protected bool _messageStackHasBeenDisplayed=false; + protected int _largestMessageLength=0; + + protected int _marginTop = 10; + protected int _marginLeft = 10; + protected int _padding = 10; + + protected int _fontSize = 10; + protected int _characterHeight = 16; + protected int _characterWidth = 6; + + /// + /// Draws a box containing the current stack of messages on top of the screen. + /// + protected virtual void OnGUI() + { + // we define the style to use and the font size + GUIStyle style = GUI.skin.GetStyle ("label"); + style.fontSize = _fontSize; + + // we determine our box dimension based on the number of lines and the length of the longest line + int boxHeight = _numberOfMessages*_characterHeight; + int boxWidth = _largestMessageLength * _characterWidth; + + // we draw a box and the message on top of it + GUI.Box (new Rect (_marginLeft,_marginTop,boxWidth+_padding*2,boxHeight+_padding*2), ""); + GUI.Label(new Rect(_marginLeft+_padding, _marginTop+_padding, boxWidth, boxHeight), _messageStack); + + // we set our flag to true, which will trigger the reset of the stack next time it's accessed + _messageStackHasBeenDisplayed=true; + } + + /// + /// Sets the size of the font, and automatically deduces the character's height and width. + /// + /// Font size. + public virtual void SetFontSize(int fontSize) + { + Mathf.Clamp(fontSize,10,100); + + _fontSize = fontSize; + _characterHeight = (int)Mathf.Round(1.6f * fontSize + 0.49f); + _characterWidth = (int)Mathf.Round(0.6f * fontSize + 0.49f); + + } + + /// + /// Replaces the content of the current message stack with the specified string + /// + /// New message. + public virtual void SetMessage(string newMessage) + { + _messageStack=newMessage; + _numberOfMessages=1; + } + + /// + /// Adds the specified message to the message stack. + /// + /// New message. + public virtual void AddMessage(string newMessage) + { + // if the message stack has been displayed, we empty it and reset our counters + if (_messageStackHasBeenDisplayed) + { + _messageStack=""; + _messageStackHasBeenDisplayed=false; + _numberOfMessages=0; + _largestMessageLength=0; + } + + // we add the specified message to the stack + _messageStack+=newMessage+"\n"; + // if this new message is longer than our previous longer message, we store it (this will expand the box's width + if (newMessage.Length > _largestMessageLength) + { + _largestMessageLength = newMessage.Length; + } + // we increment our counter + _numberOfMessages++; + } + } +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs.meta new file mode 100644 index 0000000..9057758 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMConsole.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 878e38786f84b4cb2befd22693a97a33 +timeCreated: 1455961502 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs new file mode 100644 index 0000000..41f0e44 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs @@ -0,0 +1,258 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public static class MMDebug + { + public static MMConsole _console; + + /// + /// Draws a debug ray in 2D and does the actual raycast + /// + /// The raycast hit. + /// Ray origin point. + /// Ray direction. + /// Ray distance. + /// Mask. + /// If set to true debug. + /// Color. + public static RaycastHit2D RayCast(Vector2 rayOriginPoint, Vector2 rayDirection, float rayDistance, LayerMask mask, Color color,bool drawGizmo=false) + { + if (drawGizmo) + { + Debug.DrawRay (rayOriginPoint, rayDirection * rayDistance, color); + } + return Physics2D.Raycast(rayOriginPoint,rayDirection,rayDistance,mask); + } + + + public static RaycastHit2D MonoRayCastNonAlloc(RaycastHit2D[] array, Vector2 rayOriginPoint, Vector2 rayDirection, float rayDistance, LayerMask mask, Color color,bool drawGizmo=false) + { + if (drawGizmo) + { + Debug.DrawRay (rayOriginPoint, rayDirection * rayDistance, color); + } + if (Physics2D.RaycastNonAlloc(rayOriginPoint, rayDirection, array, rayDistance, mask) > 0) + { + return array[0]; + } + return new RaycastHit2D(); + } + + /// + /// Draws a debug ray in 3D and does the actual raycast + /// + /// The raycast hit. + /// Ray origin point. + /// Ray direction. + /// Ray distance. + /// Mask. + /// If set to true debug. + /// Color. + /// If set to true draw gizmo. + public static RaycastHit Raycast3D(Vector3 rayOriginPoint, Vector2 rayDirection, float rayDistance, LayerMask mask, Color color,bool drawGizmo=false) + { + if (drawGizmo) + { + Debug.DrawRay (rayOriginPoint, rayDirection * rayDistance, color); + } + RaycastHit hit; + Physics.Raycast(rayOriginPoint,rayDirection,out hit,rayDistance,mask); + return hit; + } + + /// + /// Outputs the message object to the console, prefixed with the current timestamp + /// + /// Message. + public static void DebugLogTime(object message, string color="") + { + string colorPrefix = ""; + string colorSuffix = ""; + if (color != "") + { + colorPrefix = ""; + colorSuffix = ""; + } + Debug.Log (colorPrefix + Time.time + " " + message + colorSuffix); + + } + + /// + /// Instantiates a MMConsole if there isn't one already, and adds the message in parameter to it. + /// + /// Message. + public static void DebugOnScreen(string message) + { + InstantiateOnScreenConsole(); + _console.AddMessage(message); + } + + /// + /// Instantiates a MMConsole if there isn't one already, and displays the label in bold and its value next to it. + /// + /// Label. + /// Value. + /// The optional font size. + public static void DebugOnScreen(string label, object value, int fontSize=10) + { + InstantiateOnScreenConsole(fontSize); + _console.AddMessage(""+label+" : "+value); + } + + /// + /// Instantiates the on screen console if there isn't one already + /// + public static void InstantiateOnScreenConsole(int fontSize=10) + { + if (_console == null) + { + // we instantiate the console + GameObject newGameObject = new GameObject(); + newGameObject.name="MMConsole"; + _console = newGameObject.AddComponent(); + _console.SetFontSize(fontSize); + } + } + + /// + /// Draws a gizmo arrow going from the origin position and along the direction Vector3 + /// + /// Origin. + /// Direction. + /// Color. + public static void GizmosDrawArrow(Vector3 origin, Vector3 direction, Color color) + { + float arrowHeadLength = 3.00f; + float arrowHeadAngle = 25.0f; + + Gizmos.color = color; + Gizmos.DrawRay(origin, direction); + + DrawArrowEnd(true,origin,direction,color,arrowHeadLength,arrowHeadAngle); + } + + /// + /// Draws a debug arrow going from the origin position and along the direction Vector3 + /// + /// Origin. + /// Direction. + /// Color. + public static void DebugDrawArrow(Vector3 origin, Vector3 direction, Color color) + { + float arrowHeadLength = 0.20f; + float arrowHeadAngle = 35.0f; + + Debug.DrawRay(origin, direction, color); + + DrawArrowEnd(false,origin,direction,color,arrowHeadLength,arrowHeadAngle); + } + + + private static void DrawArrowEnd (bool drawGizmos, Vector3 arrowEndPosition, Vector3 direction, Color color, float arrowHeadLength = 0.25f, float arrowHeadAngle = 40.0f) + { + if (direction == Vector3.zero) + { + return; + } + Vector3 right = Quaternion.LookRotation (direction) * Quaternion.Euler (arrowHeadAngle, 0, 0) * Vector3.back; + Vector3 left = Quaternion.LookRotation (direction) * Quaternion.Euler (-arrowHeadAngle, 0, 0) * Vector3.back; + Vector3 up = Quaternion.LookRotation (direction) * Quaternion.Euler (0, arrowHeadAngle, 0) * Vector3.back; + Vector3 down = Quaternion.LookRotation (direction) * Quaternion.Euler (0, -arrowHeadAngle, 0) * Vector3.back; + if (drawGizmos) + { + Gizmos.color = color; + Gizmos.DrawRay (arrowEndPosition + direction, right * arrowHeadLength); + Gizmos.DrawRay (arrowEndPosition + direction, left * arrowHeadLength); + Gizmos.DrawRay (arrowEndPosition + direction, up * arrowHeadLength); + Gizmos.DrawRay (arrowEndPosition + direction, down * arrowHeadLength); + } + else + { + Debug.DrawRay (arrowEndPosition + direction, right * arrowHeadLength, color); + Debug.DrawRay (arrowEndPosition + direction, left * arrowHeadLength, color); + Debug.DrawRay (arrowEndPosition + direction, up * arrowHeadLength, color); + Debug.DrawRay (arrowEndPosition + direction, down * arrowHeadLength, color); + } + } + + /// + /// Draws handles to materialize the bounds of an object on screen. + /// + /// Bounds. + /// Color. + public static void DrawHandlesBounds(Bounds bounds, Color color) + { + #if UNITY_EDITOR + Vector3 boundsCenter = bounds.center; + Vector3 boundsExtents = bounds.extents; + + Vector3 v3FrontTopLeft = new Vector3(boundsCenter.x - boundsExtents.x, boundsCenter.y + boundsExtents.y, boundsCenter.z - boundsExtents.z); // Front top left corner + Vector3 v3FrontTopRight = new Vector3(boundsCenter.x + boundsExtents.x, boundsCenter.y + boundsExtents.y, boundsCenter.z - boundsExtents.z); // Front top right corner + Vector3 v3FrontBottomLeft = new Vector3(boundsCenter.x - boundsExtents.x, boundsCenter.y - boundsExtents.y, boundsCenter.z - boundsExtents.z); // Front bottom left corner + Vector3 v3FrontBottomRight = new Vector3(boundsCenter.x + boundsExtents.x, boundsCenter.y - boundsExtents.y, boundsCenter.z - boundsExtents.z); // Front bottom right corner + Vector3 v3BackTopLeft = new Vector3(boundsCenter.x - boundsExtents.x, boundsCenter.y + boundsExtents.y, boundsCenter.z + boundsExtents.z); // Back top left corner + Vector3 v3BackTopRight = new Vector3(boundsCenter.x + boundsExtents.x, boundsCenter.y + boundsExtents.y, boundsCenter.z + boundsExtents.z); // Back top right corner + Vector3 v3BackBottomLeft = new Vector3(boundsCenter.x - boundsExtents.x, boundsCenter.y - boundsExtents.y, boundsCenter.z + boundsExtents.z); // Back bottom left corner + Vector3 v3BackBottomRight = new Vector3(boundsCenter.x + boundsExtents.x, boundsCenter.y - boundsExtents.y, boundsCenter.z + boundsExtents.z); // Back bottom right corner + + + Handles.color = color; + + Handles.DrawLine (v3FrontTopLeft, v3FrontTopRight); + Handles.DrawLine (v3FrontTopRight, v3FrontBottomRight); + Handles.DrawLine (v3FrontBottomRight, v3FrontBottomLeft); + Handles.DrawLine (v3FrontBottomLeft, v3FrontTopLeft); + + Handles.DrawLine (v3BackTopLeft, v3BackTopRight); + Handles.DrawLine (v3BackTopRight, v3BackBottomRight); + Handles.DrawLine (v3BackBottomRight, v3BackBottomLeft); + Handles.DrawLine (v3BackBottomLeft, v3BackTopLeft); + + Handles.DrawLine (v3FrontTopLeft, v3BackTopLeft); + Handles.DrawLine (v3FrontTopRight, v3BackTopRight); + Handles.DrawLine (v3FrontBottomRight, v3BackBottomRight); + Handles.DrawLine (v3FrontBottomLeft, v3BackBottomLeft); + #endif + } + + /// + /// Draws a gizmo sphere of the specified size and color at a position + /// + /// Position. + /// Size. + /// Color. + public static void DrawGizmoPoint(Vector3 position, float size, Color color) + { + Gizmos.color = color; + Gizmos.DrawWireSphere(position,size); + } + + public static void DrawGizmoRectangle(Vector2 center, Vector2 size, Color color) + { + Gizmos.color = color; + + Vector3 v3TopLeft = new Vector3(center.x - size.x/2, center.y + size.y/2, 0); + Vector3 v3TopRight = new Vector3(center.x + size.x/2, center.y + size.y/2, 0);; + Vector3 v3BottomRight = new Vector3(center.x + size.x/2, center.y - size.y/2, 0);; + Vector3 v3BottomLeft = new Vector3(center.x - size.x/2, center.y - size.y/2, 0);; + + Gizmos.DrawLine(v3TopLeft,v3TopRight); + Gizmos.DrawLine(v3TopRight,v3BottomRight); + Gizmos.DrawLine(v3BottomRight,v3BottomLeft); + Gizmos.DrawLine(v3BottomLeft,v3TopLeft); + } + + + } +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs.meta new file mode 100644 index 0000000..c4b49ab --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMDebug.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d584bb96f63ba3848ab128ed5686f2f7 +timeCreated: 1455815182 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs new file mode 100644 index 0000000..56201b5 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs @@ -0,0 +1,118 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public static class MMFade + { + + /// + /// Fades the specified image to the target opacity and duration. + /// + /// Target. + /// Opacity. + /// Duration. + public static IEnumerator FadeImage(Image target, float duration, Color color) + { + if (target==null) + yield break; + + float alpha = target.color.a; + + for (float t = 0.0f; t < 1.0f; t += Time.deltaTime / duration) + { + if (target==null) + yield break; + Color newColor = new Color(color.r, color.g, color.b, Mathf.SmoothStep(alpha,color.a,t)); + target.color=newColor; + yield return null; + } + target.color=color; + + } + /// + /// Fades the specified image to the target opacity and duration. + /// + /// Target. + /// Opacity. + /// Duration. + public static IEnumerator FadeText(Text target, float duration, Color color) + { + if (target==null) + yield break; + + float alpha = target.color.a; + + for (float t = 0.0f; t < 1.0f; t += Time.deltaTime / duration) + { + if (target==null) + yield break; + Color newColor = new Color(color.r, color.g, color.b, Mathf.SmoothStep(alpha,color.a,t)); + target.color=newColor; + yield return null; + } + target.color=color; + } + /// + /// Fades the specified image to the target opacity and duration. + /// + /// Target. + /// Opacity. + /// Duration. + public static IEnumerator FadeSprite(SpriteRenderer target, float duration, Color color) + { + if (target==null) + yield break; + + float alpha = target.material.color.a; + + float t=0f; + while (t<1.0f) + { + if (target==null) + yield break; + + Color newColor = new Color(color.r, color.g, color.b, Mathf.SmoothStep(alpha,color.a,t)); + target.material.color=newColor; + + t += Time.deltaTime / duration; + + yield return null; + + } + Color finalColor = new Color(color.r, color.g, color.b, Mathf.SmoothStep(alpha,color.a,t)); + target.material.color=finalColor; + } + + public static IEnumerator FadeCanvasGroup(CanvasGroup target, float duration, float targetAlpha) + { + if (target==null) + yield break; + + float currentAlpha = target.alpha; + + float t=0f; + while (t<1.0f) + { + if (target==null) + yield break; + + float newAlpha =Mathf.SmoothStep(currentAlpha,targetAlpha,t); + target.alpha=newAlpha; + + t += Time.deltaTime / duration; + + yield return null; + + } + target.alpha=targetAlpha; + } + + } +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs.meta new file mode 100644 index 0000000..92af09c --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMFade.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c55f5c5831b7b344bb8ae4b4471d7253 +timeCreated: 1455815269 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs new file mode 100644 index 0000000..36b9220 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + public static class MMGUI + { + + public static void SetSize(RectTransform rectTransform, Vector2 newSize) + { + Vector2 currSize = rectTransform.rect.size; + Vector2 sizeDiff = newSize - currSize; + rectTransform.offsetMin = rectTransform.offsetMin - new Vector2(sizeDiff.x * rectTransform.pivot.x, sizeDiff.y * rectTransform.pivot.y); + rectTransform.offsetMax = rectTransform.offsetMax + new Vector2(sizeDiff.x * (1.0f - rectTransform.pivot.x), sizeDiff.y * (1.0f - rectTransform.pivot.y)); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs.meta new file mode 100644 index 0000000..f3d03c8 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMGUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 45ff1ad6fa9e7af4ead4e1db1ed52877 +timeCreated: 1460813345 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs new file mode 100644 index 0000000..cd7450f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs @@ -0,0 +1,35 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public static class MMHelpers + { + public static T CopyComponent(T original, GameObject destination) where T : Component + { + System.Type type = original.GetType(); + var dst = destination.GetComponent(type) as T; + if (!dst) dst = destination.AddComponent(type) as T; + var fields = type.GetFields(); + foreach (var field in fields) + { + if (field.IsStatic) continue; + field.SetValue(dst, field.GetValue(original)); + } + var props = type.GetProperties(); + foreach (var prop in props) + { + if (!prop.CanWrite || !prop.CanWrite || prop.Name == "name") continue; + prop.SetValue(dst, prop.GetValue(original, null), null); + } + return dst as T; + } + + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs.meta new file mode 100644 index 0000000..48bf1ee --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMHelpers.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 61848ed5026b644829e2fcb4a2207848 +timeCreated: 1456159535 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMImage.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMImage.cs new file mode 100644 index 0000000..d9eefaf --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMImage.cs @@ -0,0 +1,46 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public class MMImage : MonoBehaviour + { + /// + /// Coroutine used to make the character's sprite flicker (when hurt for example). + /// + public static IEnumerator Flicker(Renderer renderer, Color initialColor, Color flickerColor, float flickerSpeed, float flickerDuration) + { + if (renderer==null) + { + yield break; + } + + if (!renderer.material.HasProperty("_Color")) + { + yield break; + } + + if (initialColor == flickerColor) + { + yield break; + } + + float flickerStop = Time.time + flickerDuration; + + while (Time.time + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public class MMInput : MonoBehaviour + { + /// + /// All possible states for a button. Can be used in a state machine. + /// + public enum ButtonStates { Off, ButtonDown, ButtonPressed, ButtonUp } + + /// + /// Takes an axis and returns a ButtonState depending on whether the axis is pressed or not (useful for xbox triggers for example), and when you need to use an axis/trigger as a binary thing + /// + /// The axis as button. + /// Axis name. + /// Threshold value below which the button is off or released. + /// Current state of the axis. + public static ButtonStates ProcessAxisAsButton (string axisName, float threshold, ButtonStates currentState) + { + float axisValue = Input.GetAxis (axisName); + ButtonStates returnState; + + // + if (axisValue < threshold) + { + if (currentState == ButtonStates.ButtonPressed) + { + returnState = ButtonStates.ButtonUp; + } + else + { + returnState = ButtonStates.Off; + } + } + else + { + if (currentState == ButtonStates.Off) + { + returnState = ButtonStates.ButtonDown; + } + else + { + returnState = ButtonStates.ButtonPressed; + } + } + return returnState; + } + + /// + /// IM button, short for + /// + public class IMButton + { + public MMStateMachine State {get;protected set;} + public string ButtonID; + + public delegate void ButtonDownMethodDelegate(); + public delegate void ButtonPressedMethodDelegate(); + public delegate void ButtonUpMethodDelegate(); + + public ButtonDownMethodDelegate ButtonDownMethod; + public ButtonPressedMethodDelegate ButtonPressedMethod; + public ButtonUpMethodDelegate ButtonUpMethod; + + public IMButton(string playerID, string buttonID, ButtonDownMethodDelegate btnDown, ButtonPressedMethodDelegate btnPressed, ButtonUpMethodDelegate btnUp) + { + ButtonID = playerID + "_" + buttonID; + ButtonDownMethod = btnDown; + ButtonUpMethod = btnUp; + ButtonPressedMethod = btnPressed; + State = new MMStateMachine (null, false); + State.ChangeState (MMInput.ButtonStates.Off); + } + + public virtual void TriggerButtonDown() + { + ButtonDownMethod (); + } + + public virtual void TriggerButtonPressed() + { + ButtonPressedMethod (); + } + + public virtual void TriggerButtonUp() + { + ButtonUpMethod (); + } + } + } + + +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMInput.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMInput.cs.meta new file mode 100644 index 0000000..f027696 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMInput.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a0874b2291d387847be139a6824cc576 +timeCreated: 1477174630 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs new file mode 100644 index 0000000..3ce8bdb --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs @@ -0,0 +1,21 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + public class MMLayers + { + public static bool LayerInLayerMask(int layer, LayerMask layerMask) + { + if(((1 << layer) & layerMask) != 0) + { + return true; + } + else + { + return false; + } + } + + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs.meta new file mode 100644 index 0000000..c45b704 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMLayers.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 01d38a71c34274ee0b94de34ac7931c8 +timeCreated: 1470777155 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs new file mode 100644 index 0000000..0ae88d6 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs @@ -0,0 +1,20 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System; +using System.Linq; + +public static class MMLists +{ + public static void Shuffle(this IList ts) + { + var count = ts.Count; + var last = count - 1; + for (var i = 0; i < last; ++i) { + var r = UnityEngine.Random.Range(i, count); + var tmp = ts[i]; + ts[i] = ts[r]; + ts[r] = tmp; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs.meta new file mode 100644 index 0000000..8bc40e2 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMLists.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5eef34927cad5114da1241bab0d5e131 +timeCreated: 1467223193 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs new file mode 100644 index 0000000..9005492 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs @@ -0,0 +1,234 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public static class MMMaths + { + /// + /// Takes a Vector3 and turns it into a Vector2 + /// + /// The vector2. + /// The Vector3 to turn into a Vector2. + public static Vector2 Vector3ToVector2 (Vector3 target) + { + return new Vector2(target.x, target.y); + } + + /// + /// Takes a Vector2 and turns it into a Vector3 with a null z value + /// + /// The vector3. + /// The Vector2 to turn into a Vector3. + public static Vector3 Vector2ToVector3 (Vector2 target) + { + return new Vector3(target.x, target.y, 0); + } + + /// + /// Takes a Vector2 and turns it into a Vector3 with the specified z value + /// + /// The vector3. + /// The Vector2 to turn into a Vector3. + /// New Z value. + public static Vector3 Vector2ToVector3 (Vector2 target, float newZValue) + { + return new Vector3(target.x, target.y, newZValue); + } + + /// + /// Rounds all components of a Vector3. + /// + /// The vector3. + /// Vector. + public static Vector3 RoundVector3 (Vector3 vector) + { + return new Vector3 (Mathf.Round (vector.x), Mathf.Round (vector.y), Mathf.Round (vector.z)); + } + + /// + /// Returns a random vector3 from 2 defined vector3. + /// + /// The vector3. + /// Minimum. + /// Maximum. + public static Vector3 RandomVector3(Vector3 minimum, Vector3 maximum) + { + return new Vector3(UnityEngine.Random.Range(minimum.x, maximum.x), + UnityEngine.Random.Range(minimum.y, maximum.y), + UnityEngine.Random.Range(minimum.z, maximum.z)); + } + + /// + /// Rotates a point around the given pivot. + /// + /// The new point position. + /// The point to rotate. + /// The pivot's position. + /// The angle we want to rotate our point. + public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, float angle) + { + angle = angle*(Mathf.PI/180f); + var rotatedX = Mathf.Cos(angle) * (point.x - pivot.x) - Mathf.Sin(angle) * (point.y-pivot.y) + pivot.x; + var rotatedY = Mathf.Sin(angle) * (point.x - pivot.x) + Mathf.Cos(angle) * (point.y - pivot.y) + pivot.y; + return new Vector3(rotatedX,rotatedY,0); + } + + /// + /// Rotates a point around the given pivot. + /// + /// The new point position. + /// The point to rotate. + /// The pivot's position. + /// The angle as a Vector3. + public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angle) + { + // we get point direction from the point to the pivot + Vector3 direction = point - pivot; + // we rotate the direction + direction = Quaternion.Euler(angle) * direction; + // we determine the rotated point's position + point = direction + pivot; + return point; + } + + /// + /// Rotates a point around the given pivot. + /// + /// The new point position. + /// The point to rotate. + /// The pivot's position. + /// The angle as a Vector3. + public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion quaternion) + { + // we get point direction from the point to the pivot + Vector3 direction = point - pivot; + // we rotate the direction + direction = quaternion * direction; + // we determine the rotated point's position + point = direction + pivot; + return point; + } + + /// + /// Computes and returns the angle between two vectors, on a 360° scale + /// + /// The . + /// Vector a. + /// Vector b. + public static float AngleBetween(Vector2 vectorA, Vector2 vectorB) + { + float angle = Vector2.Angle(vectorA, vectorB); + Vector3 cross = Vector3.Cross(vectorA, vectorB); + + if (cross.z > 0) + { + angle = 360 - angle; + } + + return angle; + } + + /// + /// Returns the sum of all the int passed in parameters + /// + /// Things to add. + public static int Sum(params int[] thingsToAdd) + { + int result=0; + for (int i = 0; i < thingsToAdd.Length; i++) + { + result += thingsToAdd[i]; + } + return result; + } + + /// + /// Returns the result of rolling a dice of the specified number of sides + /// + /// The result of the dice roll. + /// Number of sides of the dice. + public static int RollADice(int numberOfSides) + { + return (UnityEngine.Random.Range(1,numberOfSides)); + } + + /// + /// Returns a random success based on X% of chance. + /// Example : I have 20% of chance to do X, Chance(20) > true, yay! + /// + /// Percent of chance. + public static bool Chance(int percent) + { + return (UnityEngine.Random.Range(0,100) <= percent); + } + + /// + /// Moves from "from" to "to" by the specified amount and returns the corresponding value + /// + /// From. + /// To. + /// Amount. + public static float Approach(float from, float to, float amount) + { + if (from < to) + { + from += amount; + if (from > to) + { + return to; + } + } + else + { + from -= amount; + if (from < to) + { + return to; + } + } + return from; + } + + + /// + /// Remaps a value x in interval [A,B], to the proportional value in interval [C,D] + /// + /// The value to remap. + /// the minimum bound of interval [A,B] that contains the x value + /// the maximum bound of interval [A,B] that contains the x value + /// the minimum bound of target interval [C,D] + /// the maximum bound of target interval [C,D] + public static float Remap(float x, float A, float B, float C, float D) + { + float remappedValue = C + (x-A)/(B-A) * (D - C); + return remappedValue; + } + + public static float RoundToClosest(float value, float[] possibleValues) + { + if (possibleValues.Length == 0) + { + return 0f; + } + + float closestValue = possibleValues[0]; + + foreach (float possibleValue in possibleValues) + { + if (Mathf.Abs(closestValue - value) > Mathf.Abs(possibleValue - value)) + { + closestValue = possibleValue; + } + } + return closestValue; + + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs.meta new file mode 100644 index 0000000..6067f52 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMMaths.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fc798aa25d3634ebbb996c8d60ace648 +timeCreated: 1456158841 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs new file mode 100644 index 0000000..11276e3 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs @@ -0,0 +1,39 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public static class MMMovement + { + + /// + /// Moves an object from point A to point B in a given time + /// + /// Moving object. + /// Point a. + /// Point b. + /// Time. + public static IEnumerator MoveFromTo(GameObject movingObject,Vector3 pointA, Vector3 pointB, float time, float approximationDistance) + { + float t = 0f; + + float distance = Vector3.Distance(movingObject.transform.position, pointB); + + while (distance >= approximationDistance) + { + distance = Vector3.Distance(movingObject.transform.position, pointB); + t += Time.deltaTime / time; + movingObject.transform.position = Vector3.Lerp(pointA, pointB, t); + yield return 0; + } + yield break; + } + + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs.meta new file mode 100644 index 0000000..f253fb4 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMMovement.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 195cd029f2c828848a5f260fc5457f3e +timeCreated: 1456589453 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs b/Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs new file mode 100644 index 0000000..8ed95ae --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs @@ -0,0 +1,49 @@ +using UnityEngine; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Various static methods used throughout the Infinite Runner Engine and the Corgi Engine. + /// + + public class MMTime : MonoBehaviour + { + /// + /// Takes a hh:mm:ss:SSS string and turns it into a float value expressed in seconds + /// + /// a number of seconds. + /// Time in string notation to decode. + public static float TimeStringToFloat(string timeInStringNotation) + { + if (timeInStringNotation.Length!=12) + { + throw new Exception("The time in the TimeStringToFloat method must be specified using a hh:mm:ss:SSS syntax"); + } + + string[] timeStringArray = timeInStringNotation.Split(new string[] {":"},StringSplitOptions.None); + + float startTime=0f; + float result; + if (float.TryParse(timeStringArray[0], out result)) + { + startTime+=result*3600f; + } + if (float.TryParse(timeStringArray[1], out result)) + { + startTime+=result*60f; + } + if (float.TryParse(timeStringArray[2], out result)) + { + startTime+=result; + } + if (float.TryParse(timeStringArray[3], out result)) + { + startTime+=result/1000f; + } + + return startTime; + } + } +} diff --git a/Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs.meta b/Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs.meta new file mode 100644 index 0000000..f85e494 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/MMHelpers/MMTime.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 49af33803f6148b4dad64c35bf986275 +timeCreated: 1455815764 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectBounds.meta b/Assets/CorgiEngine/MMTools/ObjectBounds.meta new file mode 100644 index 0000000..a89b60e --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectBounds.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3a2b2f35e2549ae49ac776b042b0e8a0 +folderAsset: yes +timeCreated: 1462451216 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectBounds/Editor.meta b/Assets/CorgiEngine/MMTools/ObjectBounds/Editor.meta new file mode 100644 index 0000000..fa7ace7 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectBounds/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d1b1ace44427bac4f822123983d35d44 +folderAsset: yes +timeCreated: 1462451237 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs b/Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs new file mode 100644 index 0000000..9473b9d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs @@ -0,0 +1,36 @@ +using UnityEngine; +using System.Collections; +using UnityEditor; +using MoreMountains.Tools; + +namespace MoreMountains.Tools +{ + [CustomEditor(typeof(ObjectBounds),true)] + public class ObjectBoundsEditor : Editor + { + protected ObjectBounds _objectBounds; + + public override void OnInspectorGUI() + { + _objectBounds = (ObjectBounds)target; + + DrawDefaultInspector(); + + if (_objectBounds.GetComponent()==null && _objectBounds.BoundsBasedOn==ObjectBounds.WaysToDetermineBounds.Renderer) + { + EditorGUILayout.HelpBox("You've defined this object as having Renderer defined bounds, but no renderer is attached to the object. Add a Renderer, or switch to collider based bounds. The bounds are the dimensions that will be used when spawning your object and to determine when it should be recycled.",MessageType.Warning); + } + + if (_objectBounds.GetComponent()==null && _objectBounds.BoundsBasedOn==ObjectBounds.WaysToDetermineBounds.Collider) + { + EditorGUILayout.HelpBox("You've defined this object as having Collider defined bounds, but no Collider is attached to the object. Add a Collider, or switch to renderer based bounds. The bounds are the dimensions that will be used when spawning your object and to determine when it should be recycled.",MessageType.Warning); + } + + if (_objectBounds.GetComponent()==null && _objectBounds.BoundsBasedOn==ObjectBounds.WaysToDetermineBounds.Collider2D) + { + EditorGUILayout.HelpBox("You've defined this object as having Collider2D defined bounds, but no Collider2D is attached to the object. Add a Collider2D, or switch to renderer based bounds. The bounds are the dimensions that will be used when spawning your object and to determine when it should be recycled.",MessageType.Warning); + } + + } + } +} diff --git a/Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs.meta b/Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs.meta new file mode 100644 index 0000000..d96ffec --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectBounds/Editor/ObjectBoundsEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bada8c222727b4fe49b6a90f1dddad8e +timeCreated: 1456407699 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs b/Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs new file mode 100644 index 0000000..c20c3e4 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs @@ -0,0 +1,84 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using System; + +namespace MoreMountains.Tools +{ + public class ObjectBounds : MonoBehaviour + { + public enum WaysToDetermineBounds { Collider, Collider2D, Renderer, Undefined } + public WaysToDetermineBounds BoundsBasedOn; + + + public Vector3 Size { get; set; } + + /// + /// When this component is added we define its bounds. + /// + protected virtual void Reset() + { + DefineBoundsChoice(); + } + + /// + /// Tries to determine automatically what the bounds should be based on. + /// In this order, it'll keep the last found of these : Collider2D, Collider or Renderer. + /// If none of these is found, it'll be set as Undefined. + /// + protected virtual void DefineBoundsChoice() + { + BoundsBasedOn = WaysToDetermineBounds.Undefined; + if (GetComponent()!=null) + { + BoundsBasedOn = WaysToDetermineBounds.Renderer; + } + if (GetComponent()!=null) + { + BoundsBasedOn = WaysToDetermineBounds.Collider; + } + if (GetComponent()!=null) + { + BoundsBasedOn = WaysToDetermineBounds.Collider2D; + } + } + + /// + /// Returns the bounds of the object, based on what has been defined + /// + public virtual Bounds GetBounds() + { + if (BoundsBasedOn==WaysToDetermineBounds.Renderer) + { + if (GetComponent()==null) + { + throw new Exception("The PoolableObject "+gameObject.name+" is set as having Renderer based bounds but no Renderer component can be found."); + } + return GetComponent().bounds; + } + + if (BoundsBasedOn==WaysToDetermineBounds.Collider) + { + if (GetComponent()==null) + { + throw new Exception("The PoolableObject "+gameObject.name+" is set as having Collider based bounds but no Collider component can be found."); + } + return GetComponent().bounds; + } + + if (BoundsBasedOn==WaysToDetermineBounds.Collider2D) + { + if (GetComponent()==null) + { + throw new Exception("The PoolableObject "+gameObject.name+" is set as having Collider2D based bounds but no Collider2D component can be found."); + } + return GetComponent().bounds; + } + + return new Bounds(Vector3.zero,Vector3.zero); + } + + + + } +} diff --git a/Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs.meta b/Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs.meta new file mode 100644 index 0000000..4a22cd5 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectBounds/ObjectBounds.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d4ce56ba58e8048bfa52c9dcaccc8ebf +timeCreated: 1456675988 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectPool.meta b/Assets/CorgiEngine/MMTools/ObjectPool.meta new file mode 100644 index 0000000..5a754a1 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 269bfcb0e3d5a4350a24346983d94b29 +folderAsset: yes +timeCreated: 1434369355 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs b/Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs new file mode 100644 index 0000000..11f3b92 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs @@ -0,0 +1,514 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; +using System; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + [Serializable] + public class MultipleObjectPoolerObject + { + public GameObject GameObjectToPool; + public int PoolSize; + public bool PoolCanExpand = true; + public bool Enabled = true; + } + + public enum PoolingMethods { OriginalOrder, OriginalOrderSequential, RandomBetweenObjects, RandomPoolSizeBased } + + /// + /// This class allows you to have a pool of various objects to pool from. + /// + public class MultipleObjectPooler : ObjectPooler + { + public List Pool; + [Information("A MultipleObjectPooler is a reserve of objects, to be used by a Spawner. When asked, it will return an object from the pool (ideally an inactive one) chosen based on the pooling method you've chosen.\n- OriginalOrder will spawn objects in the order you've set them in the inspector (from top to bottom)\n- OriginalOrderSequential will do the same, but will empty each pool before moving to the next object\n- RandomBetweenObjects will pick one object from the pool, at random, but ignoring its pool size, each object has equal chances to get picked\n- PoolSizeBased randomly choses one object from the pool, based on its pool size probability (the larger the pool size, the higher the chances it'll get picked)'...",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + public PoolingMethods PoolingMethod = PoolingMethods.RandomPoolSizeBased; + [Information("If you set CanPoolSameObjectTwice to false, the Pooler will try to prevent the same object from being pooled twice to avoid repetition. This will only affect random pooling methods, not ordered pooling.",MoreMountains.Tools.InformationAttribute.InformationType.Info,false)] + public bool CanPoolSameObjectTwice=true; + + /// this object is just used to group the pooled objects + protected GameObject _waitingPool; + /// the actual object pool + protected List _pooledGameObjects; + protected List _pooledGameObjectsOriginalOrder; + protected List _randomizedPool; + protected string _lastPooledObjectName; + + protected int _currentIndex=0; + + /// + /// Fills the object pool with the amount of objects you specified in the inspector. + /// + protected override void FillObjectPool() + { + // we create a container that will hold all the instances we create + _waitingPool = new GameObject("[MultipleObjectPooler] " + this.name); + // we initialize the pool + _pooledGameObjects = new List(); + // we create a randomized pool for picking purposes + _randomizedPool = new List() ; + for (int i = 0; i < Pool.Count; i++) + { + _randomizedPool.Add(Pool[i]); + } + _randomizedPool.Shuffle(); + + // if there's only one item in the Pool, we force CanPoolSameObjectTwice to true + if (Pool.Count <= 1) + { + CanPoolSameObjectTwice=true; + } + + bool stillObjectsToPool; + int[] poolSizes; + + // if we're gonna pool in the original inspector order + switch (PoolingMethod) + { + case PoolingMethods.OriginalOrder: + + stillObjectsToPool = true; + _pooledGameObjectsOriginalOrder = new List(); + + // we store our poolsizes in a temp array so it doesn't impact the inspector + poolSizes = new int[Pool.Count]; + for (int i = 0; i < Pool.Count; i++) + { + poolSizes[i] = Pool[i].PoolSize; + } + + // we go through our objects in the order they were in the inspector, and fill the pool while we find objects to add + while (stillObjectsToPool) + { + stillObjectsToPool = false; + for (int i = 0; i < Pool.Count; i++) + { + if (poolSizes[i] > 0) + { + AddOneObjectToThePool(Pool[i].GameObjectToPool); + poolSizes[i]--; + stillObjectsToPool = true; + } + } + } + break; + case PoolingMethods.OriginalOrderSequential: + + _pooledGameObjectsOriginalOrder = new List(); + + // we store our poolsizes in a temp array so it doesn't impact the inspector + foreach (MultipleObjectPoolerObject pooledGameObject in Pool) + { + for (int i = 0; i < pooledGameObject.PoolSize ; i++ ) + { + AddOneObjectToThePool(pooledGameObject.GameObjectToPool); + } + } + break; + default: + int k = 0; + // for each type of object specified in the inspector + foreach (MultipleObjectPoolerObject pooledGameObject in Pool) + { + // if there's no specified number of objects to pool for that type of object, we do nothing and exit + if (k > Pool.Count) { return; } + + // we add, one by one, the number of objects of that type, as specified in the inspector + for (int j = 0; j < Pool[k].PoolSize; j++) + { + AddOneObjectToThePool(pooledGameObject.GameObjectToPool); + } + k++; + } + break; + } + if ((PoolingMethod==PoolingMethods.OriginalOrder) || (PoolingMethod == PoolingMethods.OriginalOrderSequential)) + { + foreach (GameObject pooledObject in _pooledGameObjects) + { + _pooledGameObjectsOriginalOrder.Add(pooledObject); + } + } + + + } + + /// + /// Adds one object of the specified type to the object pool. + /// + /// The object that just got added. + /// The type of object to add to the pool. + protected virtual GameObject AddOneObjectToThePool(GameObject typeOfObject) + { + GameObject newGameObject = (GameObject)Instantiate(typeOfObject); + newGameObject.gameObject.SetActive(false); + newGameObject.transform.SetParent(_waitingPool.transform); + newGameObject.name=typeOfObject.name; + _pooledGameObjects.Add(newGameObject); + return newGameObject; + } + + /// + /// Gets a random object from the pool. + /// + /// The pooled game object. + public override GameObject GetPooledGameObject() + { + GameObject pooledGameObject; + switch (PoolingMethod) + { + case PoolingMethods.OriginalOrder: + pooledGameObject = GetPooledGameObjectOriginalOrder(); + break; + case PoolingMethods.RandomPoolSizeBased: + pooledGameObject = GetPooledGameObjectPoolSizeBased(); + break; + case PoolingMethods.RandomBetweenObjects: + pooledGameObject = GetPooledGameObjectRandomBetweenObjects(); + break; + case PoolingMethods.OriginalOrderSequential: + pooledGameObject = GetPooledGameObjectOriginalOrder(); + break; + default: + pooledGameObject = null; + break; + } + if (pooledGameObject!=null) + { + _lastPooledObjectName = pooledGameObject.name; + } + else + { + _lastPooledObjectName=""; + } + return pooledGameObject; + } + + /// + /// Tries to find a gameobject in the pool according to the order the list has been setup in (one of each, no matter how big their respective pool sizes) + /// + /// The pooled game object original order. + protected virtual GameObject GetPooledGameObjectOriginalOrder() + { + int newIndex; + // if we've reached the end of our list, we start again from the beginning + if (_currentIndex>=_pooledGameObjectsOriginalOrder.Count) + { + ResetCurrentIndex (); + } + + MultipleObjectPoolerObject searchedObject = GetPoolObject(_pooledGameObjects[_currentIndex].gameObject); + + if (_currentIndex >= _pooledGameObjects.Count) { return null; } + if (!searchedObject.Enabled) { _currentIndex++; return null; } + + // if the object is already active, we need to find another one + if (_pooledGameObjects[_currentIndex].gameObject.activeInHierarchy) + { + GameObject findObject = FindInactiveObject(_pooledGameObjects[_currentIndex].gameObject.name,_pooledGameObjects); + if (findObject != null) + { + _currentIndex++; + return findObject; + } + + // if its pool can expand, we create a new one + if (searchedObject.PoolCanExpand) + { + _currentIndex++; + return AddOneObjectToThePool(searchedObject.GameObjectToPool); + } + else + { + // if it can't expand we return nothing + return null; + } + } + else + { + // if the object is inactive, we return it + newIndex = _currentIndex; + _currentIndex++; + return _pooledGameObjects[newIndex]; + } + } + + /// + /// Randomly choses one object from the pool, based on its pool size probability (the larger the pool size, the higher the chances it'll get picked) + /// + /// The pooled game object pool size based. + protected virtual GameObject GetPooledGameObjectPoolSizeBased() + { + // we get a random index + int randomIndex = UnityEngine.Random.Range(0, _pooledGameObjects.Count); + + int overflowCounter=0; + + // we check to see if that object is enabled, if it's not we loop + while (!PoolObjectEnabled(_pooledGameObjects[randomIndex]) && overflowCounter < _pooledGameObjects.Count) + { + randomIndex = UnityEngine.Random.Range(0, _pooledGameObjects.Count); + overflowCounter++; + } + if (!PoolObjectEnabled(_pooledGameObjects[randomIndex])) + { + return null; + } + + // if we can't pool the same object twice, we'll loop for a while to try and get another one + overflowCounter = 0; + while (!CanPoolSameObjectTwice + && _pooledGameObjects[randomIndex].name == _lastPooledObjectName + && overflowCounter < _pooledGameObjects.Count) + { + randomIndex = UnityEngine.Random.Range(0, _pooledGameObjects.Count); + overflowCounter++; + } + + // if the item we've picked is active + if (_pooledGameObjects[randomIndex].gameObject.activeInHierarchy) + { + // we try to find another inactive object of the same type + GameObject pulledObject = FindInactiveObject(_pooledGameObjects[randomIndex].gameObject.name,_pooledGameObjects); + if (pulledObject!=null) + { + return pulledObject; + } + else + { + // if we couldn't find an inactive object of this type, we see if it can expand + MultipleObjectPoolerObject searchedObject = GetPoolObject(_pooledGameObjects[randomIndex].gameObject); + if (searchedObject==null) + { + return null; + } + // if the pool for this object is allowed to grow (this is set in the inspector if you're wondering) + if (searchedObject.PoolCanExpand) + { + return AddOneObjectToThePool(searchedObject.GameObjectToPool); + } + else + { + // if it's not allowed to grow, we return nothing. + return null; + } + } + } + else + { + // if the pool wasn't empty, we return the random object we've found. + return _pooledGameObjects[randomIndex]; + } + } + + /// + /// Gets one object from the pool, at random, but ignoring its pool size, each object has equal chances to get picked + /// + /// The pooled game object random between objects. + protected virtual GameObject GetPooledGameObjectRandomBetweenObjects() + { + // we pick one of the objects in the original pool at random + int randomIndex = UnityEngine.Random.Range(0, Pool.Count); + + int overflowCounter=0; + + // if we can't pool the same object twice, we'll loop for a while to try and get another one + while (!CanPoolSameObjectTwice && Pool[randomIndex].GameObjectToPool.name == _lastPooledObjectName && overflowCounter < _pooledGameObjects.Count ) + { + randomIndex = UnityEngine.Random.Range(0, Pool.Count); + overflowCounter++; + } + int originalRandomIndex = randomIndex+1; + + bool objectFound=false; + + // while we haven't found an object to return, and while we haven't gone through all the different object types, we keep going + overflowCounter=0; + while (!objectFound + && randomIndex != originalRandomIndex + && overflowCounter < _pooledGameObjects.Count) + { + // if our index is at the end, we reset it + if (randomIndex >= Pool.Count) + { + randomIndex=0; + } + + if (!Pool[randomIndex].Enabled) + { + randomIndex++; + overflowCounter++; + continue; + } + + // we try to find an inactive object of that type in the pool + GameObject newGameObject = FindInactiveObject(Pool[randomIndex].GameObjectToPool.name, _pooledGameObjects); + if (newGameObject!=null) + { + objectFound=true; + return newGameObject; + } + else + { + // if there's none and if we can expand, we expand + if (Pool[randomIndex].PoolCanExpand) + { + return AddOneObjectToThePool(Pool[randomIndex].GameObjectToPool); + } + } + randomIndex++; + overflowCounter++; + } + return null; + } + + /// + /// Gets an object of the specified name from the pool + /// + /// The pooled game object of type. + /// Type. + protected virtual GameObject GetPooledGameObjectOfType(string searchedName) + { + GameObject newObject = FindInactiveObject(searchedName,_pooledGameObjects); + + if (newObject!=null) + { + return newObject; + } + else + { + // if we've not returned the object, that means the pool is empty (at least it means it doesn't contain any object of that specific type) + // so if the pool is allowed to expand + GameObject searchedObject = FindObject(searchedName,_pooledGameObjects); + if (searchedObject == null) + { + return null; + } + + if (GetPoolObject(FindObject(searchedName,_pooledGameObjects)).PoolCanExpand) + { + // we create a new game object of that type, we add it to the pool for further use, and return it. + GameObject newGameObject = (GameObject)Instantiate(searchedObject); + _pooledGameObjects.Add(newGameObject); + return newGameObject; + } + } + + // if the pool was empty for that object and not allowed to expand, we return nothing. + return null; + } + + /// + /// Finds an inactive object in the pool based on its name. + /// Returns null if no inactive object by that name were found in the pool + /// + /// The inactive object. + /// Searched name. + protected virtual GameObject FindInactiveObject(string searchedName, List list) + { + for (int i = 0; i < list.Count; i++) + { + // if we find an object inside the pool that matches the asked type + if (list[i].name.Equals(searchedName)) + { + // and if that object is inactive right now + if (!list[i].gameObject.activeInHierarchy) + { + // we return it + return list[i]; + } + } + } + return null; + } + + protected virtual GameObject FindAnyInactiveObject(List list) + { + for (int i = 0; i < list.Count; i++) + { + // and if that object is inactive right now + if (!list[i].gameObject.activeInHierarchy) + { + // we return it + return list[i]; + } + } + return null; + } + + /// + /// Finds an object in the pool based on its name, active or inactive + /// Returns null if there's no object by that name in the pool + /// + /// The object. + /// Searched name. + protected virtual GameObject FindObject(string searchedName,List list) + { + for (int i = 0; i < list.Count; i++) + { + // if we find an object inside the pool that matches the asked type + if (list[i].name.Equals(searchedName)) + { + // and if that object is inactive right now + return list[i]; + } + } + return null; + } + + /// + /// Returns (if it exists) the MultipleObjectPoolerObject from the original Pool based on a GameObject. + /// Note that this is name based. + /// + /// The pool object. + /// Tested object. + protected virtual MultipleObjectPoolerObject GetPoolObject(GameObject testedObject) + { + if (testedObject==null) + { + return null; + } + int i=0; + foreach(MultipleObjectPoolerObject poolerObject in Pool) + { + if (testedObject.name.Equals(poolerObject.GameObjectToPool.name)) + { + return (poolerObject); + } + i++; + } + return null; + } + + protected virtual bool PoolObjectEnabled(GameObject testedObject) + { + MultipleObjectPoolerObject searchedObject = GetPoolObject(testedObject); + if (searchedObject != null) + { + return searchedObject.Enabled; + } + else + { + return false; + } + } + + public virtual void EnableObjects(string name,bool newStatus) + { + foreach(MultipleObjectPoolerObject poolerObject in Pool) + { + if (name.Equals(poolerObject.GameObjectToPool.name)) + { + poolerObject.Enabled = newStatus; + } + } + } + + public virtual void ResetCurrentIndex() + { + _currentIndex=0; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs.meta b/Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs.meta new file mode 100644 index 0000000..4580682 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/MultipleObjectPooler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 72614d3b296314bfb993b1b8fe96c02c +timeCreated: 1437564518 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs b/Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs new file mode 100644 index 0000000..6b027f1 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs @@ -0,0 +1,50 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + /// + /// A base class, meant to be extended depending on the use (simple, multiple object pooler), and used as an interface by the spawners. + /// Still handles common stuff like singleton and initialization on start(). + /// DO NOT add this class to a prefab, nothing would happen. Instead, add SimpleObjectPooler or MultipleObjectPooler. + /// + public class ObjectPooler : MonoBehaviour + { + public static ObjectPooler Instance; + + /// + /// Singleton + /// + protected virtual void Awake() + { + Instance = this; + FillObjectPool(); + } + + /// + /// On start, we fill the pool with the specified gameobjects + /// + protected virtual void Start() + { + + } + + /// + /// Implement this method to fill the pool with objects + /// + protected virtual void FillObjectPool() + { + return ; + } + + /// + /// Implement this method to return a gameobject + /// + /// The pooled game object. + public virtual GameObject GetPooledGameObject() + { + return null; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs.meta b/Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs.meta new file mode 100644 index 0000000..ae13cb8 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/ObjectPooler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 712b8e171d90a2f4aac05486feb75ade +timeCreated: 1447376640 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs b/Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs new file mode 100644 index 0000000..c6035a3 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs @@ -0,0 +1,61 @@ +using UnityEngine; +using System.Collections; +using MoreMountains.Tools; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Add this class to an object that you expect to pool from an objectPooler. + /// Note that these objects can't be destroyed by calling Destroy(), they'll just be set inactive (that's the whole point). + /// + public class PoolableObject : ObjectBounds + { + public delegate void Events(); + public event Events OnSpawnComplete; + + /// The life time, in seconds, of the object. If set to 0 it'll live forever, if set to any positive value it'll be set inactive after that time. + public float LifeTime = 0f; + + /// + /// Turns the instance inactive, in order to eventually reuse it. + /// + public virtual void Destroy() + { + gameObject.SetActive(false); + } + + protected virtual void Update() + { + + } + + /// + /// When the objects get enabled (usually after having been pooled from an ObjectPooler, we initiate its death countdown. + /// + protected virtual void OnEnable() + { + Size = GetBounds().extents * 2; + if (LifeTime>0) + { + Invoke("Destroy", LifeTime); + } + } + + /// + /// When the object gets disabled (maybe it got out of bounds), we cancel its programmed death + /// + protected virtual void OnDisable() + { + CancelInvoke(); + } + + public void TriggerOnSpawnComplete() + { + if(OnSpawnComplete != null) + { + OnSpawnComplete(); + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs.meta b/Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs.meta new file mode 100644 index 0000000..2cd2b49 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/PoolableObject.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 26b5a1513eddd15488e8dc903ac7dcbc +timeCreated: 1434220996 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs b/Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs new file mode 100644 index 0000000..d89b259 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs @@ -0,0 +1,83 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + + public class SimpleObjectPooler : ObjectPooler + { + /// the game object we'll instantiate + public GameObject GameObjectToPool; + /// the number of objects we'll add to the pool + public int PoolSize = 20; + /// if true, the pool will automatically add objects to the itself if needed + public bool PoolCanExpand = true; + + /// this object is just used to group the pooled objects + protected GameObject _waitingPool; + /// the actual object pool + protected List _pooledGameObjects; + + /// + /// Fills the object pool with the gameobject type you've specified in the inspector + /// + protected override void FillObjectPool() + { + // we create a container that will hold all the instances we create + _waitingPool = new GameObject("[SimpleObjectPooler] " + this.name); + + // we initialize the list we'll use to + _pooledGameObjects = new List(); + + // we add to the pool the specified number of objects + for (int i = 0; i < PoolSize; i++) + { + AddOneObjectToThePool (); + } + } + + /// + /// This method returns one inactive object from the pool + /// + /// The pooled game object. + public override GameObject GetPooledGameObject() + { + // we go through the pool looking for an inactive object + for (int i=0; i< _pooledGameObjects.Count; i++) + { + if (!_pooledGameObjects[i].gameObject.activeInHierarchy) + { + // if we find one, we return it + return _pooledGameObjects[i]; + } + } + // if we haven't found an inactive object (the pool is empty), and if we can extend it, we add one new object to the pool, and return it + if (PoolCanExpand) + { + return AddOneObjectToThePool(); + } + // if the pool is empty and can't grow, we return nothing. + return null; + } + + /// + /// Adds one object of the specified type (in the inspector) to the pool. + /// + /// The one object to the pool. + protected virtual GameObject AddOneObjectToThePool() + { + if (GameObjectToPool==null) + { + Debug.LogWarning("The "+gameObject.name+" ObjectPooler doesn't have any GameObjectToPool defined.", gameObject); + return null; + } + GameObject newGameObject = (GameObject)Instantiate(GameObjectToPool); + newGameObject.gameObject.SetActive(false); + newGameObject.transform.SetParent(_waitingPool.transform); + newGameObject.name=GameObjectToPool.name+"-"+_pooledGameObjects.Count; + _pooledGameObjects.Add(newGameObject); + return newGameObject; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs.meta b/Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs.meta new file mode 100644 index 0000000..a850e3f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/ObjectPool/SimpleObjectPooler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 74dcd6b3416498f438586dfec5198aa1 +timeCreated: 1434219314 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Particles.meta b/Assets/CorgiEngine/MMTools/Particles.meta new file mode 100644 index 0000000..b897d81 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6f7001b5ded60f94cacfd8da7ccb88b4 +folderAsset: yes +timeCreated: 1462451430 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs b/Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs new file mode 100644 index 0000000..e3a8ded --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs @@ -0,0 +1,64 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// Add this class to a ParticleSystem so it auto destroys once it has stopped emitting. + /// Make sure your ParticleSystem isn't looping, otherwise this script will be useless + /// + public class AutoDestroyParticleSystem : MonoBehaviour + { + /// True if the ParticleSystem should also destroy its parent + public bool DestroyParent=false; + + /// If for some reason your particles don't get destroyed automatically at the end of the emission, you can force a destroy after a delay. Leave it at zero otherwise. + public float DestroyDelay=0f; + + protected ParticleSystem _particleSystem; + protected float _startTime; + + /// + /// Initialization, we get the ParticleSystem component + /// + protected virtual void Start() + { + _particleSystem = GetComponent(); + if (DestroyDelay!=0) + { + _startTime = Time.time; + } + } + + /// + /// When the ParticleSystem stops playing, we destroy it. + /// + protected virtual void Update() + { + if ( (DestroyDelay!=0) && (Time.time - _startTime > DestroyDelay) ) + { + DestroyParticleSystem(); + } + + if (_particleSystem.isPlaying) + { + return; + } + + DestroyParticleSystem(); + } + + protected virtual void DestroyParticleSystem() + { + if (transform.parent!=null) + { + if(DestroyParent) + { + Destroy(transform.parent.gameObject); + } + } + + Destroy (gameObject); + } + } +} diff --git a/Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs.meta b/Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs.meta new file mode 100644 index 0000000..00207e1 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/AutoDestroyParticleSystem.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 74f903e375ff34b1c87c4ebff3cacfb5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs b/Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs new file mode 100644 index 0000000..ae1d13a --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs @@ -0,0 +1,42 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + [ExecuteInEditMode] + /// + /// Adds this class to a UnityStandardAssets.ImageEffects.GlobalFog to change its color + /// Why this is not native, I don't know. + /// + public class ChangeFogColor : MonoBehaviour + { + /// Adds this class to a UnityStandardAssets.ImageEffects.GlobalFog to change its color + [Information("Adds this class to a UnityStandardAssets.ImageEffects.GlobalFog to change its color",InformationAttribute.InformationType.Info,false)] + public Color FogColor; + + /// + /// Sets the fog's color to the one set in the inspector + /// + protected virtual void SetupFogColor () + { + RenderSettings.fogColor = FogColor; + RenderSettings.fog = true; + } + + /// + /// On Start(), we set the fog's color + /// + protected virtual void Start() + { + SetupFogColor(); + } + + /// + /// Whenever there's a change in the camera's inspector, we change the fog's color + /// + protected virtual void OnValidate() + { + SetupFogColor(); + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs.meta b/Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs.meta new file mode 100644 index 0000000..1433c40 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/ChangeFogColor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9262c2614cea90647b2daea11fca03b8 +timeCreated: 1467547351 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Particles/Editor.meta b/Assets/CorgiEngine/MMTools/Particles/Editor.meta new file mode 100644 index 0000000..7ee4e80 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3dd6fd169b2c230438f456fa613b23a0 +folderAsset: yes +timeCreated: 1491156248 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Particles/Editor/MMTrailRendererSortingLayerEditor.cs b/Assets/CorgiEngine/MMTools/Particles/Editor/MMTrailRendererSortingLayerEditor.cs new file mode 100644 index 0000000..c1ada6e --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/Editor/MMTrailRendererSortingLayerEditor.cs @@ -0,0 +1,62 @@ +using System; +using UnityEngine; +using UnityEditor; +using UnityEditorInternal; +using System.Reflection; + +namespace MoreMountains.Tools +{ + [CanEditMultipleObjects()] + [CustomEditor(typeof(MMTrailRendererSortingLayer))] + public class RendererLayerEditor : Editor + { + int popupMenuIndex; + string[] sortingLayerNames; + protected MMTrailRendererSortingLayer _mmTrailRendererSortingLayer; + protected TrailRenderer _trailRenderer; + + void OnEnable() + { + sortingLayerNames = GetSortingLayerNames(); + _mmTrailRendererSortingLayer = (MMTrailRendererSortingLayer)target; + _trailRenderer = _mmTrailRendererSortingLayer.GetComponent (); + + for (int i = 0; i + /// Adds this class to particles to force their sorting layer + /// + public class VisibleParticle : MonoBehaviour { + + /// + /// Sets the particle system's renderer to the Visible Particles sorting layer + /// + protected virtual void Start () + { + GetComponent().GetComponent().sortingLayerName = "VisibleParticles"; + } + } +} diff --git a/Assets/CorgiEngine/MMTools/Particles/VisibleParticle.cs.meta b/Assets/CorgiEngine/MMTools/Particles/VisibleParticle.cs.meta new file mode 100644 index 0000000..b2408fc --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Particles/VisibleParticle.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 04c04bad88f3aab48a659dbd2a5844ab +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/CorgiEngine/MMTools/RigidbodyInterface.meta b/Assets/CorgiEngine/MMTools/RigidbodyInterface.meta new file mode 100644 index 0000000..23380e1 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/RigidbodyInterface.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: be85ff8dc0782b54db832e0de11d8a69 +folderAsset: yes +timeCreated: 1462451410 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs b/Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs new file mode 100644 index 0000000..7a19e0e --- /dev/null +++ b/Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs @@ -0,0 +1,315 @@ +using UnityEngine; +using System.Collections; + +namespace MoreMountains.Tools +{ + /// + /// This class acts as an interface to allow the demo levels to work whether the environment (colliders, rigidbodies) are set as 2D or 3D. + /// If you already know for sure that you're going for a 2D or 3D game, I suggest you replace the use of this class with the appropriate classes. + /// + public class RigidbodyInterface : MonoBehaviour + { + /// + /// Returns the rigidbody's position + /// + /// The position. + public Vector3 position + { + get + { + if (_rigidbody2D != null) + { + return _rigidbody2D.position; + } + if (_rigidbody != null) + { + return _rigidbody.position; + } + return Vector3.zero; + } + set { } + } + + /// + /// Only use if you absolutely need to target the rigidbody2D specifically + /// + /// The internal rigid body2 d. + public Rigidbody2D InternalRigidBody2D + { + get { + return _rigidbody2D; + } + } + + /// + /// Only use if you absolutely need to target the rigidbody2D specifically + /// + /// The internal rigid body. + public Rigidbody InternalRigidBody + { + get { + return _rigidbody; + } + } + + /// + /// Gets or sets the velocity of the rigidbody associated to the interface. + /// + /// The velocity. + public Vector3 Velocity + { + get + { + if (_mode == "2D") + { + return(_rigidbody2D.velocity); + } + else + { + if (_mode == "3D") + { + return(_rigidbody.velocity); + } + else + { + return new Vector3(0,0,0); + } + } + } + set + { + if (_mode == "2D") { + _rigidbody2D.velocity = value; + } + if (_mode == "3D") { + _rigidbody.velocity = value; + } + } + } + + /// + /// Gets the collider bounds. + /// + /// The collider bounds. + public Bounds ColliderBounds + { + get + { + if (_rigidbody2D != null) + { + return _collider2D.bounds; + } + if (_rigidbody != null) + { + return _collider.bounds; + } + return new Bounds(); + } + } + + /// + /// Gets a value indicating whether this is kinematic. + /// + /// true if is kinematic; otherwise, false. + public bool isKinematic + { + get + { + if (_mode == "2D") + { + return(_rigidbody2D.isKinematic); + } + if (_mode == "3D") + { + return(_rigidbody.isKinematic); + } + return false; + } + } + + protected string _mode; + protected Rigidbody2D _rigidbody2D; + protected Rigidbody _rigidbody; + protected Collider2D _collider2D; + protected Collider _collider; + protected Bounds _colliderBounds; + + /// + /// Initialization + /// + protected virtual void Awake () + { + // we check for rigidbodies, and depending on their presence determine if the interface will work with 2D or 3D rigidbodies and colliders. + _rigidbody2D=GetComponent(); + _rigidbody=GetComponent(); + + if (_rigidbody2D != null) + { + _mode="2D"; + _collider2D = GetComponent (); + } + if (_rigidbody != null) + { + _mode="3D"; + _collider = GetComponent (); + } + if (_rigidbody==null && _rigidbody2D==null) + { + Debug.LogWarning("A RigidBodyInterface has been added to "+gameObject+" but there's no Rigidbody or Rigidbody2D on it.", gameObject); + } + } + + /// + /// Adds the specified force to the rigidbody associated to the interface.. + /// + /// Force. + public virtual void AddForce(Vector3 force) + { + if (_mode == "2D") + { + _rigidbody2D.AddForce(force,ForceMode2D.Impulse); + } + if (_mode == "3D") + { + _rigidbody.AddForce(force); + } + } + + /// + /// Adds the specified relative force to the rigidbody associated to the interface.. + /// + /// Force. + public virtual void AddRelativeForce(Vector3 force) + { + if (_mode == "2D") + { + _rigidbody2D.AddRelativeForce(force,ForceMode2D.Impulse); + } + if (_mode == "3D") + { + _rigidbody.AddRelativeForce(force); + } + } + + + + /// + /// Move the rigidbody to the position vector specified + /// + /// + public virtual void MovePosition(Vector3 newPosition) + { + if (_mode == "2D") + { + _rigidbody2D.MovePosition(newPosition); + } + if (_mode == "3D") + { + _rigidbody.MovePosition(newPosition); + } + } + + /// + /// Resets the angular velocity. + /// + public virtual void ResetAngularVelocity() + { + if (_mode == "2D") + { + _rigidbody2D.angularVelocity = 0; + } + if (_mode == "3D") + { + _rigidbody.angularVelocity = Vector3.zero; + } + } + + /// + /// Resets the rotation. + /// + public virtual void ResetRotation() + { + if (_mode == "2D") + { + _rigidbody2D.rotation = 0; + } + if (_mode == "3D") + { + _rigidbody.rotation = Quaternion.identity; + } + } + + + /// + /// Determines whether the rigidbody associated to the interface is kinematic + /// + /// true if this instance is kinematic the specified status; otherwise, false. + /// If set to true status. + public virtual void IsKinematic(bool status) + { + if (_mode == "2D") + { + _rigidbody2D.isKinematic=status; + } + if (_mode == "3D") + { + _rigidbody.isKinematic=status; + } + } + + + /// + /// Enables the box collider associated to the interface. + /// + /// If set to true status. + public virtual void EnableBoxCollider(bool status) + { + if (_mode == "2D") + { + GetComponent().enabled=status; + } + if (_mode == "3D") + { + GetComponent().enabled=status; + } + } + + /// + /// Use this to check if you're dealing with a 3D object + /// + /// true if this instance is3 d; otherwise, false. + public bool Is3D + { + get + { + if (_mode=="3D") + { + return true; + } + else + { + return false; + } + } + } + + /// + /// Use this to check if you're dealing with a 2D object + /// + /// The position. + public bool Is2D + { + get + { + if (_mode=="2D") + { + return true; + } + else + { + return false; + } + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs.meta b/Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs.meta new file mode 100644 index 0000000..111a858 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/RigidbodyInterface/RigidbodyInterface.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 151cbd258ab4fe74cbc312ac33dc6463 +timeCreated: 1440662735 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/SaveLoad.meta b/Assets/CorgiEngine/MMTools/SaveLoad.meta new file mode 100644 index 0000000..ed6a74d --- /dev/null +++ b/Assets/CorgiEngine/MMTools/SaveLoad.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 79ffe05f70177854bb2bc948327922a7 +folderAsset: yes +timeCreated: 1482071371 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs b/Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs new file mode 100644 index 0000000..c099763 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs @@ -0,0 +1,77 @@ +using UnityEngine; +using System.Collections; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +namespace MoreMountains.Tools +{ + public static class SaveManager + { + private const string _baseFolderName = "/MMData/"; + private const string _defaultFolderName = "SaveManager"; + + /*protected virtual void Awake() + { + + _saveFileName = _savePath+gameObject.name+".achievements.binary"; + }*/ + + static string DetermineSavePath(string folderName = _defaultFolderName) + { + string savePath; + if (Application.platform == RuntimePlatform.IPhonePlayer) + { + savePath = Application.persistentDataPath + _baseFolderName; + } + else + { + savePath = Application.dataPath + _baseFolderName; + } + savePath = savePath + folderName + "/"; + return savePath; + } + + static string DetermineSaveFileName(string fileName) + { + return fileName+".binary"; + } + + public static void Save(object saveObject, string fileName, string foldername = _defaultFolderName) + { + string savePath = DetermineSavePath(foldername); + string saveFileName = DetermineSaveFileName(fileName); + + if (!Directory.Exists(savePath)) + { + //MMDebug.DebugLogTime("save : directory doesn't exist, let's create it"); + Directory.CreateDirectory(savePath); + } + BinaryFormatter formatter = new BinaryFormatter(); + FileStream saveFile = File.Create(saveFileName); + formatter.Serialize(saveFile, saveObject); + + saveFile.Close(); + } + + public static object Load(object saveObject, string fileName, string foldername = _defaultFolderName) + { + string savePath = DetermineSavePath(foldername); + string saveFileName = savePath + DetermineSaveFileName(fileName); + + object returnObject; + + // if the MMSaves directory or the save file doesn't exist, there's nothing to load, we do nothing and exit + if (!Directory.Exists(savePath) || !File.Exists(saveFileName)) + { + //MMDebug.DebugLogTime("nothing to load at "+_saveFileName); + return null; + } + BinaryFormatter formatter = new BinaryFormatter(); + FileStream saveFile = File.Open(saveFileName, FileMode.Open, FileAccess.Read, FileShare.Read); + returnObject = formatter.Deserialize(saveFile); + saveFile.Close(); + + return returnObject; + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs.meta b/Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs.meta new file mode 100644 index 0000000..5c1bc70 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/SaveLoad/SaveLoadManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: aa3d0794b4f8e4b40b6a3dde5b794c0b +timeCreated: 1478535356 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Singletons.meta b/Assets/CorgiEngine/MMTools/Singletons.meta new file mode 100644 index 0000000..9bd1721 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Singletons.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f40f4cea7b7008544a54de513bda089b +folderAsset: yes +timeCreated: 1462451198 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/Singletons/PersistentHumbleSingleton.cs b/Assets/CorgiEngine/MMTools/Singletons/PersistentHumbleSingleton.cs new file mode 100644 index 0000000..6e12a3c --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Singletons/PersistentHumbleSingleton.cs @@ -0,0 +1,69 @@ +using UnityEngine; +using System; + +namespace MoreMountains.Tools +{ + /// + /// Persistent humble singleton, basically a classic singleton but will destroy any other older components of the same type it finds on awake + /// + public class PersistentHumbleSingleton : MonoBehaviour where T : Component + { + protected static T _instance; + public float InitializationTime; + + /// + /// Singleton design pattern + /// + /// The instance. + public static T Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType (); + if (_instance == null) + { + GameObject obj = new GameObject (); + obj.hideFlags = HideFlags.HideAndDontSave; + _instance = obj.AddComponent (); + } + } + return _instance; + } + } + + /// + /// On awake, we check if there's already a copy of the object in the scene. If there's one, we destroy it. + /// + protected virtual void Awake () + { + if (!Application.isPlaying) + { + return; + } + + InitializationTime=Time.time; + + DontDestroyOnLoad (this.gameObject); + // we check for existing objects of the same type + T[] check = FindObjectsOfType(); + foreach (T searched in check) + { + if (searched!=this) + { + // if we find another object of the same type (not this), and if it's older than our current object, we destroy it. + if (searched.GetComponent>().InitializationTime + /// Persistent singleton. + /// + public class PersistentSingleton : MonoBehaviour where T : Component + { + protected static T _instance; + protected bool _enabled; + + /// + /// Singleton design pattern + /// + /// The instance. + public static T Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType (); + if (_instance == null) + { + GameObject obj = new GameObject (); + //obj.hideFlags = HideFlags.HideAndDontSave; + _instance = obj.AddComponent (); + } + } + return _instance; + } + } + + /// + /// On awake, we check if there's already a copy of the object in the scene. If there's one, we destroy it. + /// + protected virtual void Awake () + { + if (!Application.isPlaying) + { + return; + } + + if(_instance == null) + { + //If I am the first instance, make me the Singleton + _instance = this as T; + DontDestroyOnLoad (transform.gameObject); + _enabled = true; + } + else + { + //If a Singleton already exists and you find + //another reference in scene, destroy it! + if(this != _instance) + { + Destroy(this.gameObject); + } + } + } + } +} diff --git a/Assets/CorgiEngine/MMTools/Singletons/PersistentSingleton.cs.meta b/Assets/CorgiEngine/MMTools/Singletons/PersistentSingleton.cs.meta new file mode 100644 index 0000000..cdfccb4 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Singletons/PersistentSingleton.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 32ed9853196ac60439e759122c98a09f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/CorgiEngine/MMTools/Singletons/Singleton.cs b/Assets/CorgiEngine/MMTools/Singletons/Singleton.cs new file mode 100644 index 0000000..7894a19 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Singletons/Singleton.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +namespace MoreMountains.Tools +{ + /// + /// Singleton pattern. + /// + public class Singleton : MonoBehaviour where T : Component + { + protected static T _instance; + + /// + /// Singleton design pattern + /// + /// The instance. + public static T Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType (); + if (_instance == null) + { + GameObject obj = new GameObject (); + //obj.hideFlags = HideFlags.HideAndDontSave; + _instance = obj.AddComponent (); + } + } + return _instance; + } + } + + /// + /// On awake, we initialize our instance. Make sure to call base.Awake() in override if you need awake. + /// + protected virtual void Awake () + { + if (!Application.isPlaying) + { + return; + } + + _instance = this as T; + } + } +} diff --git a/Assets/CorgiEngine/MMTools/Singletons/Singleton.cs.meta b/Assets/CorgiEngine/MMTools/Singletons/Singleton.cs.meta new file mode 100644 index 0000000..ee9167f --- /dev/null +++ b/Assets/CorgiEngine/MMTools/Singletons/Singleton.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 5dbb0d969791b2a4f991754c03cf2579 +timeCreated: 1462623191 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/StateMachine.meta b/Assets/CorgiEngine/MMTools/StateMachine.meta new file mode 100644 index 0000000..dcc3893 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/StateMachine.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 27f16a0cb1e7db949bbdf8c014b0d661 +folderAsset: yes +timeCreated: 1470161054 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs b/Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs new file mode 100644 index 0000000..b77de01 --- /dev/null +++ b/Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs @@ -0,0 +1,136 @@ +using UnityEngine; +using UnityEngine.UI; +using System.Collections; +using MoreMountains.Tools; +using System; +using System.Collections.Generic; + +namespace MoreMountains.Tools +{ + public struct MMStateChangeEvent where T: struct, IComparable, IConvertible, IFormattable + { + public GameObject Target; + public MMStateMachine TargetStateMachine; + public T NewState; + public T PreviousState; + + public MMStateChangeEvent(MMStateMachine stateMachine) + { + Target = stateMachine.Target; + TargetStateMachine = stateMachine; + NewState = stateMachine.CurrentState; + PreviousState = stateMachine.PreviousState; + } + } + + /// + /// Public interface for the state machine. + /// This is used by the StateMachineProcessor. + /// + public interface MMIStateMachine + { + bool TriggerEvents { get; set; } + } + + /// + /// StateMachine manager, designed with simplicity in mind (as simple as a state machine can be anyway). + /// To use it, you need an enum. For example : public enum CharacterConditions { Normal, ControlledMovement, Frozen, Paused, Dead } + /// Declare it like so : public StateMachine ConditionStateMachine; + /// Initialize it like that : ConditionStateMachine = new StateMachine(); + /// Then from anywhere, all you need to do is update its state when needed, like that for example : ConditionStateMachine.ChangeState(CharacterConditions.Dead); + /// The state machine will store for you its current and previous state, accessible at all times, and will also optionnally trigger events on enter/exit of these states. + /// You can go further by using a StateMachineProcessor class, to trigger more events (see the list below). + /// + public class MMStateMachine : MMIStateMachine where T : struct, IComparable, IConvertible, IFormattable + { + /// If you set TriggerEvents to true, the state machine will trigger events when entering and exiting a state. + /// Additionnally, if you also use a StateMachineProcessor, it'll trigger events for the current state on FixedUpdate, LateUpdate, but also + /// on Update (separated in EarlyUpdate, Update and EndOfUpdate, triggered in this order at Update() + /// To listen to these events, from any class, in its Start() method (or wherever you prefer), use MMEventManager.StartListening(gameObject.GetInstanceID().ToString()+"XXXEnter",OnXXXEnter); + /// where XXX is the name of the state you're listening to, and OnXXXEnter is the method you want to call when that event is triggered. + /// MMEventManager.StartListening(gameObject.GetInstanceID().ToString()+"CrouchingEarlyUpdate",OnCrouchingEarlyUpdate); for example will listen to the Early Update event of the Crouching state, and + /// will trigger the OnCrouchingEarlyUpdate() method. + public bool TriggerEvents { get; set; } + /// the name of the target gameobject + public GameObject Target; + /// the current character's movement state + public T CurrentState { get; protected set; } + /// the character's movement state before entering the current one + public T PreviousState { get; protected set; } + + /// + /// Creates a new StateMachine, with a targetName (used for events, usually use GetInstanceID()), and whether you want to use events with it or not + /// + /// Target name. + /// If set to true trigger events. + public MMStateMachine(GameObject target, bool triggerEvents) + { + this.Target = target; + this.TriggerEvents = triggerEvents; + } + + /// + /// Changes the current movement state to the one specified in the parameters, and triggers exit and enter events if needed + /// + /// New state. + public virtual void ChangeState(T newState) + { + // if the "new state" is the current one, we do nothing and exit + if (newState.Equals(CurrentState)) + { + return; + } + + + // if we've chosen to trigger events, we trigger our exit event + /*if (TriggerEvents) + { + MMEventManager.TriggerEvent(TargetName+CurrentState.ToString()+"Exit"); + }*/ + + // we store our previous character movement state + PreviousState = CurrentState; + CurrentState = newState; + + // uncomment this to debug state changes. + //MMDebug.DebugLogTime ("new state : " + newState); + + // if we've chosen to trigger events, we trigger our enter event + /*if (TriggerEvents) + { + MMEventManager.TriggerEvent(TargetName+newState.ToString()+"Enter"); + }*/ + + if (TriggerEvents) + { + MMEventManager.TriggerEvent (new MMStateChangeEvent (this)); + } + } + + /// + /// Returns the character to the state it was in before its current state + /// + public virtual void RestorePreviousState() + { + // if we've chosen to trigger events, we trigger our exit event + /*if (TriggerEvents) + { + MMEventManager.TriggerEvent(TargetName+CurrentState.ToString()+"Exit"); + }*/ + + // we restore our previous state + CurrentState = PreviousState; + + // if we've chosen to trigger events, we trigger our enter event + /*if (TriggerEvents) + { + MMEventManager.TriggerEvent(TargetName+CurrentState.ToString()+"Enter"); + }*/ + + if (TriggerEvents) + { + MMEventManager.TriggerEvent (new MMStateChangeEvent (this)); + } + } + } +} \ No newline at end of file diff --git a/Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs.meta b/Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs.meta new file mode 100644 index 0000000..7d0677b --- /dev/null +++ b/Assets/CorgiEngine/MMTools/StateMachine/MMStateMachine.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ab8af48f1aee1a64cb6d29e1d9b03029 +timeCreated: 1482073090 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: