FInalize first pass over cement decoration game

This commit is contained in:
Michal Pikulski
2025-11-24 15:54:03 +01:00
parent e04db31a98
commit b90ab4b0ba
32 changed files with 954 additions and 570 deletions

View File

@@ -168,7 +168,6 @@ namespace AppleHills.Core.Settings
public interface IStatueDressupSettings
{
// Decoration Display
Vector2 DefaultIconSize { get; }
Vector2 DefaultAuthoredSize { get; }
// Decoration Content

View File

@@ -12,9 +12,6 @@ namespace Core.Settings
public class StatueDressupSettings : BaseSettings, IStatueDressupSettings
{
[Header("Decoration Display")]
[Tooltip("Default icon size for decorations in the menu grid")]
[SerializeField] private Vector2 defaultIconSize = new Vector2(64f, 64f);
[Tooltip("Default full size for decorations when placed on statue")]
[SerializeField] private Vector2 defaultAuthoredSize = new Vector2(128f, 128f);
@@ -80,7 +77,6 @@ namespace Core.Settings
[SerializeField] private int maxSavedDecorations = 50;
// IStatueDressupSettings implementation - Decoration Display
public Vector2 DefaultIconSize => defaultIconSize;
public Vector2 DefaultAuthoredSize => defaultAuthoredSize;
// IStatueDressupSettings implementation - Decoration Content
@@ -120,8 +116,6 @@ namespace Core.Settings
base.OnValidate();
// Validate decoration display
defaultIconSize.x = Mathf.Max(16f, defaultIconSize.x);
defaultIconSize.y = Mathf.Max(16f, defaultIconSize.y);
defaultAuthoredSize.x = Mathf.Max(32f, defaultAuthoredSize.x);
defaultAuthoredSize.y = Mathf.Max(32f, defaultAuthoredSize.y);

View File

@@ -3,7 +3,6 @@ using Core;
using Core.Lifecycle;
using Minigames.StatueDressup.Data;
using Minigames.StatueDressup.DragDrop;
using UI.DragAndDrop.Core;
using UnityEngine;
using UnityEngine.UI;
@@ -15,21 +14,21 @@ namespace Minigames.StatueDressup.Controllers
public class DecorationMenuController : ManagedBehaviour
{
[Header("References")]
[SerializeField] private DecorationItem itemPrefab;
[SerializeField] private DecorationGridIcon iconPrefab;
[SerializeField] private DecorationDraggableInstance draggablePrefab;
[SerializeField] private Transform itemsContainer;
[SerializeField] private Transform draggableContainer; // Parent for spawned draggables
[SerializeField] private Button nextPageButton;
[SerializeField] private Button previousPageButton;
[SerializeField] private RectTransform statueArea; // For overlap detection
[SerializeField] private Transform statueParent; // Parent for placed decorations
[SerializeField] private StatueDecorationController statueController; // Controller for registration
[SerializeField] private Image statueOutline; // Outline image shown during drag to indicate valid drop area
[Header("Layout")]
[SerializeField] private GridLayoutGroup gridLayout;
private int _currentPage;
private int _totalPages;
private List<DecorationItem> _spawnedItems = new List<DecorationItem>();
private Dictionary<DecorationItem, DecorationData> _itemDataMapping = new Dictionary<DecorationItem, DecorationData>();
private List<DecorationGridIcon> _spawnedIcons = new List<DecorationGridIcon>();
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
// Properties
@@ -65,6 +64,12 @@ namespace Minigames.StatueDressup.Controllers
return;
}
// Ensure outline starts hidden
if (statueOutline != null)
{
statueOutline.gameObject.SetActive(false);
}
var allDecorations = _settings.AllDecorations;
int itemsPerPage = _settings.ItemsPerPage;
@@ -101,7 +106,7 @@ namespace Minigames.StatueDressup.Controllers
}
/// <summary>
/// Populate the current page with decoration items
/// Populate the current page with decoration icons
/// </summary>
private void PopulateCurrentPage()
{
@@ -118,19 +123,19 @@ namespace Minigames.StatueDressup.Controllers
Logging.Debug($"[DecorationMenuController] Populating page {_currentPage + 1}/{_totalPages}");
// Clear existing items
ClearItems();
// Clear existing icons
ClearIcons();
// 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}");
Logging.Debug($"[DecorationMenuController] Spawning icons {startIndex} to {endIndex - 1}");
// Spawn items for this page
// Spawn icons for this page
for (int i = startIndex; i < endIndex; i++)
{
SpawnDecorationItem(allDecorations[i]);
SpawnGridIcon(allDecorations[i]);
}
// Update button states
@@ -138,99 +143,126 @@ namespace Minigames.StatueDressup.Controllers
}
/// <summary>
/// Spawn a decoration item in the menu
/// Spawn a grid icon in the menu
/// </summary>
private void SpawnDecorationItem(DecorationData data)
private void SpawnGridIcon(DecorationData data)
{
if (itemPrefab == null || itemsContainer == null)
if (iconPrefab == null || itemsContainer == null)
{
Logging.Warning("[DecorationMenuController] Missing prefab or container");
Logging.Warning("[DecorationMenuController] Missing icon prefab or container");
return;
}
DecorationItem item = Instantiate(itemPrefab, itemsContainer);
item.SetDecorationData(data);
DecorationGridIcon icon = Instantiate(iconPrefab, itemsContainer);
icon.Initialize(data, this);
// Set statue references for overlap detection
item.SetStatueArea(statueArea);
item.SetStatueParent(statueParent);
item.SetMenuParent(itemsContainer);
item.SetController(statueController);
_spawnedIcons.Add(icon);
// 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}");
Logging.Debug($"[DecorationMenuController] Spawned icon: {data.DecorationName}");
}
/// <summary>
/// Handle item picked up from menu
/// Factory method: Spawn a draggable instance at cursor position
/// Called by DecorationGridIcon when drag starts
/// </summary>
private void HandleItemPickedUp(DraggableObject draggable)
public DecorationDraggableInstance SpawnDraggableInstance(DecorationData data, Vector3 screenPosition)
{
if (draggable is DecorationItem item && _itemDataMapping.ContainsKey(item))
if (draggablePrefab == null || statueController == null)
{
Logging.Debug($"[DecorationMenuController] Item picked up: {item.Data?.DecorationName}");
Logging.Warning("[DecorationMenuController] Missing draggable prefab or statue controller");
return null;
}
// Show statue outline
ShowStatueOutline();
// Determine parent - use draggableContainer if set, otherwise itemsContainer's parent
Transform parent = draggableContainer != null ? draggableContainer : itemsContainer.parent;
// Spawn draggable instance
DecorationDraggableInstance instance = Instantiate(draggablePrefab, parent);
// Get outline RectTransform for overlap detection
RectTransform outlineRect = statueOutline != null ? statueOutline.rectTransform : null;
// Initialize with references
instance.Initialize(
data,
outlineRect,
statueController.StatueParent,
statueController,
_settings,
OnDraggableFinished
);
// Position at cursor (in local space)
Canvas canvas = GetComponentInParent<Canvas>();
if (canvas != null)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform,
screenPosition,
canvas.worldCamera,
out Vector2 localPoint);
// 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}, isPlacedOnStatue={item.IsPlacedOnStatue}");
// If item was placed on statue, spawn replacement in menu
if (item.IsPlacedOnStatue && !item.IsInMenu)
RectTransform instanceRect = instance.GetComponent<RectTransform>();
if (instanceRect != null)
{
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);
instanceRect.localPosition = localPoint;
}
}
_spawnedItems.Clear();
_itemDataMapping.Clear();
Logging.Debug($"[DecorationMenuController] Spawned draggable instance: {data.DecorationName}");
return instance;
}
/// <summary>
/// Show the statue outline to indicate valid drop area
/// </summary>
private void ShowStatueOutline()
{
if (statueOutline != null)
{
statueOutline.gameObject.SetActive(true);
Logging.Debug("[DecorationMenuController] Statue outline shown");
}
}
/// <summary>
/// Hide the statue outline after drag ends
/// </summary>
private void HideStatueOutline()
{
if (statueOutline != null)
{
statueOutline.gameObject.SetActive(false);
Logging.Debug("[DecorationMenuController] Statue outline hidden");
}
}
/// <summary>
/// Callback from DecorationDraggableInstance when drag finishes
/// </summary>
private void OnDraggableFinished()
{
HideStatueOutline();
}
/// <summary>
/// Clear all spawned icons
/// </summary>
private void ClearIcons()
{
foreach (var icon in _spawnedIcons)
{
if (icon != null)
{
Destroy(icon.gameObject);
}
}
_spawnedIcons.Clear();
}
/// <summary>
@@ -293,8 +325,8 @@ namespace Minigames.StatueDressup.Controllers
previousPageButton.onClick.RemoveListener(OnPreviousPage);
}
// Cleanup item listeners
ClearItems();
// Cleanup icons
ClearIcons();
}
}
}

