stash work

This commit is contained in:
Michal Pikulski
2025-11-26 17:11:02 +01:00
committed by Michal Pikulski
parent 8d410b42d3
commit 5bab6d9596
38 changed files with 3634 additions and 308 deletions

View File

@@ -1,7 +1,6 @@
using Core;
using Minigames.StatueDressup.Utils;
using UnityEngine;
using UnityEngine;
using UnityEngine.UI;
using Utils;
namespace Minigames.StatueDressup.Controllers
{
@@ -24,7 +23,7 @@ namespace Minigames.StatueDressup.Controllers
captureButton.onClick.AddListener(OnCaptureClicked);
}
Debug.Log($"[PhotoCaptureTest] Ready. Photo save path: {StatuePhotoManager.GetPhotoDirectory()}");
Debug.Log($"[PhotoCaptureTest] Ready. Photo save path: {PhotoManager.GetCaptureDirectory(CaptureType.StatueMinigame)}");
}
private void OnCaptureClicked()
@@ -41,47 +40,29 @@ namespace Minigames.StatueDressup.Controllers
private System.Collections.IEnumerator CaptureCoroutine()
{
// Hide UI
foreach (var obj in hideTheseObjects)
if (obj != null) obj.SetActive(false);
yield return new WaitForEndOfFrame();
// Capture
bool done = false;
Texture2D photo = null;
StatuePhotoManager.CaptureAreaPhoto(captureArea, (texture) => {
photo = texture;
done = true;
});
yield return new WaitUntil(() => done);
// Restore UI
foreach (var obj in hideTheseObjects)
if (obj != null) obj.SetActive(true);
// Save
if (photo != null)
{
string photoId = StatuePhotoManager.SavePhoto(photo, 0);
if (!string.IsNullOrEmpty(photoId))
// Use new PhotoManager plug-and-play coroutine
yield return PhotoManager.CaptureAndSaveCoroutine(
CaptureType.StatueMinigame,
captureArea,
hideTheseObjects,
onSuccess: (photoId) =>
{
string path = $"{StatuePhotoManager.GetPhotoDirectory()}/{photoId}.png";
string path = $"{PhotoManager.GetCaptureDirectory(CaptureType.StatueMinigame)}/{photoId}.png";
Debug.Log($"[PhotoCaptureTest] ✅ SUCCESS! Photo saved: {path}");
Debug.Log($"[PhotoCaptureTest] Photo size: {photo.width}x{photo.height}");
}
else
// Load back to verify
Texture2D loadedPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, photoId);
if (loadedPhoto != null)
{
Debug.Log($"[PhotoCaptureTest] Photo size: {loadedPhoto.width}x{loadedPhoto.height}");
}
},
onFailure: (error) =>
{
Debug.LogError("[PhotoCaptureTest] ❌ Failed to save photo");
}
}
else
{
Debug.LogError("[PhotoCaptureTest] ❌ Failed to capture photo");
}
Debug.LogError($"[PhotoCaptureTest] ❌ Failed: {error}");
},
metadata: 0
);
}
private void OnDestroy()

View File

