Merge branch 'main' into audio-overhaul

This commit is contained in:
journaliciouz
2025-10-30 11:08:16 +01:00
18 changed files with 3219 additions and 3359 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5f6f72f272957b4439cbaf4e18106470
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -16,3 +16,5 @@ MonoBehaviour:
targetMinigameSceneName: DivingForPictures
description: Level loading for Quarry
mapSprite: {fileID: -3645797367086948227, guid: fea1a8662ef819746b8073c9ba0d9047, type: 3}
menuSprite: {fileID: 0}
minigameMenuSprite: {fileID: 6579828237621196356, guid: 7031dc4d177f92b4f970e104cdd6de51, type: 3}

View File

@@ -0,0 +1,550 @@
using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using UnityEditor.IMGUI.Controls;
namespace Editor.Tools
{
public class ComponentSearchReplaceWindow : EditorWindow
{
private Type selectedSearchType;
private Type selectedReplaceType;
private List<ComponentInfo> foundComponents = new List<ComponentInfo>();
private Vector2 scrollPos;
private int selectedComponentIndex = -1;
private string searchTypeName = "Select a Component...";
private string replaceTypeName = "Select a Component...";
private List<Type> allMonoBehaviourTypes = new List<Type>();
[MenuItem("Tools/Component Search & Replace")]
public static void ShowWindow()
{
var window = GetWindow<ComponentSearchReplaceWindow>("Component Search & Replace");
window.minSize = new Vector2(700, 500);
window.maxSize = new Vector2(1400, 1000);
window.position = new Rect(120, 120, 800, 600);
}
private void OnEnable()
{
CacheMonoBehaviourTypes();
EditorSceneManager.sceneOpened += (scene, mode) => ClearSearch();
SceneManager.activeSceneChanged += (oldScene, newScene) => ClearSearch();
}
private void OnDisable()
{
EditorSceneManager.sceneOpened -= (scene, mode) => ClearSearch();
SceneManager.activeSceneChanged -= (oldScene, newScene) => ClearSearch();
}
private void CacheMonoBehaviourTypes()
{
allMonoBehaviourTypes.Clear();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes()
.Where(t => (t.IsSubclassOf(typeof(MonoBehaviour)) || t.IsSubclassOf(typeof(Component)))
&& !t.IsAbstract
&& t != typeof(Transform)) // Exclude Transform as it can't be removed
.OrderBy(t => t.Name);
allMonoBehaviourTypes.AddRange(types);
}
catch (Exception)
{
// Some assemblies may throw exceptions when getting types
continue;
}
}
}
private void ClearSearch()
{
foundComponents.Clear();
selectedComponentIndex = -1;
Repaint();
}
private void OnGUI()
{
GUILayout.Space(10);
// Search Section
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Search for Component", EditorStyles.boldLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Component Type:", GUILayout.Width(120));
if (GUILayout.Button(searchTypeName, EditorStyles.popup, GUILayout.ExpandWidth(true)))
{
var dropdown = new ComponentTypeDropdown(
new AdvancedDropdownState(),
(type) =>
{
selectedSearchType = type;
searchTypeName = type != null ? type.Name : "Select a Component...";
ClearSearch();
Repaint();
});
dropdown.Show(GUILayoutUtility.GetLastRect());
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(5);
EditorGUI.BeginDisabledGroup(selectedSearchType == null);
if (GUILayout.Button("Search Scene", GUILayout.Height(30)))
{
SearchForComponents();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
// Results Section
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField($"Search Results ({foundComponents.Count} found)", EditorStyles.boldLabel);
GUILayout.Space(5);
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.ExpandHeight(true));
if (foundComponents.Count == 0)
{
EditorGUILayout.HelpBox("No components found. Select a component type and click 'Search Scene'.", MessageType.Info);
}
else
{
for (int i = 0; i < foundComponents.Count; i++)
{
var info = foundComponents[i];
if (info.component == null) continue;
bool isSelected = selectedComponentIndex == i;
Color originalColor = GUI.backgroundColor;
if (isSelected)
{
GUI.backgroundColor = new Color(0.3f, 0.6f, 1f, 0.5f);
}
EditorGUILayout.BeginVertical("box");
GUI.backgroundColor = originalColor;
EditorGUILayout.BeginHorizontal();
// Selection toggle
bool newSelected = GUILayout.Toggle(isSelected, "", GUILayout.Width(20));
if (newSelected != isSelected)
{
selectedComponentIndex = newSelected ? i : -1;
}
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField($"GameObject: {info.gameObject.name}", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"Path: {info.hierarchyPath}", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"Component: {info.component.GetType().Name}", EditorStyles.miniLabel);
EditorGUILayout.EndVertical();
if (GUILayout.Button("Ping", GUILayout.Width(80)))
{
Selection.activeObject = info.gameObject;
EditorGUIUtility.PingObject(info.gameObject);
SceneView.FrameLastActiveSceneView();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
GUILayout.Space(2);
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
// Replace Section
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Replace Component", EditorStyles.boldLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Replace With:", GUILayout.Width(120));
if (GUILayout.Button(replaceTypeName, EditorStyles.popup, GUILayout.ExpandWidth(true)))
{
var dropdown = new ComponentTypeDropdown(
new AdvancedDropdownState(),
(type) =>
{
selectedReplaceType = type;
replaceTypeName = type != null ? type.Name : "Select a Component...";
Repaint();
});
dropdown.Show(GUILayoutUtility.GetLastRect());
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(5);
bool canReplace = selectedComponentIndex >= 0 &&
selectedComponentIndex < foundComponents.Count &&
selectedReplaceType != null;
EditorGUI.BeginDisabledGroup(!canReplace);
if (!canReplace && selectedComponentIndex >= 0 && selectedReplaceType == null)
{
EditorGUILayout.HelpBox("Select a component type to replace with.", MessageType.Warning);
}
else if (!canReplace && selectedReplaceType != null)
{
EditorGUILayout.HelpBox("Select an object from the search results above.", MessageType.Warning);
}
if (GUILayout.Button("Replace Selected Component", GUILayout.Height(30)))
{
ReplaceComponent();
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
GUILayout.Space(5);
}
private void SearchForComponents()
{
if (selectedSearchType == null)
{
Debug.LogWarning("No component type selected for search.");
return;
}
foundComponents.Clear();
selectedComponentIndex = -1;
var allObjects = FindObjectsByType<GameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
foreach (var go in allObjects)
{
var component = go.GetComponent(selectedSearchType);
if (component != null)
{
foundComponents.Add(new ComponentInfo
{
gameObject = go,
component = component,
hierarchyPath = GetHierarchyPath(go)
});
}
}
foundComponents = foundComponents.OrderBy(c => c.hierarchyPath).ToList();
Debug.Log($"Found {foundComponents.Count} objects with component type '{selectedSearchType.Name}'");
Repaint();
}
private void ReplaceComponent()
{
if (selectedComponentIndex < 0 || selectedComponentIndex >= foundComponents.Count)
{
Debug.LogError("No component selected to replace.");
return;
}
if (selectedReplaceType == null)
{
Debug.LogError("No replacement component type selected.");
return;
}
var info = foundComponents[selectedComponentIndex];
var go = info.gameObject;
var oldComponent = info.component;
if (go == null || oldComponent == null)
{
Debug.LogError("Selected component or GameObject is null.");
foundComponents.RemoveAt(selectedComponentIndex);
selectedComponentIndex = -1;
Repaint();
return;
}
// Confirm the replacement
bool confirmed = EditorUtility.DisplayDialog(
"Replace Component",
$"Are you sure you want to replace '{selectedSearchType.Name}' with '{selectedReplaceType.Name}' on GameObject '{go.name}'?\n\n" +
"This action can be undone with Ctrl+Z.",
"Replace",
"Cancel"
);
if (!confirmed)
{
return;
}
// Record undo
Undo.RegisterCompleteObjectUndo(go, "Replace Component");
// Copy serialized data if possible (optional enhancement)
// This attempts to preserve common fields between components
var serializedOldComponent = new SerializedObject(oldComponent);
// Remove old component
Undo.DestroyObjectImmediate(oldComponent);
// Add new component
var newComponent = Undo.AddComponent(go, selectedReplaceType);
// Try to copy matching serialized properties
var serializedNewComponent = new SerializedObject(newComponent);
CopyMatchingSerializedProperties(serializedOldComponent, serializedNewComponent);
serializedNewComponent.ApplyModifiedProperties();
// Mark scene as dirty
EditorSceneManager.MarkSceneDirty(go.scene);
Debug.Log($"Replaced '{selectedSearchType.Name}' with '{selectedReplaceType.Name}' on '{go.name}'");
// Update the found components list
foundComponents[selectedComponentIndex] = new ComponentInfo
{
gameObject = go,
component = newComponent,
hierarchyPath = info.hierarchyPath
};
// Update search type if needed
if (selectedSearchType != selectedReplaceType)
{
foundComponents.RemoveAt(selectedComponentIndex);
selectedComponentIndex = -1;
}
Repaint();
}
private void CopyMatchingSerializedProperties(SerializedObject source, SerializedObject destination)
{
SerializedProperty iterator = source.GetIterator();
bool enterChildren = true;
while (iterator.NextVisible(enterChildren))
{
enterChildren = false;
// Skip script reference
if (iterator.propertyPath == "m_Script")
continue;
SerializedProperty destProp = destination.FindProperty(iterator.propertyPath);
if (destProp != null && destProp.propertyType == iterator.propertyType)
{
try
{
destination.CopyFromSerializedProperty(iterator);
}
catch
{
// Some properties may not be copyable
}
}
}
}
private string GetHierarchyPath(GameObject go)
{
string path = go.name;
Transform t = go.transform.parent;
while (t != null)
{
path = t.name + "/" + path;
t = t.parent;
}
return path;
}
private class ComponentInfo
{
public GameObject gameObject;
public Component component;
public string hierarchyPath;
}
}
// Advanced Dropdown for Component Type Selection (including Unity built-in components)
public class ComponentTypeDropdown : AdvancedDropdown
{
private Action<Type> onTypeSelected;
private static List<Type> cachedTypes;
public ComponentTypeDropdown(AdvancedDropdownState state, Action<Type> onTypeSelected)
: base(state)
{
this.onTypeSelected = onTypeSelected;
minimumSize = new Vector2(300, 400);
if (cachedTypes == null)
{
CacheTypes();
}
}
private static void CacheTypes()
{
cachedTypes = new List<Type>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes()
.Where(t => (t.IsSubclassOf(typeof(MonoBehaviour)) || t.IsSubclassOf(typeof(Component)))
&& !t.IsAbstract
&& t != typeof(Transform)) // Exclude Transform as it can't be removed
.OrderBy(t => t.Name);
cachedTypes.AddRange(types);
}
catch (Exception)
{
// Some assemblies may throw exceptions when getting types
}
}
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new AdvancedDropdownItem("Component Types");
// Add "None" option
var noneItem = new TypeDropdownItem("None", null);
root.AddChild(noneItem);
root.AddSeparator();
// Separate Unity built-in components from custom ones
var unityComponents = cachedTypes.Where(t =>
t.Namespace != null && t.Namespace.StartsWith("UnityEngine")).ToList();
var customComponents = cachedTypes.Except(unityComponents).ToList();
// Add Unity Components section
if (unityComponents.Any())
{
var unitySection = new AdvancedDropdownItem("Unity Components");
// Group Unity components by category (second part of namespace)
var unityGrouped = unityComponents.GroupBy(t =>
{
if (t.Namespace == "UnityEngine")
return "Core";
var parts = t.Namespace.Split('.');
return parts.Length > 1 ? parts[1] : "Other";
}).OrderBy(g => g.Key);
foreach (var group in unityGrouped)
{
if (group.Count() <= 5)
{
// Add directly if few items
foreach (var type in group.OrderBy(t => t.Name))
{
var typeItem = new TypeDropdownItem(type.Name, type);
unitySection.AddChild(typeItem);
}
}
else
{
// Create subcategory
var categoryItem = new AdvancedDropdownItem(group.Key);
foreach (var type in group.OrderBy(t => t.Name))
{
var typeItem = new TypeDropdownItem(type.Name, type);
categoryItem.AddChild(typeItem);
}
unitySection.AddChild(categoryItem);
}
}
root.AddChild(unitySection);
}
// Add Custom Components section
if (customComponents.Any())
{
root.AddSeparator();
// Group by namespace
var groupedTypes = customComponents.GroupBy(t =>
string.IsNullOrEmpty(t.Namespace) ? "(No Namespace)" : t.Namespace);
foreach (var group in groupedTypes.OrderBy(g => g.Key))
{
// For small namespaces or no namespace, add directly to root
if (group.Count() <= 3 || group.Key == "(No Namespace)")
{
foreach (var type in group)
{
var typeItem = new TypeDropdownItem(
group.Key == "(No Namespace)" ? type.Name : $"{group.Key}.{type.Name}",
type);
root.AddChild(typeItem);
}
}
else
{
// Create namespace folder
var namespaceItem = new AdvancedDropdownItem(group.Key);
foreach (var type in group)
{
var typeItem = new TypeDropdownItem(type.Name, type);
namespaceItem.AddChild(typeItem);
}
root.AddChild(namespaceItem);
}
}
}
return root;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
var typeItem = item as TypeDropdownItem;
if (typeItem != null)
{
onTypeSelected?.Invoke(typeItem.Type);
}
}
private class TypeDropdownItem : AdvancedDropdownItem
{
public Type Type { get; private set; }
public TypeDropdownItem(string name, Type type) : base(name)
{
Type = type;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 604b18a354fa44a49723ab9f9173762e
timeCreated: 1761816008

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -0,0 +1,195 @@
fileFormatVersion: 2
guid: c3d03e932743da340b8b2a2c290013f0
TextureImporter:
internalIDToNameTable:
- first:
213: -6037909929086895425
second: applehills_kort_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: applehills_kort_0
rect:
serializedVersion: 2
x: 8
y: 21
width: 498
height: 491
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: fbab8f2e8e8053ca0800000000000000
internalID: -6037909929086895425
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
applehills_kort_0: -6037909929086895425
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,195 @@
fileFormatVersion: 2
guid: 1bb06ae831042e24c915cd682fa2c6ab
TextureImporter:
internalIDToNameTable:
- first:
213: 3559306594824124945
second: power_off_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: power_off_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 384
height: 384
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 11a723f6d72356130800000000000000
internalID: 3559306594824124945
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
power_off_0: 3559306594824124945
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,195 @@
fileFormatVersion: 2
guid: 9d084ac2438d6f9448c61a62639581ba
TextureImporter:
internalIDToNameTable:
- first:
213: -1083306995931140661
second: refresh-arrow_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WindowsStoreApps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: refresh-arrow_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 464
height: 463
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: bcd24dcd60257f0f0800000000000000
internalID: -1083306995931140661
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
refresh-arrow_0: -1083306995931140661
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -136,6 +136,81 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &671326379795878693
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4498104668003212261}
- component: {fileID: 1384216111310773312}
- component: {fileID: 8822046367999630453}
m_Layer: 5
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4498104668003212261
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 671326379795878693}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 538506048417711235}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.2, y: 0.2}
m_AnchorMax: {x: 0.8, y: 0.8}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1384216111310773312
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 671326379795878693}
m_CullTransparentMesh: 1
--- !u!114 &8822046367999630453
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 671326379795878693}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -5941552768301239376, guid: 300528d856c15824989bade57a7e04e4, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &1519948061307491961
GameObject:
m_ObjectHideFlags: 0
@@ -969,13 +1044,14 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 4498104668003212261}
m_Father: {fileID: 5494371867874695937}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -100, y: 100}
m_SizeDelta: {x: 100, y: 100}
m_AnchoredPosition: {x: -56, y: 64}
m_SizeDelta: {x: 250, y: 250}
m_Pivot: {x: 1, y: 0}
--- !u!222 &1082932495279705792
CanvasRenderer:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -178,13 +178,49 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 1936850923066780126, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.x
- target: {fileID: 983415425082270079, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 983415425082270079, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 983415425082270079, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 983415425082270079, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1593702389427837575, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1804752920503555062, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1804752920503555062, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1804752920503555062, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1804752920503555062, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1936850923066780126, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.x
value: 341.5
objectReference: {fileID: 0}
- target: {fileID: 1936850923066780126, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.y
value: 0
value: 150
objectReference: {fileID: 0}
- target: {fileID: 2570593642998705886, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
@@ -248,19 +284,23 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 4841071245403604065, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4841071245403604065, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMin.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4841071245403604065, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
value: 75
objectReference: {fileID: 0}
- target: {fileID: 4841071245403604065, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
value: -75
objectReference: {fileID: 0}
- target: {fileID: 5003708969225410361, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0.0002746582
objectReference: {fileID: 0}
- target: {fileID: 5275008271445494810, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.x
@@ -270,6 +310,50 @@ PrefabInstance:
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5315106633118420968, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5315106633118420968, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5334678823793530471, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5334678823793530471, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5334678823793530471, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5334678823793530471, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5620538142629867345, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5620538142629867345, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5620538142629867345, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5620538142629867345, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5992029148489394405, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7086790971941574277, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_LocalPosition.x
value: -28.8398
@@ -332,19 +416,19 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 9220948749313234647, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMax.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 9220948749313234647, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchorMin.y
value: 0
value: 1
objectReference: {fileID: 0}
- target: {fileID: 9220948749313234647, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
value: 266.5
objectReference: {fileID: 0}
- target: {fileID: 9220948749313234647, guid: de2ed28e4200a4340a5af4086c98a0dc, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
value: -75
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []

View File

@@ -30,8 +30,13 @@ namespace Levels
public Sprite mapSprite;
/// <summary>
/// Icon to display for this level switch.
/// Icon to display in UI for this level switch.
/// </summary>
public Sprite menuSprite;
/// <summary>
/// Icon to display for minigame selection, attached to this level.
/// </summary>
public Sprite minigameMenuSprite;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -13,20 +14,33 @@ namespace Levels
public class LevelSwitchMenu : MonoBehaviour
{
[Header("UI References")]
public Image iconImage;
public TMP_Text levelNameText;
public Button confirmButton;
public Image mainLevelIconImage;
public Image minigameIconImage;
public TMP_Text mainLevelNameText;
public TMP_Text minigameLevelNameText;
public Button puzzleLevelConfirmButton;
public Button puzzleLevelRestartButton;
public Button cancelButton;
public Button minigameButton;
public GameObject padlockIcon;
public Button restartButton;
public Button minigameConfirmButton;
public GameObject popupConfirmMenu;
public Button popupConfirmButton;
public Button popupCancelButton;
public Image tintTargetImage;
public Color popupTintColor = new Color(0.5f, 0.5f, 0.5f, 1f); // grey by default
public Color disabledTintColor = new Color(0.5f, 0.5f, 0.5f, 1f); // grey by default
[Header("Minigame Lock")]
public Image padlockImage;
[Header("Scroll View")]
public ScrollRect scrollView;
public Button scrollToPuzzleLevelButton;
public Button scrollToMinigameButton;
public float scrollDuration = 1f;
private Color _originalTintColor;
private Color _originalMinigameIconColor;
private Action _onRestart;
private Coroutine _activeScrollCoroutine;
private Action _onLevelConfirm;
private Action _onMinigameConfirm;
@@ -43,35 +57,55 @@ namespace Levels
_onMinigameConfirm = onMinigameConfirm;
_onCancel = onCancel;
_onRestart = onRestart;
if(switchData != null)
{
if (iconImage)
if (mainLevelIconImage)
{
iconImage.sprite = switchData.menuSprite != null
mainLevelIconImage.sprite = switchData.menuSprite != null
? switchData.menuSprite
: switchData.mapSprite;
}
if (levelNameText) levelNameText.text = switchData?.targetLevelSceneName ?? "";
if (minigameIconImage)
{
minigameIconImage.sprite = switchData.minigameMenuSprite;
}
if (mainLevelNameText) mainLevelNameText.text = switchData?.targetLevelSceneName ?? "";
if (minigameLevelNameText) minigameLevelNameText.text = switchData.targetMinigameSceneName ?? "";
}
else
{
Logging.Warning("[LevelSwitchMenu] No level data is assigned!");
}
if (confirmButton) confirmButton.onClick.AddListener(OnConfirmClicked);
// Setup button listeners
if (puzzleLevelConfirmButton) puzzleLevelConfirmButton.onClick.AddListener(OnPuzzleLevelConfirmClicked);
if (puzzleLevelRestartButton) puzzleLevelRestartButton.onClick.AddListener(OnRestartClicked);
if (cancelButton) cancelButton.onClick.AddListener(OnCancelClicked);
if (minigameButton) minigameButton.onClick.AddListener(OnMinigameClicked);
if (restartButton) restartButton.onClick.AddListener(OnRestartClicked);
if (popupConfirmMenu) popupConfirmMenu.SetActive(false);
if (tintTargetImage) _originalTintColor = tintTargetImage.color;
if (minigameConfirmButton) minigameConfirmButton.onClick.AddListener(OnMinigameConfirmClicked);
if (scrollToPuzzleLevelButton) scrollToPuzzleLevelButton.onClick.AddListener(OnScrollToPuzzleLevelClicked);
if (scrollToMinigameButton) scrollToMinigameButton.onClick.AddListener(OnScrollToMinigameClicked);
if (popupConfirmButton) popupConfirmButton.onClick.AddListener(OnPopupConfirmClicked);
if (popupCancelButton) popupCancelButton.onClick.AddListener(OnPopupCancelClicked);
if (popupConfirmMenu) popupConfirmMenu.SetActive(false);
if (tintTargetImage) _originalTintColor = tintTargetImage.color;
if (minigameIconImage) _originalMinigameIconColor = minigameIconImage.color;
// Initialize scroll view to start at left (puzzle level view)
if (scrollView) scrollView.horizontalNormalizedPosition = 0f;
// Initialize scroll button visibility
if (scrollToMinigameButton) scrollToMinigameButton.gameObject.SetActive(true);
if (scrollToPuzzleLevelButton) scrollToPuzzleLevelButton.gameObject.SetActive(false);
// --- Minigame unlock state logic ---
if (SaveLoadManager.Instance != null)
{
if (SaveLoadManager.Instance.IsSaveDataLoaded)
{
ApplyMinigameUnlockStateIfAvailable();
ApplyMinigameUnlockState();
}
else
{
@@ -82,25 +116,34 @@ namespace Levels
private void OnDestroy()
{
if (confirmButton) confirmButton.onClick.RemoveListener(OnConfirmClicked);
if (puzzleLevelConfirmButton) puzzleLevelConfirmButton.onClick.RemoveListener(OnPuzzleLevelConfirmClicked);
if (puzzleLevelRestartButton) puzzleLevelRestartButton.onClick.RemoveListener(OnRestartClicked);
if (cancelButton) cancelButton.onClick.RemoveListener(OnCancelClicked);
if (minigameButton) minigameButton.onClick.RemoveListener(OnMinigameClicked);
if (restartButton) restartButton.onClick.RemoveListener(OnRestartClicked);
if (minigameConfirmButton) minigameConfirmButton.onClick.RemoveListener(OnMinigameConfirmClicked);
if (scrollToPuzzleLevelButton) scrollToPuzzleLevelButton.onClick.RemoveListener(OnScrollToPuzzleLevelClicked);
if (scrollToMinigameButton) scrollToMinigameButton.onClick.RemoveListener(OnScrollToMinigameClicked);
if (popupConfirmButton) popupConfirmButton.onClick.RemoveListener(OnPopupConfirmClicked);
if (popupCancelButton) popupCancelButton.onClick.RemoveListener(OnPopupCancelClicked);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
_activeScrollCoroutine = null;
}
if (SaveLoadManager.Instance != null)
{
SaveLoadManager.Instance.OnLoadCompleted -= OnSaveDataLoadedHandler;
}
}
private void OnConfirmClicked()
private void OnPuzzleLevelConfirmClicked()
{
_onLevelConfirm?.Invoke();
Destroy(gameObject);
}
private void OnMinigameClicked()
private void OnMinigameConfirmClicked()
{
_onMinigameConfirm?.Invoke();
Destroy(gameObject);
@@ -115,7 +158,7 @@ namespace Levels
private void OnRestartClicked()
{
if (popupConfirmMenu) popupConfirmMenu.SetActive(true);
if (tintTargetImage) tintTargetImage.color = popupTintColor;
if (tintTargetImage) tintTargetImage.color = disabledTintColor;
}
private void OnPopupCancelClicked()
@@ -131,20 +174,110 @@ namespace Levels
if (tintTargetImage) tintTargetImage.color = _originalTintColor;
}
private void ApplyMinigameUnlockStateIfAvailable()
private void OnScrollToMinigameClicked()
{
if (minigameButton == null || padlockIcon == null || _switchData == null)
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
}
_activeScrollCoroutine = StartCoroutine(ScrollToMinigameCoroutine());
}
private void OnScrollToPuzzleLevelClicked()
{
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
}
_activeScrollCoroutine = StartCoroutine(ScrollToPuzzleLevelCoroutine());
}
private IEnumerator ScrollToMinigameCoroutine()
{
// Hide the scroll to minigame button
if (scrollToMinigameButton) scrollToMinigameButton.gameObject.SetActive(false);
// Scroll to the right (normalized position 0.95)
float elapsed = 0f;
float startPos = scrollView.horizontalNormalizedPosition;
float targetPos = 0.9f;
while (elapsed < scrollDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / scrollDuration);
// Use SmoothStep for a smoother animation
float smoothT = Mathf.SmoothStep(0f, 1f, t);
scrollView.horizontalNormalizedPosition = Mathf.Lerp(startPos, targetPos, smoothT);
yield return null;
}
// Ensure we're at the final position
scrollView.horizontalNormalizedPosition = targetPos;
// Show the scroll to puzzle level button
if (scrollToPuzzleLevelButton) scrollToPuzzleLevelButton.gameObject.SetActive(true);
_activeScrollCoroutine = null;
}
private IEnumerator ScrollToPuzzleLevelCoroutine()
{
// Hide the scroll to puzzle level button
if (scrollToPuzzleLevelButton) scrollToPuzzleLevelButton.gameObject.SetActive(false);
// Scroll to the left (normalized position 0)
float elapsed = 0f;
float startPos = scrollView.horizontalNormalizedPosition;
float targetPos = 0f;
while (elapsed < scrollDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / scrollDuration);
// Use SmoothStep for a smoother animation
float smoothT = Mathf.SmoothStep(0f, 1f, t);
scrollView.horizontalNormalizedPosition = Mathf.Lerp(startPos, targetPos, smoothT);
yield return null;
}
// Ensure we're at the final position
scrollView.horizontalNormalizedPosition = targetPos;
// Show the scroll to minigame button
if (scrollToMinigameButton) scrollToMinigameButton.gameObject.SetActive(true);
_activeScrollCoroutine = null;
}
private void ApplyMinigameUnlockState()
{
if (_switchData == null)
return;
var data = SaveLoadManager.Instance?.currentSaveData;
string minigameName = _switchData.targetMinigameSceneName;
bool unlocked = data?.unlockedMinigames != null && !string.IsNullOrEmpty(minigameName) && data.unlockedMinigames.Contains(minigameName);
minigameButton.interactable = unlocked;
padlockIcon.SetActive(!unlocked);
// Show/hide padlock
if (padlockImage) padlockImage.gameObject.SetActive(!unlocked);
// Tint minigame icon if locked
if (minigameIconImage)
{
minigameIconImage.color = unlocked ? _originalMinigameIconColor : disabledTintColor;
}
// Enable/disable minigame confirm button
if (minigameConfirmButton)
{
minigameConfirmButton.interactable = unlocked;
}
}
private void OnSaveDataLoadedHandler(string slot)
{
ApplyMinigameUnlockStateIfAvailable();
ApplyMinigameUnlockState();
if (SaveLoadManager.Instance != null)
{
SaveLoadManager.Instance.OnLoadCompleted -= OnSaveDataLoadedHandler;

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -6,7 +7,7 @@ using UnityEngine.UI;
namespace Levels
{
/// <summary>
/// UI overlay for confirming a level switch. Displays level info and handles confirm/cancel actions, and supports leaderboard view.
/// UI overlay for confirming a level switch. Displays level info and handles confirm/cancel actions, and supports scrolling between level info and leaderboard.
/// </summary>
public class MinigameSwitchMenu : MonoBehaviour
{
@@ -15,14 +16,15 @@ namespace Levels
public TMP_Text levelNameText;
public Button confirmButton;
public Button cancelButton;
public Button leaderboardButton;
public GameObject levelInfoContainer;
public GameObject leaderboardContainer;
public Button leaderboardDismissButton;
public Button toLeaderboardButton;
public Button toLevelSelectButton;
public ScrollRect scrollView;
public float scrollDuration = 1f;
private Action _onLevelConfirm;
private Action _onCancel;
private LevelSwitchData _switchData;
private Coroutine _activeScrollCoroutine;
/// <summary>
/// Initialize the menu with data and callbacks.
@@ -36,18 +38,26 @@ namespace Levels
if (levelNameText) levelNameText.text = switchData?.targetLevelSceneName ?? "";
if (confirmButton) confirmButton.onClick.AddListener(OnConfirmClicked);
if (cancelButton) cancelButton.onClick.AddListener(OnCancelClicked);
if (leaderboardButton) leaderboardButton.onClick.AddListener(OnLeaderboardClicked);
if (leaderboardDismissButton) leaderboardDismissButton.onClick.AddListener(OnLeaderboardDismissClicked);
if (levelInfoContainer) levelInfoContainer.SetActive(true);
if (leaderboardContainer) leaderboardContainer.SetActive(false);
if (toLeaderboardButton) toLeaderboardButton.onClick.AddListener(OnToLeaderboardClicked);
if (toLevelSelectButton) toLevelSelectButton.onClick.AddListener(OnToLevelSelectClicked);
// Show only the toLeaderboardButton at start
if (toLeaderboardButton) toLeaderboardButton.gameObject.SetActive(true);
if (toLevelSelectButton) toLevelSelectButton.gameObject.SetActive(false);
// Initialize scroll view to start at left (level info view)
if (scrollView) scrollView.horizontalNormalizedPosition = 0f;
}
private void OnDestroy()
{
if (confirmButton) confirmButton.onClick.RemoveListener(OnConfirmClicked);
if (cancelButton) cancelButton.onClick.RemoveListener(OnCancelClicked);
if (leaderboardButton) leaderboardButton.onClick.RemoveListener(OnLeaderboardClicked);
if (leaderboardDismissButton) leaderboardDismissButton.onClick.RemoveListener(OnLeaderboardDismissClicked);
if (toLeaderboardButton) toLeaderboardButton.onClick.RemoveListener(OnToLeaderboardClicked);
if (toLevelSelectButton) toLevelSelectButton.onClick.RemoveListener(OnToLevelSelectClicked);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
_activeScrollCoroutine = null;
}
}
private void OnConfirmClicked()
@@ -62,16 +72,60 @@ namespace Levels
Destroy(gameObject);
}
private void OnLeaderboardClicked()
private void OnToLeaderboardClicked()
{
if (levelInfoContainer) levelInfoContainer.SetActive(false);
if (leaderboardContainer) leaderboardContainer.SetActive(true);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
}
_activeScrollCoroutine = StartCoroutine(ScrollToLeaderboardCoroutine());
}
private void OnLeaderboardDismissClicked()
private void OnToLevelSelectClicked()
{
if (levelInfoContainer) levelInfoContainer.SetActive(true);
if (leaderboardContainer) leaderboardContainer.SetActive(false);
if (_activeScrollCoroutine != null)
{
StopCoroutine(_activeScrollCoroutine);
}
_activeScrollCoroutine = StartCoroutine(ScrollToLevelSelectCoroutine());
}
private IEnumerator ScrollToLeaderboardCoroutine()
{
if (toLeaderboardButton) toLeaderboardButton.gameObject.SetActive(false);
float elapsed = 0f;
float startPos = scrollView.horizontalNormalizedPosition;
float targetPos = 0.95f;
while (elapsed < scrollDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / scrollDuration);
float smoothT = Mathf.SmoothStep(0f, 1f, t);
scrollView.horizontalNormalizedPosition = Mathf.Lerp(startPos, targetPos, smoothT);
yield return null;
}
scrollView.horizontalNormalizedPosition = targetPos;
if (toLevelSelectButton) toLevelSelectButton.gameObject.SetActive(true);
_activeScrollCoroutine = null;
}
private IEnumerator ScrollToLevelSelectCoroutine()
{
if (toLevelSelectButton) toLevelSelectButton.gameObject.SetActive(false);
float elapsed = 0f;
float startPos = scrollView.horizontalNormalizedPosition;
float targetPos = 0f;
while (elapsed < scrollDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / scrollDuration);
float smoothT = Mathf.SmoothStep(0f, 1f, t);
scrollView.horizontalNormalizedPosition = Mathf.Lerp(startPos, targetPos, smoothT);
yield return null;
}
scrollView.horizontalNormalizedPosition = targetPos;
if (toLeaderboardButton) toLeaderboardButton.gameObject.SetActive(true);
_activeScrollCoroutine = null;
}
}
}