View File

@@ -27,10 +27,13 @@ namespace Minigames.StatueDressup.Controllers
[SerializeField] private RectTransform photoArea; // Area to capture
[SerializeField] private string photoSaveKey = "MrCementStatuePhoto";
private List<DecorationItem> _placedDecorations = new List<DecorationItem>();
private List<DecorationDraggableInstance> _placedDecorations = new List<DecorationDraggableInstance>();
private bool _minigameCompleted;
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
// Public property for menu controller
public Transform StatueParent => statueParent;
/// <summary>
/// Early initialization - get settings reference
/// </summary>
@@ -72,7 +75,7 @@ namespace Minigames.StatueDressup.Controllers
/// <summary>
/// Register a decoration as placed on statue
/// </summary>
public void RegisterDecoration(DecorationItem decoration)
public void RegisterDecoration(DecorationDraggableInstance decoration)
{
if (decoration != null && !_placedDecorations.Contains(decoration))
{
@@ -87,7 +90,7 @@ namespace Minigames.StatueDressup.Controllers
/// <summary>
/// Unregister a decoration (when removed)
/// </summary>
public void UnregisterDecoration(DecorationItem decoration)
public void UnregisterDecoration(DecorationDraggableInstance decoration)
{
if (decoration != null && _placedDecorations.Contains(decoration))
{

View File

@@ -19,9 +19,6 @@ namespace Minigames.StatueDressup.Data
[Tooltip("Full size when placed on statue (actual sprite size)")]
[SerializeField] private Vector2 authoredSize = new Vector2(128f, 128f);
[Tooltip("Small size in menu icon")]
[SerializeField] private Vector2 iconSize = new Vector2(64f, 64f);
[Header("Progression (Optional)")]
[SerializeField] private bool isUnlocked = true;
@@ -30,7 +27,6 @@ namespace Minigames.StatueDressup.Data
public string DecorationName => decorationName;
public Sprite DecorationSprite => decorationSprite;
public Vector2 AuthoredSize => authoredSize;
public Vector2 IconSize => iconSize;
public bool IsUnlocked => isUnlocked;
private void OnValidate()

View File

@@ -0,0 +1,242 @@
using Core;
using Minigames.StatueDressup.Controllers;
using Minigames.StatueDressup.Data;
using Minigames.StatueDressup.Utils;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Minigames.StatueDressup.DragDrop
{
/// <summary>
/// Draggable instance of a decoration that can be placed on the statue.
/// Created dynamically when dragging from menu or picking up from statue.
/// Destroyed if dropped outside statue area.
/// </summary>
public class DecorationDraggableInstance : MonoBehaviour
{
[Header("References")]
[SerializeField] private Image decorationImage;
[SerializeField] private CanvasGroup canvasGroup;
private DecorationData _decorationData;
private RectTransform _rectTransform;
private Canvas _canvas;
private RectTransform _statueOutline;
private Transform _statueParent;
private StatueDecorationController _controller;
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
private System.Action _onFinishedCallback;
private bool _isDragging;
private bool _isPlacedOnStatue;
private Vector3 _dragOffset;
// Properties
public DecorationData Data => _decorationData;
public bool IsPlacedOnStatue => _isPlacedOnStatue;
private void Awake()
{
_rectTransform = GetComponent<RectTransform>();
_canvas = GetComponentInParent<Canvas>();
if (canvasGroup == null)
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
}
/// <summary>
/// Initialize the draggable instance
/// </summary>
public void Initialize(DecorationData data, RectTransform statueOutline, Transform statueParent,
StatueDecorationController controller, AppleHills.Core.Settings.IStatueDressupSettings settings,
System.Action onFinishedCallback)
{
_decorationData = data;
_statueOutline = statueOutline;
_statueParent = statueParent;
_controller = controller;
_settings = settings;
_onFinishedCallback = onFinishedCallback;
// 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;
}
Logging.Debug($"[DecorationDraggableInstance] Initialized: {data?.DecorationName}");
}
/// <summary>
/// Start dragging from icon
/// </summary>
public void StartDragFromIcon(PointerEventData eventData)
{
_isDragging = true;
// Calculate offset from cursor to object center
RectTransformUtility.ScreenPointToLocalPointInRectangle(
_canvas.transform as RectTransform,
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
_dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
Logging.Debug($"[DecorationDraggableInstance] Started drag from icon: {_decorationData?.DecorationName}");
}
/// <summary>
/// Continue dragging
/// </summary>
public void ContinueDrag(PointerEventData eventData)
{
if (!_isDragging) return;
// Update position to follow cursor
RectTransformUtility.ScreenPointToLocalPointInRectangle(
_canvas.transform as RectTransform,
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
_rectTransform.localPosition = localPoint + (Vector2)_dragOffset;
}
/// <summary>
/// End drag - check placement
/// </summary>
public void EndDrag(PointerEventData eventData)
{
_isDragging = false;
Logging.Debug($"[DecorationDraggableInstance] Drag ended: {_decorationData?.DecorationName}");
// Check if overlapping with statue
if (IsOverlappingStatue())
{
PlaceOnStatue();
}
else
{
PlayPopOutAndDestroy();
}
}
/// <summary>
/// Check if item overlaps with statue outline
/// </summary>
private bool IsOverlappingStatue()
{
if (_statueOutline == null || _rectTransform == null)
{
Logging.Warning($"[DecorationDraggableInstance] Cannot check overlap - statueOutline or RectTransform is null");
return false;
}
// Get bounds of this item in world space
Rect itemRect = GetWorldRect(_rectTransform);
Rect outlineRect = GetWorldRect(_statueOutline);
// Check for any overlap
bool overlaps = itemRect.Overlaps(outlineRect);
Logging.Debug($"[DecorationDraggableInstance] Overlap check: {_decorationData?.DecorationName}, overlaps={overlaps}");
return overlaps;
}
/// <summary>
/// Get world space rect for a RectTransform
/// </summary>
private Rect GetWorldRect(RectTransform rectTransform)
{
Vector3[] corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
Vector3 bottomLeft = corners[0];
Vector3 topRight = corners[2];
return new Rect(bottomLeft.x, bottomLeft.y, topRight.x - bottomLeft.x, topRight.y - bottomLeft.y);
}
/// <summary>
/// Place item on statue at current position
/// </summary>
private void PlaceOnStatue()
{
Logging.Debug($"[DecorationDraggableInstance] Placing on statue: {_decorationData?.DecorationName}");
_isPlacedOnStatue = true;
// Move to statue parent if specified
if (_statueParent != null && transform.parent != _statueParent)
{
transform.SetParent(_statueParent, true); // Keep world position
}
// Register with controller
if (_controller != null)
{
_controller.RegisterDecoration(this);
}
// Notify menu controller to hide outline
_onFinishedCallback?.Invoke();
}
/// <summary>
/// Play pop-out animation and destroy
/// </summary>
private void PlayPopOutAndDestroy()
{
Logging.Debug($"[DecorationDraggableInstance] Pop-out and destroy: {_decorationData?.DecorationName}");
// Notify menu controller to hide outline immediately
_onFinishedCallback?.Invoke();
float duration = _settings?.PlacementAnimationDuration ?? 0.3f;
// Play pop-out with fade animation
TweenAnimationUtility.PopOutWithFade(transform, canvasGroup, duration, () =>
{
Destroy(gameObject);
});
}
/// <summary>
/// Allow picking up from statue for repositioning
/// </summary>
public void StartDragFromStatue(Vector3 pointerPosition)
{
if (_controller != null)
{
_controller.UnregisterDecoration(this);
}
_isPlacedOnStatue = false;
_isDragging = true;
// Calculate offset
RectTransformUtility.ScreenPointToLocalPointInRectangle(
_canvas.transform as RectTransform,
pointerPosition,
null,
out Vector2 localPoint);
_dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
Logging.Debug($"[DecorationDraggableInstance] Started drag from statue: {_decorationData?.DecorationName}");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4659fd035c74a79af0311de9e17f44a
timeCreated: 1763991638

View File

@@ -0,0 +1,100 @@
using Core;
using Minigames.StatueDressup.Data;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Minigames.StatueDressup.DragDrop
{
/// <summary>
/// Static grid icon for decorations in the menu.
/// Handles tap and drag initiation, but doesn't move itself.
/// Spawns a draggable instance when drag starts.
/// </summary>
public class DecorationGridIcon : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
[Header("References")]
[SerializeField] private Image iconImage;
[SerializeField] private DecorationData decorationData;
private Controllers.DecorationMenuController _menuController;
private DecorationDraggableInstance _activeDraggableInstance;
// Properties
public DecorationData Data => decorationData;
/// <summary>
/// Initialize the icon with decoration data
/// </summary>
public void Initialize(DecorationData data, Controllers.DecorationMenuController controller)
{
decorationData = data;
_menuController = controller;
if (iconImage != null && data != null && data.DecorationSprite != null)
{
iconImage.sprite = data.DecorationSprite;
}
}
/// <summary>
/// Handle tap/click on icon
/// </summary>
public void OnPointerClick(PointerEventData eventData)
{
// Only process clicks if we're not dragging
if (_activeDraggableInstance == null)
{
Logging.Debug($"[DecorationGridIcon] Item tapped: {decorationData?.DecorationName}");
// Future: Open detail view, preview, etc.
}
}
/// <summary>
/// Handle drag start - spawn draggable instance
/// </summary>
public void OnBeginDrag(PointerEventData eventData)
{
if (_menuController == null || decorationData == null)
{
Logging.Warning("[DecorationGridIcon] Cannot start drag - missing controller or data");
return;
}
Logging.Debug($"[DecorationGridIcon] Starting drag for: {decorationData.DecorationName}");
// Spawn draggable instance at cursor position
_activeDraggableInstance = _menuController.SpawnDraggableInstance(decorationData, eventData.position);
// Start the drag on the spawned instance
if (_activeDraggableInstance != null)
{
_activeDraggableInstance.StartDragFromIcon(eventData);
}
}
/// <summary>
/// Forward drag events to the active draggable instance
/// </summary>
public void OnDrag(PointerEventData eventData)
{
if (_activeDraggableInstance != null)
{
_activeDraggableInstance.ContinueDrag(eventData);
}
}
/// <summary>
/// Forward drag end to the active draggable instance
/// </summary>
public void OnEndDrag(PointerEventData eventData)
{
if (_activeDraggableInstance != null)
{
_activeDraggableInstance.EndDrag(eventData);
_activeDraggableInstance = null;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9c806d80a321498c9f33f13d7a31065c
timeCreated: 1763991611

View File

@@ -1,288 +0,0 @@
using Core;
using Minigames.StatueDressup.Controllers;
using Minigames.StatueDressup.Data;
using Minigames.StatueDressup.Utils;
using UI.DragAndDrop.Core;
using UnityEngine;
using UnityEngine.UI;
namespace Minigames.StatueDressup.DragDrop
{
/// <summary>
/// Individual decoration item that can be dragged from menu to statue
/// Uses overlap detection instead of slot-based placement
/// </summary>
public class DecorationItem : DraggableObject
{
[Header("Decoration Data")]
[SerializeField] private DecorationData decorationData;
[SerializeField] private Image decorationImage;
[Header("Placement")]
[SerializeField] private RectTransform statueArea; // Reference to statue area for overlap check
private Vector2 _iconSize;
private Vector2 _authoredSize;
private Vector2 _originalMenuPosition;
private Vector2 _placedPosition; // Position when placed on statue
private bool _isInMenu = true;
private bool _isPlacedOnStatue = false;
private Transform _menuParent; // Original parent in menu
private Transform _statueParent; // Parent when placed on statue
private StatueDecorationController _controller; // Controller for registration
private AppleHills.Core.Settings.IStatueDressupSettings _settings; // Settings reference
// Properties
public DecorationData Data => decorationData;
public bool IsInMenu => _isInMenu;
public bool IsPlacedOnStatue => _isPlacedOnStatue;
protected override void Initialize()
{
base.Initialize();
// Get settings
_settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IStatueDressupSettings>();
// Store menu parent
_menuParent = transform.parent;
// Find statue parent (will be set by controller)
// statueParent will be assigned externally
if (decorationData != null)
{
_iconSize = decorationData.IconSize;
_authoredSize = decorationData.AuthoredSize;
// Set initial icon size
if (RectTransform != null)
{
RectTransform.sizeDelta = _iconSize;
}
// Set sprite
if (decorationImage != null && decorationData.DecorationSprite != null)
{
decorationImage.sprite = decorationData.DecorationSprite;
}
}
// Store original menu position
if (RectTransform != null)
{
_originalMenuPosition = RectTransform.anchoredPosition;
}
}
/// <summary>
/// Set decoration data (for spawned instances)
/// </summary>
public void SetDecorationData(DecorationData data)
{
decorationData = data;
if (data != null)
{
_iconSize = data.IconSize;
_authoredSize = data.AuthoredSize;
// Update visual
if (decorationImage != null && data.DecorationSprite != null)
{
decorationImage.sprite = data.DecorationSprite;
}
// Set icon size
if (RectTransform != null)
{
RectTransform.sizeDelta = _iconSize;
}
Logging.Debug($"[DecorationItem] Set data: {data.DecorationName}, iconSize={_iconSize}, authoredSize={_authoredSize}");
}
}
/// <summary>
/// Set statue area reference for overlap detection
/// </summary>
public void SetStatueArea(RectTransform statue)
{
statueArea = statue;
}
/// <summary>
/// Set statue parent for placing items
/// </summary>
public void SetStatueParent(Transform parent)
{
_statueParent = parent;
}
/// <summary>
/// Set controller for registration callbacks
/// </summary>
public void SetController(StatueDecorationController controller)
{
_controller = controller;
}
protected override void OnDragStartedHook()
{
Logging.Debug($"[DecorationItem] OnDragStarted: {decorationData?.DecorationName}");
// If picking up from statue, allow re-positioning
if (_isPlacedOnStatue)
{
_isPlacedOnStatue = false;
Logging.Debug($"[DecorationItem] Picking up from statue for re-positioning");
}
// Scale to authored size when dragging starts
if (RectTransform != null)
{
// Smoothly transition from icon size to authored size
RectTransform.sizeDelta = _authoredSize;
float duration = _settings?.DragScaleTransitionDuration ?? 0.2f;
TweenAnimationUtility.AnimateScale(transform, Vector3.one, duration);
}
}
protected override void OnDragEndedHook()
{
Logging.Debug($"[DecorationItem] OnDragEnded: {decorationData?.DecorationName}");
// Check if overlapping with statue
if (IsOverlappingStatue())
{
PlaceOnStatue();
}
else
{
ReturnToMenu();
}
}
/// <summary>
/// Check if item overlaps with statue area
/// </summary>
private bool IsOverlappingStatue()
{
if (statueArea == null || RectTransform == null)
{
Logging.Warning($"[DecorationItem] Cannot check overlap - statueArea or RectTransform is null");
return false;
}
// Get bounds of this item in world space
Rect itemRect = GetWorldRect(RectTransform);
Rect statueRect = GetWorldRect(statueArea);
// Check for any overlap
bool overlaps = itemRect.Overlaps(statueRect);
Logging.Debug($"[DecorationItem] Overlap check: {decorationData?.DecorationName}, overlaps={overlaps}");
Logging.Debug($"[DecorationItem] Item rect: {itemRect}, Statue rect: {statueRect}");
return overlaps;
}
/// <summary>
/// Get world space rect for a RectTransform
/// </summary>
private Rect GetWorldRect(RectTransform rectTransform)
{
Vector3[] corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
Vector3 bottomLeft = corners[0];
Vector3 topRight = corners[2];
return new Rect(bottomLeft.x, bottomLeft.y, topRight.x - bottomLeft.x, topRight.y - bottomLeft.y);
}
/// <summary>
/// Place item on statue at current position
/// </summary>
private void PlaceOnStatue()
{
Logging.Debug($"[DecorationItem] Placing on statue: {decorationData?.DecorationName}");
_isInMenu = false;
_isPlacedOnStatue = true;
// Store current position
if (RectTransform != null)
{
_placedPosition = RectTransform.anchoredPosition;
}
// Move to statue parent if specified
if (_statueParent != null && transform.parent != _statueParent)
{
transform.SetParent(_statueParent, true); // Keep world position
}
// Register with controller
if (_controller != null)
{
_controller.RegisterDecoration(this);
}
// Keep authored size
// Position is already set by drag
}
/// <summary>
/// Return item to menu with animation
/// </summary>
private void ReturnToMenu()
{
Logging.Debug($"[DecorationItem] Returning to menu: {decorationData?.DecorationName}");
// Unregister from controller if was placed on statue
if (_isPlacedOnStatue && _controller != null)
{
_controller.UnregisterDecoration(this);
}
_isInMenu = true;
_isPlacedOnStatue = false;
// Return to menu parent
if (_menuParent != null && transform.parent != _menuParent)
{
transform.SetParent(_menuParent, false); // Use local positioning
}
if (RectTransform != null)
{
// Animate back to icon size
RectTransform.sizeDelta = _iconSize;
float scaleDuration = _settings?.DragScaleTransitionDuration ?? 0.2f;
TweenAnimationUtility.AnimateScale(transform, Vector3.one, scaleDuration);
// Animate back to original position
float returnDuration = _settings?.ReturnToMenuDuration ?? 0.3f;
TweenAnimationUtility.AnimateAnchoredPosition(RectTransform, _originalMenuPosition, returnDuration);
}
}
/// <summary>
/// Set original menu position (called by menu controller)
/// </summary>
public void SetOriginalMenuPosition(Vector2 position)
{
_originalMenuPosition = position;
}
/// <summary>
/// Set menu parent (called by menu controller)
/// </summary>
public void SetMenuParent(Transform parent)
{
_menuParent = parent;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 31a82dde0ffb439e86b79499b9daa92b
timeCreated: 1763745531

View File

@@ -1,110 +0,0 @@
using Core;
using Minigames.StatueDressup.Data;
using Minigames.StatueDressup.Utils;
using UI.DragAndDrop.Core;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Minigames.StatueDressup.DragDrop
{
/// <summary>
/// Slot on the statue where decorations can be placed
/// </summary>
public class StatueDecorationSlot : DraggableSlot, IPointerEnterHandler, IPointerExitHandler
{
[Header("Slot Configuration")]
[SerializeField] private bool isPermanent = true; // Can't remove once placed
[Header("Glow Effect")]
[SerializeField] private GameObject glowEffect;
[SerializeField] private float glowPulseAmount = 1.1f;
[SerializeField] private float glowPulseDuration = 0.8f;
private bool _isGlowing;
private Pixelplacement.TweenSystem.TweenBase _glowTween;
public bool IsPermanent => isPermanent;
private void Start()
{
// Hide glow effect initially
if (glowEffect != null)
{
glowEffect.SetActive(false);
}
}
public new void OnPointerEnter(PointerEventData eventData)
{
// Only glow when dragging a matching decoration
if (eventData.pointerDrag != null)
{
var decoration = eventData.pointerDrag.GetComponent<DecorationItem>();
if (decoration != null && !IsOccupied)
{
StartGlow();
}
}
}
public new void OnPointerExit(PointerEventData eventData)
{
StopGlow();
}
/// <summary>
/// Start glow effect
/// </summary>
private void StartGlow()
{
if (_isGlowing || glowEffect == null)
return;
_isGlowing = true;
glowEffect.SetActive(true);
Logging.Debug($"[StatueDecorationSlot] Starting glow on {name}");
// Pulse animation
_glowTween = TweenAnimationUtility.StartGlowPulse(glowEffect.transform, glowPulseAmount, glowPulseDuration);
}
/// <summary>
/// Stop glow effect
/// </summary>
private void StopGlow()
{
if (!_isGlowing || glowEffect == null)
return;
_isGlowing = false;
Logging.Debug($"[StatueDecorationSlot] Stopping glow on {name}");
// Stop pulse animation
if (_glowTween != null)
{
TweenAnimationUtility.StopTweens(glowEffect.transform);
_glowTween = null;
}
glowEffect.SetActive(false);
}
/// <summary>
/// Override to check category matching (uses base CanAccept)
/// </summary>
public new bool CanAccept(DraggableObject draggable)
{
// First check base conditions
return base.CanAccept(draggable);
}
private void OnDisable()
{
// Clean up glow on disable
StopGlow();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f68e3749518141b6bc818938dd8dc57d
timeCreated: 1763745550

View File

@@ -145,6 +145,26 @@ namespace Minigames.StatueDressup.Utils
return Tween.CanvasGroupAlpha(canvasGroup, targetAlpha, duration, 0f, Tween.EaseInOut, completeCallback: onComplete);
}
/// <summary>
/// Pop-out with fade - scale to 0 and fade out simultaneously
/// </summary>
public static void PopOutWithFade(Transform transform, CanvasGroup canvasGroup, float duration, Action onComplete = null)
{
// Scale to 0
Tween.LocalScale(transform, Vector3.zero, duration, 0f, Tween.EaseInBack);
// Fade out simultaneously
if (canvasGroup != null)
{
Tween.CanvasGroupAlpha(canvasGroup, 0f, duration, 0f, Tween.EaseInOut, completeCallback: onComplete);
}
else
{
// If no canvas group, just call complete after scale
Tween.LocalScale(transform, Vector3.zero, duration, 0f, Tween.EaseInBack, completeCallback: onComplete);
}
}
#endregion
}
}