@@ -0,0 +1,163 @@
using Core;
using UnityEngine;
using UnityEngine.EventSystems;
using Utils;
namespace Minigames.StatueDressup.Controllers
{
/// <summary>
/// Manages enlarged photo preview display.
/// Handles backdrop, preview spawning, and click-to-dismiss.
/// Similar to CardEnlargeController but simpler (no state machine).
/// </summary>
public class PhotoEnlargeController
{
private readonly GameObject _backdrop;
private readonly Transform _enlargedContainer;
private readonly float _animationDuration;
private GameObject _currentEnlargedPreview;
private PhotoGridItem _currentSourceItem;
/// <summary>
/// Constructor
/// </summary>
public PhotoEnlargeController(GameObject backdrop, Transform enlargedContainer, float animationDuration = 0.3f)
{
_backdrop = backdrop;
_enlargedContainer = enlargedContainer;
_animationDuration = animationDuration;
}
/// <summary>
/// Check if a photo is currently enlarged
/// </summary>
public bool IsPhotoEnlarged => _currentEnlargedPreview != null;
/// <summary>
/// Enlarge a photo from a grid item
/// </summary>
public void EnlargePhoto(PhotoGridItem sourceItem, GameObject previewPrefab, Texture2D photoTexture, float enlargedScale)
{
if (sourceItem == null || previewPrefab == null || photoTexture == null)
{
Logging.Error("[PhotoEnlargeController] Invalid parameters for EnlargePhoto");
return;
}
// Don't allow multiple enlargements
if (_currentEnlargedPreview != null)
{
Logging.Warning("[PhotoEnlargeController] Photo already enlarged");
return;
}
_currentSourceItem = sourceItem;
// Show backdrop
if (_backdrop != null)
{
_backdrop.SetActive(true);
}
// Spawn preview clone
_currentEnlargedPreview = Object.Instantiate(previewPrefab, _enlargedContainer);
_currentEnlargedPreview.transform.SetAsLastSibling();
// Position at source item's world position
_currentEnlargedPreview.transform.position = sourceItem.transform.position;
_currentEnlargedPreview.transform.localScale = sourceItem.transform.localScale;
// Set photo texture on preview
var previewImage = _currentEnlargedPreview.GetComponent<UnityEngine.UI.Image>();
if (previewImage != null)
{
// Create sprite from texture
Sprite photoSprite = Sprite.Create(
photoTexture,
new Rect(0, 0, photoTexture.width, photoTexture.height),
new Vector2(0.5f, 0.5f)
);
previewImage.sprite = photoSprite;
}
// Add click handler to preview
var clickHandler = _currentEnlargedPreview.GetComponent<EventTrigger>();
if (clickHandler == null)
{
clickHandler = _currentEnlargedPreview.AddComponent<EventTrigger>();
}
var pointerClickEntry = new EventTrigger.Entry { eventID = EventTriggerType.PointerClick };
pointerClickEntry.callback.AddListener((_) => { ShrinkPhoto(); });
clickHandler.triggers.Add(pointerClickEntry);
// Animate to center and scale up
TweenAnimationUtility.AnimateLocalPosition(_currentEnlargedPreview.transform, Vector3.zero, _animationDuration);
TweenAnimationUtility.AnimateScale(_currentEnlargedPreview.transform, Vector3.one * enlargedScale, _animationDuration);
// Play audio feedback
AudioManager.Instance.LoadAndPlayUIAudio("card_albumdrop_deep", false);
Logging.Debug("[PhotoEnlargeController] Photo enlarged");
}
/// <summary>
/// Shrink the currently enlarged photo back to grid
/// </summary>
public void ShrinkPhoto()
{
if (_currentEnlargedPreview == null || _currentSourceItem == null)
{
Logging.Warning("[PhotoEnlargeController] No photo to shrink");
return;
}
// Hide backdrop
if (_backdrop != null)
{
_backdrop.SetActive(false);
}
// Get target position from source item
Vector3 targetWorldPos = _currentSourceItem.transform.position;
Vector3 targetScale = _currentSourceItem.transform.localScale;
GameObject previewToDestroy = _currentEnlargedPreview;
// Animate back to source position
Pixelplacement.Tween.Position(previewToDestroy.transform, targetWorldPos, _animationDuration, 0f, Pixelplacement.Tween.EaseInOut);
TweenAnimationUtility.AnimateScale(previewToDestroy.transform, targetScale, _animationDuration, () =>
{
// Destroy preview after animation
Object.Destroy(previewToDestroy);
});
// Clear references
_currentEnlargedPreview = null;
_currentSourceItem = null;
Logging.Debug("[PhotoEnlargeController] Photo shrunk");
}
/// <summary>
/// Cleanup - call when gallery is closed
/// </summary>
public void Cleanup()
{
if (_currentEnlargedPreview != null)
{
Object.Destroy(_currentEnlargedPreview);
_currentEnlargedPreview = null;
}
if (_backdrop != null)
{
_backdrop.SetActive(false);
}
_currentSourceItem = null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8ba20462f9a647ee90389c7480147d2e
timeCreated: 1764151481

View File

@@ -64,14 +64,14 @@ namespace Minigames.StatueDressup.Controllers
}
/// <summary>
/// Handle click to show enlarged view
/// Handle click to enlarge/shrink photo
/// </summary>
public void OnPointerClick(PointerEventData eventData)
{
if (_galleryController != null && !string.IsNullOrEmpty(_photoId))
{
Logging.Debug($"[PhotoGridItem] Clicked: {_photoId}");
_galleryController.ShowEnlargedView(_photoId);
_galleryController.OnGridItemClicked(this, _photoId);
}
}
}

View File

@@ -1,9 +1,12 @@
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
using Minigames.StatueDressup.Data;
using Minigames.StatueDressup.DragDrop;
using UI.Core;
using UnityEngine;
using UnityEngine.UI;
using Utils;
namespace Minigames.StatueDressup.Controllers
{
@@ -19,17 +22,24 @@ namespace Minigames.StatueDressup.Controllers
[SerializeField] private DecorationMenuController menuController;
[SerializeField] private Button takePhotoButton;
[SerializeField] private GameObject statue;
[SerializeField] private DecorationDraggableInstance draggablePrefab; // Prefab for spawning decorations
[Header("UI Pages")]
[SerializeField] private UI.PlayAreaPage playAreaPage;
[SerializeField] private UI.PhotoGalleryPage photoGalleryPage;
[SerializeField] private Button openGalleryButton;
[Header("UI Elements to Hide for Photo")]
[SerializeField] private GameObject[] uiElementsToHideForPhoto;
[Header("Photo Settings")]
[SerializeField] private RectTransform photoArea; // Area to capture
[SerializeField] private string photoSaveKey = "MrCementStatuePhoto";
private List<DecorationDraggableInstance> _placedDecorations = new List<DecorationDraggableInstance>();
private bool _minigameCompleted;
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
private Dictionary<string, DecorationData> _decorationDataDict;
private UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<System.Collections.Generic.IList<DecorationData>> _decorationDataHandle;
// Public property for menu controller
public Transform StatueParent => statueParent;
@@ -48,19 +58,36 @@ namespace Minigames.StatueDressup.Controllers
/// <summary>
/// Main initialization after all managers are ready
/// </summary>
internal override void OnManagedStart()
internal override async void OnManagedStart()
{
base.OnManagedStart();
Logging.Debug("[StatueDecorationController] Initializing minigame");
// Load all DecorationData via Addressables first
await LoadDecorationDataAsync();
// TODO: If ever picture gallery
// Setup UI pages (DISABLED - kept for future gallery integration)
// if (playAreaPage != null && UIPageController.Instance != null)
// {
// UIPageController.Instance.PushPage(playAreaPage);
// Logging.Debug("[StatueDecorationController] Play area page registered");
// }
// Setup photo button
if (takePhotoButton != null)
{
// TODO: Remove comment when ready
// takePhotoButton.onClick.AddListener(OnTakePhoto);
takePhotoButton.onClick.AddListener(OnTakePhoto);
}
// TODO: If ever picture gallery
// Setup gallery button (DISABLED - kept for future use)
// if (openGalleryButton != null)
// {
// openGalleryButton.onClick.AddListener(OnOpenGallery);
// }
// Subscribe to menu controller for tracking placed items
// Items will manage their own placement via overlap detection
if (menuController != null)
@@ -73,6 +100,49 @@ namespace Minigames.StatueDressup.Controllers
LoadStatueState();
}
/// <summary>
/// Load all DecorationData assets via Addressables and build lookup dictionary
/// </summary>
private async System.Threading.Tasks.Task LoadDecorationDataAsync()
{
string label = _settings?.DecorationDataLabel;
if (string.IsNullOrEmpty(label))
{
Logging.Error("[StatueDecorationController] Decoration data label not set in settings!");
return;
}
Logging.Debug($"[StatueDecorationController] Loading DecorationData with label '{label}'...");
// Use utility to load all DecorationData and create dictionary by ID
var result = await AddressablesUtility.LoadAssetsByLabelAsync<DecorationData, string>(
label,
data => data.DecorationId
);
_decorationDataDict = result.dictionary;
_decorationDataHandle = result.handle;
Logging.Debug($"[StatueDecorationController] Loaded {_decorationDataDict.Count} DecorationData assets");
}
/// <summary>
/// Open photo gallery
/// </summary>
private void OnOpenGallery()
{
if (photoGalleryPage != null && UIPageController.Instance != null)
{
UIPageController.Instance.PushPage(photoGalleryPage);
Logging.Debug("[StatueDecorationController] Opening photo gallery");
}
else
{
Logging.Warning("[StatueDecorationController] Photo gallery page not assigned");
}
}
/// <summary>
/// Register a decoration as placed on statue
/// </summary>
@@ -116,76 +186,66 @@ namespace Minigames.StatueDressup.Controllers
Logging.Debug("[StatueDecorationController] Taking photo of statue");
// Hide UI elements
HideUIForPhoto(true);
// Wait a frame for UI to hide, then capture
// CapturePhotoCoroutine handles all UI hiding/showing
StartCoroutine(CapturePhotoCoroutine());
}
/// <summary>
/// Capture photo after UI is hidden
/// Capture photo and save decoration metadata
/// </summary>
private System.Collections.IEnumerator CapturePhotoCoroutine()
{
yield return new WaitForEndOfFrame();
int decorationCount = _placedDecorations.Count;
bool captureSuccess = false;
string savedPhotoId = null;
// Capture using Screenshot Helper via StatuePhotoManager
bool captureComplete = false;
Texture2D capturedPhoto = null;
Utils.StatuePhotoManager.CaptureAreaPhoto(
// Capture photo
yield return PhotoManager.CaptureAndSaveCoroutine(
CaptureType.StatueMinigame,
photoArea,
(Texture2D texture) =>
{
capturedPhoto = texture;
captureComplete = true;
},
Camera.main
);
// Wait for capture to complete
yield return new WaitUntil(() => captureComplete);
if (capturedPhoto != null)
{
// Save photo with StatuePhotoManager
int decorationCount = _placedDecorations.Count;
string photoId = Utils.StatuePhotoManager.SavePhoto(capturedPhoto, decorationCount);
if (!string.IsNullOrEmpty(photoId))
uiElementsToHideForPhoto,
onSuccess: (photoId) =>
{
Logging.Debug($"[StatueDecorationController] Photo saved: {photoId}");
// Award cards
AwardCards();
// Update town icon
UpdateTownIcon(capturedPhoto);
// Show completion feedback
ShowCompletionFeedback();
_minigameCompleted = true;
}
else
savedPhotoId = photoId;
captureSuccess = true;
},
onFailure: (error) =>
{
Logging.Error("[StatueDecorationController] Failed to save photo!");
Logging.Error($"[StatueDecorationController] Photo capture failed: {error}");
DebugUIMessage.Show("Failed to save photo!", Color.red);
}
}
else
captureSuccess = false;
},
metadata: decorationCount
);
// If photo failed, abort
if (!captureSuccess)
{
Logging.Error("[StatueDecorationController] Failed to capture photo!");
DebugUIMessage.Show("Failed to capture photo!", Color.red);
yield break;
}
// Restore UI
HideUIForPhoto(false);
// Save decoration metadata
SaveDecorationMetadata(savedPhotoId);
// Load the saved photo for town icon update
Texture2D savedPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, savedPhotoId);
// Award cards
AwardCards();
// Update town icon
if (savedPhoto != null)
{
UpdateTownIcon(savedPhoto);
}
// Show completion feedback
ShowCompletionFeedback();
_minigameCompleted = true;
}
/// <summary>
/// Award Blokkemon cards to player
/// </summary>
@@ -219,17 +279,60 @@ namespace Minigames.StatueDressup.Controllers
}
/// <summary>
/// Hide/show UI elements for photo
/// Save decoration metadata for reconstruction
/// </summary>
private void HideUIForPhoto(bool hide)
private void SaveDecorationMetadata(string photoId)
{
foreach (var element in uiElementsToHideForPhoto)
// Determine coordinate system type
bool isUIRectTransform = statue != null && statue.GetComponent<RectTransform>() != null;
// Get statue size for coordinate conversion
Vector2 statueSize = Vector2.one;
if (isUIRectTransform && statue != null)
{
if (element != null)
RectTransform statueRect = statue.GetComponent<RectTransform>();
statueSize = statueRect.rect.size;
}
else if (statue != null)
{
SpriteRenderer statueSprite = statue.GetComponent<SpriteRenderer>();
if (statueSprite != null && statueSprite.sprite != null)
{
element.SetActive(!hide);
statueSize = statueSprite.sprite.bounds.size;
}
}
StatueDecorationData data = new StatueDecorationData
{
photoId = photoId,
timestamp = System.DateTime.Now.ToString("o"),
coordinateSystem = isUIRectTransform ? CoordinateSystemType.UIRectTransform : CoordinateSystemType.WorldSpace,
sourceStatueSize = statueSize
};
// Collect all decoration placements
foreach (var decoration in _placedDecorations)
{
if (decoration == null || decoration.Data == null) continue;
SpriteRenderer spriteRenderer = decoration.GetComponent<SpriteRenderer>();
DecorationPlacement placement = new DecorationPlacement
{
decorationId = decoration.Data.DecorationId,
localPosition = decoration.transform.localPosition,
localScale = decoration.transform.localScale,
rotation = decoration.transform.eulerAngles.z,
sortingOrder = spriteRenderer != null ? spriteRenderer.sortingOrder : 0
};
data.placements.Add(placement);
}
// Save metadata using PhotoManager
PhotoManager.SaveDecorationMetadata(CaptureType.StatueMinigame, photoId, data);
Logging.Debug($"[StatueDecorationController] Saved decoration metadata: {data.placements.Count} decorations");
}
/// <summary>
@@ -252,7 +355,7 @@ namespace Minigames.StatueDressup.Controllers
}
/// <summary>
/// Load saved statue decoration state
/// Load saved statue decoration state from metadata
/// </summary>
private void LoadStatueState()
{
@@ -263,10 +366,83 @@ namespace Minigames.StatueDressup.Controllers
return;
}
// TODO: Implement load system
// Restore decorations from saved state
// Check if DecorationData is loaded
if (_decorationDataDict == null || _decorationDataDict.Count == 0)
{
Logging.Warning("[StatueDecorationController] DecorationData not loaded yet. Cannot restore state.");
return;
}
Logging.Debug($"[StatueDecorationController] State loaded from {_settings.StateSaveKey} (TODO: implement persistence)");
// Load latest decoration metadata
StatueDecorationData data = PhotoManager.LoadLatestDecorationMetadata<StatueDecorationData>(CaptureType.StatueMinigame);
if (data == null || data.placements == null || data.placements.Count == 0)
{
Logging.Debug("[StatueDecorationController] No saved state found");
return;
}
Logging.Debug($"[StatueDecorationController] Restoring {data.placements.Count} decorations from saved state");
// Spawn each decoration
int successCount = 0;
foreach (var placement in data.placements)
{
if (SpawnSavedDecoration(placement))
{
successCount++;
}
}
Logging.Debug($"[StatueDecorationController] Successfully restored {successCount}/{data.placements.Count} decorations");
}
/// <summary>
/// Spawn a saved decoration from placement data
/// </summary>
private bool SpawnSavedDecoration(DecorationPlacement placement)
{
// Look up DecorationData from dictionary
if (!_decorationDataDict.TryGetValue(placement.decorationId, out DecorationData decorationData))
{
Logging.Warning($"[StatueDecorationController] DecorationData not found for ID: {placement.decorationId}");
return false;
}
if (draggablePrefab == null || statueParent == null)
{
Logging.Error("[StatueDecorationController] Missing draggable prefab or statue parent");
return false;
}
// Spawn decoration instance
DecorationDraggableInstance instance = Instantiate(draggablePrefab, statueParent);
// Initialize in "placed" state (skip drag logic)
instance.InitializeAsPlaced(
decorationData,
this,
_settings
);
// Apply saved transform
instance.transform.localPosition = placement.localPosition;
instance.transform.localScale = placement.localScale;
instance.transform.localEulerAngles = new Vector3(0, 0, placement.rotation);
// Set sorting order if has SpriteRenderer
SpriteRenderer spriteRenderer = instance.GetComponent<SpriteRenderer>();
if (spriteRenderer != null)
{
spriteRenderer.sortingOrder = placement.sortingOrder;
}
// Register with controller
RegisterDecoration(instance);
Logging.Debug($"[StatueDecorationController] Restored decoration: {placement.decorationId} at {placement.localPosition}");
return true;
}
/// <summary>
@@ -276,11 +452,21 @@ namespace Minigames.StatueDressup.Controllers
{
base.OnManagedDestroy();
// Cleanup button listener
// Release Addressables handle
AddressablesUtility.ReleaseHandle(_decorationDataHandle);
// Cleanup button listeners
if (takePhotoButton != null)
{
takePhotoButton.onClick.RemoveListener(OnTakePhoto);
}
// TODO: If ever picture gallery
// Gallery button cleanup (DISABLED - kept for future use)
// if (openGalleryButton != null)
// {
// openGalleryButton.onClick.RemoveListener(OnOpenGallery);
// }
}
}
}

