cement daddy
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
using System.Collections.Generic;
|
||||
using Core;
|
||||
using Minigames.StatueDressup.Data;
|
||||
using Minigames.StatueDressup.DragDrop;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the side menu with decoration items and pagination
|
||||
/// </summary>
|
||||
public class DecorationMenuController : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private DecorationItem itemPrefab;
|
||||
[SerializeField] private Transform itemsContainer;
|
||||
[SerializeField] private Button nextPageButton;
|
||||
[SerializeField] private Button previousPageButton;
|
||||
|
||||
[Header("Configuration")]
|
||||
[SerializeField] private List<DecorationData> allDecorations = new List<DecorationData>();
|
||||
[SerializeField] private int itemsPerPage = 10; // 2 columns x 5 rows
|
||||
|
||||
[Header("Layout")]
|
||||
[SerializeField] private GridLayoutGroup gridLayout;
|
||||
|
||||
private int _currentPage = 0;
|
||||
private int _totalPages = 0;
|
||||
private List<DecorationItem> _spawnedItems = new List<DecorationItem>();
|
||||
private Dictionary<DecorationItem, DecorationData> _itemDataMapping = new Dictionary<DecorationItem, DecorationData>();
|
||||
|
||||
// Properties
|
||||
public int CurrentPage => _currentPage;
|
||||
public int TotalPages => _totalPages;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Logging.Debug($"[DecorationMenuController] Initializing with {allDecorations.Count} decorations");
|
||||
|
||||
// Calculate total pages
|
||||
_totalPages = Mathf.CeilToInt((float)allDecorations.Count / itemsPerPage);
|
||||
Logging.Debug($"[DecorationMenuController] Total pages: {_totalPages}");
|
||||
|
||||
// Setup buttons
|
||||
if (nextPageButton != null)
|
||||
{
|
||||
nextPageButton.onClick.AddListener(OnNextPage);
|
||||
}
|
||||
|
||||
if (previousPageButton != null)
|
||||
{
|
||||
previousPageButton.onClick.AddListener(OnPreviousPage);
|
||||
}
|
||||
|
||||
// Subscribe to drag events for all items
|
||||
// (will be handled per-item when spawned)
|
||||
|
||||
// Populate first page
|
||||
PopulateCurrentPage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the current page with decoration items
|
||||
/// </summary>
|
||||
private void PopulateCurrentPage()
|
||||
{
|
||||
Logging.Debug($"[DecorationMenuController] Populating page {_currentPage + 1}/{_totalPages}");
|
||||
|
||||
// Clear existing items
|
||||
ClearItems();
|
||||
|
||||
// Calculate range for current page
|
||||
int startIndex = _currentPage * itemsPerPage;
|
||||
int endIndex = Mathf.Min(startIndex + itemsPerPage, allDecorations.Count);
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Spawning items {startIndex} to {endIndex - 1}");
|
||||
|
||||
// Spawn items for this page
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
SpawnDecorationItem(allDecorations[i]);
|
||||
}
|
||||
|
||||
// Update button states
|
||||
UpdateNavigationButtons();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn a decoration item in the menu
|
||||
/// </summary>
|
||||
private void SpawnDecorationItem(DecorationData data)
|
||||
{
|
||||
if (itemPrefab == null || itemsContainer == null)
|
||||
{
|
||||
Logging.Warning("[DecorationMenuController] Missing prefab or container");
|
||||
return;
|
||||
}
|
||||
|
||||
DecorationItem item = Instantiate(itemPrefab, itemsContainer);
|
||||
item.SetDecorationData(data);
|
||||
|
||||
// Store original position for return animation
|
||||
if (item.RectTransform != null)
|
||||
{
|
||||
// Force layout update to get correct position
|
||||
Canvas.ForceUpdateCanvases();
|
||||
item.SetOriginalMenuPosition(item.RectTransform.anchoredPosition);
|
||||
}
|
||||
|
||||
// Subscribe to drag events
|
||||
item.OnDragStarted += HandleItemPickedUp;
|
||||
item.OnDragEnded += HandleItemDropped;
|
||||
|
||||
_spawnedItems.Add(item);
|
||||
_itemDataMapping[item] = data;
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Spawned: {data.DecorationName} at position {item.RectTransform?.anchoredPosition}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle item picked up from menu
|
||||
/// </summary>
|
||||
private void HandleItemPickedUp(DraggableObject draggable)
|
||||
{
|
||||
if (draggable is DecorationItem item && _itemDataMapping.ContainsKey(item))
|
||||
{
|
||||
Logging.Debug($"[DecorationMenuController] Item picked up: {item.Data?.DecorationName}");
|
||||
|
||||
// Spawn replacement in menu slot
|
||||
// This ensures menu always shows available items
|
||||
DecorationData data = _itemDataMapping[item];
|
||||
// We'll spawn replacement only if item is actually placed, not on pickup
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle item dropped (either placed on statue or returned to menu)
|
||||
/// </summary>
|
||||
private void HandleItemDropped(DraggableObject draggable)
|
||||
{
|
||||
if (draggable is DecorationItem item && _itemDataMapping.ContainsKey(item))
|
||||
{
|
||||
Logging.Debug($"[DecorationMenuController] Item dropped: {item.Data?.DecorationName}, slot={item.CurrentSlot?.name}");
|
||||
|
||||
// If item was placed on statue, spawn replacement in menu
|
||||
if (item.CurrentSlot != null && !item.IsInMenu)
|
||||
{
|
||||
DecorationData data = _itemDataMapping[item];
|
||||
|
||||
// Remove original from tracking
|
||||
_spawnedItems.Remove(item);
|
||||
_itemDataMapping.Remove(item);
|
||||
|
||||
// Spawn replacement
|
||||
SpawnDecorationItem(data);
|
||||
|
||||
Logging.Debug($"[DecorationMenuController] Spawned replacement for: {data.DecorationName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all spawned items
|
||||
/// </summary>
|
||||
private void ClearItems()
|
||||
{
|
||||
foreach (var item in _spawnedItems)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
item.OnDragStarted -= HandleItemPickedUp;
|
||||
item.OnDragEnded -= HandleItemDropped;
|
||||
Destroy(item.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
_spawnedItems.Clear();
|
||||
_itemDataMapping.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to next page
|
||||
/// </summary>
|
||||
private void OnNextPage()
|
||||
{
|
||||
if (_currentPage < _totalPages - 1)
|
||||
{
|
||||
_currentPage++;
|
||||
PopulateCurrentPage();
|
||||
Logging.Debug($"[DecorationMenuController] Next page: {_currentPage + 1}/{_totalPages}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to previous page
|
||||
/// </summary>
|
||||
private void OnPreviousPage()
|
||||
{
|
||||
if (_currentPage > 0)
|
||||
{
|
||||
_currentPage--;
|
||||
PopulateCurrentPage();
|
||||
Logging.Debug($"[DecorationMenuController] Previous page: {_currentPage + 1}/{_totalPages}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update navigation button interactability
|
||||
/// </summary>
|
||||
private void UpdateNavigationButtons()
|
||||
{
|
||||
if (previousPageButton != null)
|
||||
{
|
||||
previousPageButton.interactable = _currentPage > 0;
|
||||
}
|
||||
|
||||
if (nextPageButton != null)
|
||||
{
|
||||
nextPageButton.interactable = _currentPage < _totalPages - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Cleanup button listeners
|
||||
if (nextPageButton != null)
|
||||
{
|
||||
nextPageButton.onClick.RemoveListener(OnNextPage);
|
||||
}
|
||||
|
||||
if (previousPageButton != null)
|
||||
{
|
||||
previousPageButton.onClick.RemoveListener(OnPreviousPage);
|
||||
}
|
||||
|
||||
// Cleanup item listeners
|
||||
ClearItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acbd542762b44e719326dff6c3a69e6e
|
||||
timeCreated: 1763745579
|
||||
@@ -0,0 +1,267 @@
|
||||
using System.Collections.Generic;
|
||||
using Core;
|
||||
using Minigames.StatueDressup.DragDrop;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minigames.StatueDressup.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Main controller for the Mr. Cement statue decoration minigame
|
||||
/// </summary>
|
||||
public class StatueDecorationController : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private StatueDecorationSlot[] statueSlots;
|
||||
[SerializeField] private DecorationMenuController menuController;
|
||||
[SerializeField] private Button takePhotoButton;
|
||||
[SerializeField] private GameObject statue;
|
||||
|
||||
[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 Dictionary<StatueDecorationSlot, DecorationItem> _placedDecorations = new Dictionary<StatueDecorationSlot, DecorationItem>();
|
||||
private bool _minigameCompleted = false;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Logging.Debug("[StatueDecorationController] Initializing minigame");
|
||||
|
||||
// Setup photo button
|
||||
if (takePhotoButton != null)
|
||||
{
|
||||
takePhotoButton.onClick.AddListener(OnTakePhoto);
|
||||
}
|
||||
|
||||
// Subscribe to slot occupation events
|
||||
foreach (var slot in statueSlots)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
slot.OnOccupied += HandleDecorationPlaced;
|
||||
}
|
||||
}
|
||||
|
||||
// Load saved state if exists
|
||||
LoadStatueState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle decoration placed in slot
|
||||
/// </summary>
|
||||
private void HandleDecorationPlaced(DraggableObject draggable)
|
||||
{
|
||||
if (draggable is DecorationItem decoration)
|
||||
{
|
||||
var slot = decoration.CurrentSlot as StatueDecorationSlot;
|
||||
if (slot != null)
|
||||
{
|
||||
_placedDecorations[slot] = decoration;
|
||||
Logging.Debug($"[StatueDecorationController] Decoration placed: {decoration.Data?.DecorationName} in slot {slot.name}");
|
||||
|
||||
// Auto-save state
|
||||
SaveStatueState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take photo of decorated statue
|
||||
/// </summary>
|
||||
private void OnTakePhoto()
|
||||
{
|
||||
if (_minigameCompleted)
|
||||
{
|
||||
Logging.Debug("[StatueDecorationController] Minigame already completed");
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.Debug("[StatueDecorationController] Taking photo of statue");
|
||||
|
||||
// Hide UI elements
|
||||
HideUIForPhoto(true);
|
||||
|
||||
// Wait a frame for UI to hide, then capture
|
||||
StartCoroutine(CapturePhotoCoroutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture photo after UI is hidden
|
||||
/// </summary>
|
||||
private System.Collections.IEnumerator CapturePhotoCoroutine()
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
// Capture the photo area
|
||||
Texture2D photo = CaptureScreenshotArea();
|
||||
|
||||
if (photo != null)
|
||||
{
|
||||
// Save photo to album
|
||||
SavePhotoToAlbum(photo);
|
||||
|
||||
// Award cards
|
||||
AwardCards();
|
||||
|
||||
// Update town icon
|
||||
UpdateTownIcon(photo);
|
||||
|
||||
// Show completion feedback
|
||||
ShowCompletionFeedback();
|
||||
|
||||
_minigameCompleted = true;
|
||||
}
|
||||
|
||||
// Restore UI
|
||||
HideUIForPhoto(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Capture screenshot of specific area
|
||||
/// </summary>
|
||||
private Texture2D CaptureScreenshotArea()
|
||||
{
|
||||
if (photoArea == null)
|
||||
{
|
||||
Logging.Warning("[StatueDecorationController] No photo area specified, capturing full screen");
|
||||
|
||||
// Capture full screen
|
||||
Texture2D screenshot = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
|
||||
screenshot.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
|
||||
screenshot.Apply();
|
||||
return screenshot;
|
||||
}
|
||||
|
||||
// Get world corners of the rect
|
||||
Vector3[] corners = new Vector3[4];
|
||||
photoArea.GetWorldCorners(corners);
|
||||
|
||||
// Convert to screen space
|
||||
Vector2 min = RectTransformUtility.WorldToScreenPoint(Camera.main, corners[0]);
|
||||
Vector2 max = RectTransformUtility.WorldToScreenPoint(Camera.main, corners[2]);
|
||||
|
||||
int width = (int)(max.x - min.x);
|
||||
int height = (int)(max.y - min.y);
|
||||
|
||||
Logging.Debug($"[StatueDecorationController] Capturing area: {width}x{height} at ({min.x}, {min.y})");
|
||||
|
||||
// Capture the specified area
|
||||
Texture2D screenshot = new Texture2D(width, height, TextureFormat.RGB24, false);
|
||||
screenshot.ReadPixels(new Rect(min.x, min.y, width, height), 0, 0);
|
||||
screenshot.Apply();
|
||||
|
||||
return screenshot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save photo to card album
|
||||
/// </summary>
|
||||
private void SavePhotoToAlbum(Texture2D photo)
|
||||
{
|
||||
// TODO: Integrate with existing album save system
|
||||
// For now, save to PlayerPrefs as base64
|
||||
byte[] bytes = photo.EncodeToPNG();
|
||||
string base64 = System.Convert.ToBase64String(bytes);
|
||||
PlayerPrefs.SetString(photoSaveKey, base64);
|
||||
PlayerPrefs.Save();
|
||||
|
||||
Logging.Debug("[StatueDecorationController] Photo saved to album");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Award Blokkemon cards to player
|
||||
/// </summary>
|
||||
private void AwardCards()
|
||||
{
|
||||
// TODO: Integrate with MinigameBoosterGiver
|
||||
// MinigameBoosterGiver.GiveBooster();
|
||||
|
||||
Logging.Debug("[StatueDecorationController] Cards awarded (TODO: implement)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update town menu icon with decorated statue
|
||||
/// </summary>
|
||||
private void UpdateTownIcon(Texture2D photo)
|
||||
{
|
||||
// TODO: Integrate with town system
|
||||
// TownIconUpdater.SetStatueIcon(photo);
|
||||
|
||||
Logging.Debug("[StatueDecorationController] Town icon updated (TODO: implement)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show completion feedback to player
|
||||
/// </summary>
|
||||
private void ShowCompletionFeedback()
|
||||
{
|
||||
// TODO: Show success message/animation
|
||||
DebugUIMessage.Show("Photo captured! Mr. Cement looks amazing!", Color.green);
|
||||
Logging.Debug("[StatueDecorationController] Minigame completed!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide/show UI elements for photo
|
||||
/// </summary>
|
||||
private void HideUIForPhoto(bool hide)
|
||||
{
|
||||
foreach (var element in uiElementsToHideForPhoto)
|
||||
{
|
||||
if (element != null)
|
||||
{
|
||||
element.SetActive(!hide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save current statue decoration state
|
||||
/// </summary>
|
||||
private void SaveStatueState()
|
||||
{
|
||||
// TODO: Implement save system
|
||||
// Save slot ID -> decoration ID mapping
|
||||
|
||||
Logging.Debug("[StatueDecorationController] State saved (TODO: implement persistence)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load saved statue decoration state
|
||||
/// </summary>
|
||||
private void LoadStatueState()
|
||||
{
|
||||
// TODO: Implement load system
|
||||
// Restore decorations to slots
|
||||
|
||||
Logging.Debug("[StatueDecorationController] State loaded (TODO: implement persistence)");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Cleanup button listener
|
||||
if (takePhotoButton != null)
|
||||
{
|
||||
takePhotoButton.onClick.RemoveListener(OnTakePhoto);
|
||||
}
|
||||
|
||||
// Cleanup slot listeners
|
||||
foreach (var slot in statueSlots)
|
||||
{
|
||||
if (slot != null)
|
||||
{
|
||||
slot.OnOccupied -= HandleDecorationPlaced;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19e312ceaffa40ae90ac87b8209319cb
|
||||
timeCreated: 1763745610
|
||||
Reference in New Issue
Block a user