View File

@@ -2,9 +2,9 @@
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
using Minigames.StatueDressup.Utils;
using UnityEngine;
using UnityEngine.UI;
using Utils;
namespace Minigames.StatueDressup.Controllers
{
@@ -18,49 +18,50 @@ namespace Minigames.StatueDressup.Controllers
[Header("Gallery UI")]
[SerializeField] private Transform gridContainer;
[SerializeField] private PhotoGridItem gridItemPrefab;
[SerializeField] private ScrollRect scrollRect;
[Header("Enlarged View")]
[SerializeField] private GameObject enlargedViewPanel;
[SerializeField] private Image enlargedPhotoImage;
[SerializeField] private Button closeEnlargedButton;
[SerializeField] private Button deletePhotoButton;
[SerializeField] private Text photoInfoText;
[SerializeField] private Transform enlargedContainer; // Container for enlarged preview (top layer)
[SerializeField] private GameObject backdrop; // Dark backdrop for enlarged view
[SerializeField] private GameObject enlargedPreviewPrefab; // Prefab for enlarged preview (same as grid item)
[Header("Pagination")]
[SerializeField] private Button loadMoreButton;
[SerializeField] private Text statusText;
[SerializeField] private Button previousPageButton;
[SerializeField] private Button nextPageButton;
[SerializeField] private Text pageStatusText;
[Header("Settings")]
[SerializeField] private int itemsPerPage = 20;
[SerializeField] private int thumbnailSize = 256;
[SerializeField] private int maxCachedThumbnails = 50; // Keep recent thumbnails in memory
private int _currentPage = 0;
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
private int _currentPage;
private List<string> _allPhotoIds = new List<string>();
private Dictionary<string, PhotoGridItem> _activeGridItems = new Dictionary<string, PhotoGridItem>();
private Dictionary<string, Texture2D> _thumbnailCache = new Dictionary<string, Texture2D>();
private Dictionary<string, Texture2D> _fullPhotoCache = new Dictionary<string, Texture2D>(); // Cache full photos for enlargement
private Queue<string> _thumbnailCacheOrder = new Queue<string>();
private string _currentEnlargedPhotoId = null;
private Texture2D _currentEnlargedTexture = null;
private bool _isLoadingPage;
private PhotoEnlargeController _enlargeController;
internal override void OnManagedStart()
{
base.OnManagedStart();
// Setup buttons
if (closeEnlargedButton != null)
closeEnlargedButton.onClick.AddListener(CloseEnlargedView);
// Get settings
_settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
if (deletePhotoButton != null)
deletePhotoButton.onClick.AddListener(DeleteCurrentPhoto);
// Initialize enlarge controller
_enlargeController = new PhotoEnlargeController(backdrop, enlargedContainer, _settings?.GalleryAnimationDuration ?? 0.3f);
if (loadMoreButton != null)
loadMoreButton.onClick.AddListener(LoadNextPage);
// Setup page navigation buttons
if (previousPageButton != null)
previousPageButton.onClick.AddListener(OnPreviousPageClicked);
// Hide enlarged view initially
if (enlargedViewPanel != null)
enlargedViewPanel.SetActive(false);
if (nextPageButton != null)
nextPageButton.onClick.AddListener(OnNextPageClicked);
// Hide backdrop initially
if (backdrop != null)
backdrop.SetActive(false);
// Clear grid initially (in case there are leftover items from scene setup)
ClearGrid();
// Load first page
RefreshGallery();
@@ -72,35 +73,35 @@ namespace Minigames.StatueDressup.Controllers
public void RefreshGallery()
{
// Clear existing items
ClearGallery();
ClearGrid();
// Get all photo IDs
_allPhotoIds = StatuePhotoManager.GetAllPhotoIds();
_allPhotoIds = PhotoManager.GetAllPhotoIds(CaptureType.StatueMinigame);
_currentPage = 0;
Logging.Debug($"[StatuePhotoGalleryController] Gallery refreshed: {_allPhotoIds.Count} photos");
// Load first page
LoadNextPage();
// Display first page
DisplayCurrentPage();
}
/// <summary>
/// Load next page of photos
/// Display the current page of photos (clears grid and shows only current page)
/// </summary>
private void LoadNextPage()
private void DisplayCurrentPage()
{
List<string> pagePhotoIds = StatuePhotoManager.GetPhotoIdsPage(_currentPage, itemsPerPage);
if (_isLoadingPage) return;
if (pagePhotoIds.Count == 0)
{
if (loadMoreButton != null)
loadMoreButton.gameObject.SetActive(false);
UpdateStatusText($"All photos loaded ({_allPhotoIds.Count} total)");
return;
}
_isLoadingPage = true;
Logging.Debug($"[StatuePhotoGalleryController] Loading page {_currentPage}: {pagePhotoIds.Count} items");
// Clear current grid
ClearGrid();
// Get photos for current page
int itemsPerPage = _settings?.GalleryItemsPerPage ?? 20;
List<string> pagePhotoIds = PhotoManager.GetPhotoIdsPage(CaptureType.StatueMinigame, _currentPage, itemsPerPage);
Logging.Debug($"[StatuePhotoGalleryController] Displaying page {_currentPage + 1}: {pagePhotoIds.Count} items");
// Spawn grid items for this page
foreach (string photoId in pagePhotoIds)
@@ -108,14 +109,64 @@ namespace Minigames.StatueDressup.Controllers
SpawnGridItem(photoId);
}
_currentPage++;
// Update button states
UpdatePageButtons();
// Update UI state
bool hasMore = _currentPage * itemsPerPage < _allPhotoIds.Count;
if (loadMoreButton != null)
loadMoreButton.gameObject.SetActive(hasMore);
// Update status text
int totalPages = Mathf.CeilToInt((float)_allPhotoIds.Count / itemsPerPage);
UpdateStatusText($"Page {_currentPage + 1}/{totalPages} ({_allPhotoIds.Count} photos)");
UpdateStatusText($"Showing {_activeGridItems.Count} of {_allPhotoIds.Count} photos");
_isLoadingPage = false;
}
/// <summary>
/// Update page navigation button states
/// </summary>
private void UpdatePageButtons()
{
int itemsPerPage = _settings?.GalleryItemsPerPage ?? 20;
int totalPages = Mathf.CeilToInt((float)_allPhotoIds.Count / itemsPerPage);
// Enable/disable previous button
if (previousPageButton != null)
{
previousPageButton.interactable = _currentPage > 0;
}
// Enable/disable next button
if (nextPageButton != null)
{
nextPageButton.interactable = _currentPage < totalPages - 1;
}
}
/// <summary>
/// Navigate to previous page
/// </summary>
private void OnPreviousPageClicked()
{
if (_currentPage > 0)
{
_currentPage--;
DisplayCurrentPage();
Logging.Debug($"[StatuePhotoGalleryController] Navigated to previous page: {_currentPage}");
}
}
/// <summary>
/// Navigate to next page
/// </summary>
private void OnNextPageClicked()
{
int itemsPerPage = _settings?.GalleryItemsPerPage ?? 20;
int totalPages = Mathf.CeilToInt((float)_allPhotoIds.Count / itemsPerPage);
if (_currentPage < totalPages - 1)
{
_currentPage++;
DisplayCurrentPage();
Logging.Debug($"[StatuePhotoGalleryController] Navigated to next page: {_currentPage}");
}
}
/// <summary>
@@ -154,7 +205,7 @@ namespace Minigames.StatueDressup.Controllers
yield return null;
// Load full photo
Texture2D fullPhoto = StatuePhotoManager.LoadPhoto(photoId);
Texture2D fullPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, photoId);
if (fullPhoto == null)
{
@@ -163,7 +214,8 @@ namespace Minigames.StatueDressup.Controllers
}
// Create thumbnail
Texture2D thumbnail = StatuePhotoManager.CreateThumbnail(fullPhoto, thumbnailSize);
int thumbSize = _settings?.GalleryThumbnailSize ?? 256;
Texture2D thumbnail = PhotoManager.CreateThumbnail(fullPhoto, thumbSize);
// Destroy full photo immediately (we only need thumbnail)
Destroy(fullPhoto);
@@ -188,7 +240,8 @@ namespace Minigames.StatueDressup.Controllers
_thumbnailCacheOrder.Enqueue(photoId);
// Evict oldest if over limit
while (_thumbnailCache.Count > maxCachedThumbnails && _thumbnailCacheOrder.Count > 0)
int maxCached = _settings?.GalleryMaxCachedThumbnails ?? 50;
while (_thumbnailCache.Count > maxCached && _thumbnailCacheOrder.Count > 0)
{
string oldestId = _thumbnailCacheOrder.Dequeue();
@@ -202,154 +255,90 @@ namespace Minigames.StatueDressup.Controllers
}
/// <summary>
/// Show enlarged view of a photo (called by PhotoGridItem)
/// Enlarge a photo (called by PhotoGridItem)
/// </summary>
public void ShowEnlargedView(string photoId)
public void OnGridItemClicked(PhotoGridItem gridItem, string photoId)
{
if (enlargedViewPanel == null || enlargedPhotoImage == null)
if (_enlargeController == null)
{
Logging.Warning("[StatuePhotoGalleryController] Enlarged view UI not configured");
Logging.Error("[StatuePhotoGalleryController] Enlarge controller not initialized");
return;
}
Logging.Debug($"[StatuePhotoGalleryController] Showing enlarged view: {photoId}");
// Clear previous enlarged texture
if (_currentEnlargedTexture != null)
// If already enlarged, shrink it
if (_enlargeController.IsPhotoEnlarged)
{
Destroy(_currentEnlargedTexture);
_currentEnlargedTexture = null;
}
// Load full-size photo
_currentEnlargedTexture = StatuePhotoManager.LoadPhoto(photoId);
if (_currentEnlargedTexture == null)
{
Logging.Error($"[StatuePhotoGalleryController] Failed to load enlarged photo: {photoId}");
_enlargeController.ShrinkPhoto();
return;
}
// Create sprite from texture
Sprite enlargedSprite = Sprite.Create(
_currentEnlargedTexture,
new Rect(0, 0, _currentEnlargedTexture.width, _currentEnlargedTexture.height),
new Vector2(0.5f, 0.5f)
);
Logging.Debug($"[StatuePhotoGalleryController] Enlarging photo: {photoId}");
enlargedPhotoImage.sprite = enlargedSprite;
_currentEnlargedPhotoId = photoId;
float enlargedScale = _settings?.GalleryEnlargedScale ?? 2.5f;
// Update photo info
UpdatePhotoInfo(photoId);
// Show panel
enlargedViewPanel.SetActive(true);
}
/// <summary>
/// Close enlarged view
/// </summary>
private void CloseEnlargedView()
{
if (enlargedViewPanel != null)
enlargedViewPanel.SetActive(false);
// Clean up texture
if (_currentEnlargedTexture != null)
// Check cache first
if (_fullPhotoCache.TryGetValue(photoId, out Texture2D fullPhoto))
{
Destroy(_currentEnlargedTexture);
_currentEnlargedTexture = null;
}
_currentEnlargedPhotoId = null;
}
/// <summary>
/// Delete currently viewed photo
/// </summary>
private void DeleteCurrentPhoto()
{
if (string.IsNullOrEmpty(_currentEnlargedPhotoId))
{
Logging.Warning("[StatuePhotoGalleryController] No photo selected for deletion");
return;
}
string photoIdToDelete = _currentEnlargedPhotoId;
// Close enlarged view first
CloseEnlargedView();
// Delete photo
bool deleted = StatuePhotoManager.DeletePhoto(photoIdToDelete);
if (deleted)
{
// Remove from grid
if (_activeGridItems.TryGetValue(photoIdToDelete, out PhotoGridItem gridItem))
{
Destroy(gridItem.gameObject);
_activeGridItems.Remove(photoIdToDelete);
}
// Remove from cache
if (_thumbnailCache.TryGetValue(photoIdToDelete, out Texture2D thumbnail))
{
Destroy(thumbnail);
_thumbnailCache.Remove(photoIdToDelete);
}
// Refresh photo list
_allPhotoIds.Remove(photoIdToDelete);
UpdateStatusText($"Photo deleted. {_allPhotoIds.Count} photos remaining");
Logging.Debug($"[StatuePhotoGalleryController] Photo deleted: {photoIdToDelete}");
}
}
/// <summary>
/// Update photo info text in enlarged view
/// </summary>
private void UpdatePhotoInfo(string photoId)
{
if (photoInfoText == null) return;
StatuePhotoManager.PhotoMetadata metadata = StatuePhotoManager.GetPhotoMetadata(photoId);
if (metadata != null)
{
System.DateTime timestamp = System.DateTime.Parse(metadata.timestamp);
string dateStr = timestamp.ToString("MMM dd, yyyy hh:mm tt");
float fileSizeMB = metadata.fileSizeBytes / (1024f * 1024f);
photoInfoText.text = $"Date: {dateStr}\n" +
$"Decorations: {metadata.decorationCount}\n" +
$"Size: {fileSizeMB:F2} MB";
// Use cached photo
_enlargeController.EnlargePhoto(gridItem, enlargedPreviewPrefab != null ? enlargedPreviewPrefab : gridItem.gameObject, fullPhoto, enlargedScale);
}
else
{
photoInfoText.text = "Photo information unavailable";
// Load full-size photo
fullPhoto = PhotoManager.LoadPhoto(CaptureType.StatueMinigame, photoId);
if (fullPhoto == null)
{
Logging.Error($"[StatuePhotoGalleryController] Failed to load photo: {photoId}");
return;
}
// Cache it (limited cache)
if (_fullPhotoCache.Count < 10) // Keep only recent 10 full photos
{
_fullPhotoCache[photoId] = fullPhoto;
}
_enlargeController.EnlargePhoto(gridItem, enlargedPreviewPrefab != null ? enlargedPreviewPrefab : gridItem.gameObject, fullPhoto, enlargedScale);
}
}
/// <summary>
/// Cleanup when gallery is closed
/// </summary>
public void CleanupGallery()
{
if (_enlargeController != null)
{
_enlargeController.Cleanup();
}
// Clean up cached full photos
foreach (var photo in _fullPhotoCache.Values)
{
if (photo != null)
{
Destroy(photo);
}
}
_fullPhotoCache.Clear();
}
/// <summary>
/// Update status text
/// </summary>
private void UpdateStatusText(string message)
{
if (statusText != null)
statusText.text = message;
if (pageStatusText != null)
pageStatusText.text = message;
Logging.Debug($"[StatuePhotoGalleryController] Status: {message}");
}
/// <summary>
/// Clear all grid items and cached data
/// Clear only the grid items (used when switching pages)
/// </summary>
private void ClearGallery()
private void ClearGrid()
{
// Destroy grid items
foreach (var gridItem in _activeGridItems.Values)
@@ -359,6 +348,16 @@ namespace Minigames.StatueDressup.Controllers
}
_activeGridItems.Clear();
Logging.Debug("[StatuePhotoGalleryController] Grid cleared");
}
/// <summary>
/// Clear all grid items and cached data (full cleanup)
/// </summary>
private void ClearGallery()
{
ClearGrid();
// Clear thumbnail cache
foreach (var thumbnail in _thumbnailCache.Values)
{
@@ -368,7 +367,7 @@ namespace Minigames.StatueDressup.Controllers
_thumbnailCache.Clear();
_thumbnailCacheOrder.Clear();
Logging.Debug("[StatuePhotoGalleryController] Gallery cleared");
Logging.Debug("[StatuePhotoGalleryController] Gallery fully cleared");
}
internal override void OnManagedDestroy()
@@ -377,17 +376,14 @@ namespace Minigames.StatueDressup.Controllers
// Cleanup
ClearGallery();
CloseEnlargedView();
CleanupGallery();
// Unsubscribe buttons
if (closeEnlargedButton != null)
closeEnlargedButton.onClick.RemoveListener(CloseEnlargedView);
if (previousPageButton != null)
previousPageButton.onClick.RemoveListener(OnPreviousPageClicked);
if (deletePhotoButton != null)
deletePhotoButton.onClick.RemoveListener(DeleteCurrentPhoto);
if (loadMoreButton != null)
loadMoreButton.onClick.RemoveListener(LoadNextPage);
if (nextPageButton != null)
nextPageButton.onClick.RemoveListener(OnNextPageClicked);
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Minigames.StatueDressup.Data
{
/// <summary>
/// Metadata for a single decoration placement
/// </summary>
[Serializable]
public class DecorationPlacement
{
public string decorationId; // Unique ID to load decoration
public Vector2 localPosition; // Position relative to statue
public Vector2 localScale; // Scale relative to statue
public float rotation; // Z rotation in degrees
public int sortingOrder; // Sprite sorting order
}
/// <summary>
/// Coordinate system type used when saving decoration positions
/// </summary>
public enum CoordinateSystemType
{
WorldSpace, // Regular Transform (world units)
UIRectTransform // UI RectTransform (pixel coordinates)
}
/// <summary>
/// Collection of decoration placements for a statue
/// </summary>
[Serializable]
public class StatueDecorationData
{
public string photoId; // Associated photo ID
public string timestamp; // When captured
public CoordinateSystemType coordinateSystem; // Source coordinate system
public Vector2 sourceStatueSize; // Size of statue in source units (for conversion)
public List<DecorationPlacement> placements = new List<DecorationPlacement>();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bde3a2b8ef7247d29967999bb5e9dbd8
timeCreated: 1764163703

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21f5742e8db64d52a293d822c93df4f3
timeCreated: 1764163758

View File

@@ -0,0 +1,305 @@
using System.Collections.Generic;
using Core;
using Core.Lifecycle;
using Minigames.StatueDressup.Data;
using UnityEngine;
using UnityEngine.ResourceManagement.AsyncOperations;
using Utils;
namespace Minigames.StatueDressup.Display
{
/// <summary>
/// Loads decoration metadata and reconstructs decorations on a statue sprite.
/// Place this component on a GameObject with a SpriteRenderer showing the statue.
/// On Start, loads all DecorationData via Addressables label, then spawns decorations from metadata.
/// </summary>
[RequireComponent(typeof(SpriteRenderer))]
public class StatueDecorationLoader : ManagedBehaviour
{
[Header("Settings")]
[Tooltip("Root GameObject for spawning decorations (clears only this, not statue children)")]
[SerializeField] private Transform decorationRoot;
[Tooltip("Load specific photo ID, or leave empty to load latest")]
[SerializeField] private string specificPhotoId = "";
[Header("Debug")]
[SerializeField] private bool showDebugInfo = true;
private SpriteRenderer _statueSpriteRenderer;
private Dictionary<string, DecorationData> _decorationDataDict;
private AsyncOperationHandle<IList<DecorationData>> _decorationDataHandle;
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
internal override void OnManagedStart()
{
base.OnManagedStart();
_statueSpriteRenderer = GetComponent<SpriteRenderer>();
// Get settings
_settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
// Ensure decoration root exists
if (decorationRoot == null)
{
GameObject rootObj = new GameObject("DecorationRoot");
rootObj.transform.SetParent(transform, false);
decorationRoot = rootObj.transform;
if (showDebugInfo)
{
Logging.Debug("[StatueDecorationLoader] Created decoration root automatically");
}
}
// Start async loading via coroutine wrapper
StartCoroutine(LoadAndDisplayDecorationsCoroutine());
}
/// <summary>
/// Coroutine wrapper for async loading and display
/// </summary>
private System.Collections.IEnumerator LoadAndDisplayDecorationsCoroutine()
{
// Convert async Task to coroutine-compatible operation
var loadTask = LoadDecorationDataAsync();
// Wait for async operation to complete
while (!loadTask.IsCompleted)
{
yield return null;
}
// Check for exceptions
if (loadTask.IsFaulted)
{
Logging.Error($"[StatueDecorationLoader] Failed to load decoration data: {loadTask.Exception?.GetBaseException().Message}");
yield break;
}
// Load and display decorations
LoadAndDisplayDecorations();
}
/// <summary>
/// Load all DecorationData assets via Addressables and build lookup dictionary
/// </summary>
private async System.Threading.Tasks.Task LoadDecorationDataAsync()
{
string label = _settings?.DecorationDataLabel;
if (string.IsNullOrEmpty(label))
{
Logging.Error("[StatueDecorationLoader] Decoration data label not set in settings!");
return;
}
if (showDebugInfo)
{
Logging.Debug($"[StatueDecorationLoader] Loading DecorationData with label '{label}'...");
}
// Use utility to load all DecorationData and create dictionary by ID
var result = await AddressablesUtility.LoadAssetsByLabelAsync<DecorationData, string>(
label,
data => data.DecorationId, // Key selector: use DecorationId as key
progress => { /* Optional: could show loading bar */ }
);
_decorationDataDict = result.dictionary;
_decorationDataHandle = result.handle;
if (showDebugInfo)
{
Logging.Debug($"[StatueDecorationLoader] Loaded {_decorationDataDict.Count} DecorationData assets");
}
}
/// <summary>
/// Load decoration metadata and spawn decorations
/// </summary>
public void LoadAndDisplayDecorations()
{
// Check if DecorationData is loaded
if (_decorationDataDict == null || _decorationDataDict.Count == 0)
{
Logging.Warning("[StatueDecorationLoader] DecorationData not loaded yet. Cannot display decorations.");
return;
}
// Load metadata
StatueDecorationData data = string.IsNullOrEmpty(specificPhotoId)
? PhotoManager.LoadLatestDecorationMetadata<StatueDecorationData>(CaptureType.StatueMinigame)
: PhotoManager.LoadDecorationMetadata<StatueDecorationData>(CaptureType.StatueMinigame, specificPhotoId);
if (data == null)
{
Logging.Warning("[StatueDecorationLoader] No decoration metadata found");
return;
}
if (showDebugInfo)
{
Logging.Debug($"[StatueDecorationLoader] Loading {data.placements.Count} decorations from {data.photoId}");
Logging.Debug($"[StatueDecorationLoader] Source coordinate system: {data.coordinateSystem}, statue size: {data.sourceStatueSize}");
}
// Clear existing decorations (in case reloading)
ClearDecorations();
// Calculate coordinate conversion factor if needed
float conversionFactor = CalculateCoordinateConversion(data);
// Spawn each decoration synchronously (data already loaded)
int successCount = 0;
foreach (var placement in data.placements)
{
if (SpawnDecoration(placement, conversionFactor))
{
successCount++;
}
}
if (showDebugInfo)
{
Logging.Debug($"[StatueDecorationLoader] Successfully loaded {successCount}/{data.placements.Count} decorations");
}
}
/// <summary>
/// Calculate coordinate conversion factor between source and target coordinate systems
/// </summary>
private float CalculateCoordinateConversion(StatueDecorationData data)
{
// If source was world space and we're also world space, no conversion needed
if (data.coordinateSystem == CoordinateSystemType.WorldSpace)
{
if (showDebugInfo)
{
Logging.Debug("[StatueDecorationLoader] No coordinate conversion needed (WorldSpace → WorldSpace)");
}
return 1f;
}
// Source was UI RectTransform (pixels), target is WorldSpace (units)
// Need to convert from source statue pixel size to target statue world size
// Get target statue size (world units)
Vector2 targetStatueSize = Vector2.one;
if (_statueSpriteRenderer != null && _statueSpriteRenderer.sprite != null)
{
targetStatueSize = _statueSpriteRenderer.sprite.bounds.size;
}
// Calculate conversion factor (target size / source size)
float conversionX = targetStatueSize.x / data.sourceStatueSize.x;
float conversionY = targetStatueSize.y / data.sourceStatueSize.y;
// Use average of X and Y for uniform scaling (or could use separate X/Y)
float conversionFactor = (conversionX + conversionY) / 2f;
if (showDebugInfo)
{
Logging.Debug($"[StatueDecorationLoader] Coordinate conversion: UI({data.sourceStatueSize}) → World({targetStatueSize}) = factor {conversionFactor:F3}");
}
return conversionFactor;
}
/// <summary>
/// Spawn a single decoration from placement data
/// Looks up DecorationData from pre-loaded dictionary and applies coordinate conversion
/// </summary>
private bool SpawnDecoration(DecorationPlacement placement, float conversionFactor)
{
// Look up DecorationData from dictionary
if (!_decorationDataDict.TryGetValue(placement.decorationId, out DecorationData decorationData))
{
Logging.Warning($"[StatueDecorationLoader] DecorationData not found for ID: {placement.decorationId}");
return false;
}
// Get sprite from DecorationData
Sprite decorationSprite = decorationData.DecorationSprite;
if (decorationSprite == null)
{
Logging.Warning($"[StatueDecorationLoader] DecorationData has null sprite: {placement.decorationId}");
return false;
}
// Create GameObject for decoration
GameObject decorationObj = new GameObject($"Decoration_{placement.decorationId}");
decorationObj.transform.SetParent(decorationRoot, false); // false = keep local position
// Add SpriteRenderer
SpriteRenderer spriteRenderer = decorationObj.AddComponent<SpriteRenderer>();
spriteRenderer.sprite = decorationSprite;
spriteRenderer.sortingLayerName = "Foreground";
spriteRenderer.sortingOrder = _statueSpriteRenderer.sortingOrder + placement.sortingOrder;
// Apply transform with coordinate conversion
Vector3 convertedPosition = placement.localPosition * conversionFactor;
decorationObj.transform.localPosition = convertedPosition;
decorationObj.transform.localScale = placement.localScale;
decorationObj.transform.localEulerAngles = new Vector3(0, 0, placement.rotation);
if (showDebugInfo)
{
Logging.Debug($"[StatueDecorationLoader] Spawned: {placement.decorationId} at {convertedPosition} (original: {placement.localPosition}, factor: {conversionFactor:F3})");
}
return true;
}
/// <summary>
/// Clear all existing decorations from decorationRoot
/// </summary>
public void ClearDecorations()
{
if (decorationRoot == null) return;
// Remove all children from decoration root only
for (int i = decorationRoot.childCount - 1; i >= 0; i--)
{
if (Application.isPlaying)
{
Destroy(decorationRoot.GetChild(i).gameObject);
}
else
{
DestroyImmediate(decorationRoot.GetChild(i).gameObject);
}
}
}
/// <summary>
/// Cleanup - release Addressables handle
/// </summary>
private void OnDestroy()
{
// Release DecorationData handle
AddressablesUtility.ReleaseHandle(_decorationDataHandle);
}
/// <summary>
/// Reload decorations (useful for testing)
/// </summary>
[ContextMenu("Reload Decorations")]
public void ReloadDecorations()
{
LoadAndDisplayDecorations();
}
/// <summary>
/// Load specific photo's decorations
/// </summary>
public void LoadSpecificPhoto(string photoId)
{
specificPhotoId = photoId;
LoadAndDisplayDecorations();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50d0f4591bbd40fc81dc615fa465e0c5
timeCreated: 1764163758

View File

@@ -1,7 +1,6 @@
using Core;
using Minigames.StatueDressup.Controllers;
using Minigames.StatueDressup.Data;
using Minigames.StatueDressup.Utils;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
@@ -77,6 +76,40 @@ namespace Minigames.StatueDressup.DragDrop
Logging.Debug($"[DecorationDraggableInstance] Initialized: {data?.DecorationName}");
}
/// <summary>
/// Initialize as already placed decoration (from saved state)
/// Skips drag logic and sets up as if already placed on statue
/// </summary>
public void InitializeAsPlaced(DecorationData data, StatueDecorationController controller,
AppleHills.Core.Settings.IStatueDressupSettings settings)
{
_decorationData = data;
_controller = controller;
_settings = settings;
_isPlacedOnStatue = true;
_isDragging = false;
// Set sprite
if (decorationImage != null && data != null && data.DecorationSprite != null)
{
decorationImage.sprite = data.DecorationSprite;
}
// Set authored size
if (_rectTransform != null && data != null)
{
_rectTransform.sizeDelta = data.AuthoredSize;
}
// Make non-interactive for placed state (can be made interactive later if needed)
if (canvasGroup != null)
{
canvasGroup.blocksRaycasts = true;
}
Logging.Debug($"[DecorationDraggableInstance] Initialized as placed: {data?.DecorationName}");
}
/// <summary>
/// Start dragging from icon
/// </summary>

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49800fa9079d485cbc65922d238d214a
timeCreated: 1764151454

View File

@@ -0,0 +1,28 @@
using UI.Core;
namespace Minigames.StatueDressup.UI
{
/// <summary>
/// UIPage wrapper for the photo gallery.
/// Simple stock page with no transition animations.
/// </summary>
public class PhotoGalleryPage : UIPage
{
protected override void DoTransitionIn(System.Action onComplete)
{
// Instant transition - just show
gameObject.SetActive(true);
onComplete?.Invoke();
}
protected override void DoTransitionOut(System.Action onComplete)
{
// Instant transition - just hide
gameObject.SetActive(false);
onComplete?.Invoke();
}
// OnBackPressed uses default behavior (pops the page)
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3136a7edf8de421e9860c6bd4e1dca10
timeCreated: 1764151460

View File

@@ -0,0 +1,32 @@
using UI.Core;
namespace Minigames.StatueDressup.UI
{
/// <summary>
/// UIPage wrapper for the statue decoration play area.
/// Simple stock page with no transition animations.
/// </summary>
public class PlayAreaPage : UIPage
{
protected override void DoTransitionIn(System.Action onComplete)
{
// Instant transition - just show
gameObject.SetActive(true);
onComplete?.Invoke();
}
protected override void DoTransitionOut(System.Action onComplete)
{
// Instant transition - just hide
gameObject.SetActive(false);
onComplete?.Invoke();
}
public override void OnBackPressed()
{
// Play area is the root page - don't allow back navigation
// Override if you want custom behavior (e.g., quit minigame)
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edd2867b8a4f42d8af202f215c3ecc47
timeCreated: 1764151454