Merge a card refresh (#59)

- **Refactored Card Placement Flow**
  - Separated card presentation from orchestration logic
  - Extracted `CornerCardManager` for pending card lifecycle (spawn, shuffle, rebuild)
  - Extracted `AlbumNavigationService` for book page navigation and zone mapping
  - Extracted `CardEnlargeController` for backdrop management and card reparenting
  - Implemented controller pattern (non-MonoBehaviour) for complex logic
  - Cards now unparent from slots before rebuild to prevent premature destruction

- **Improved Corner Card Display**
  - Fixed cards spawning on top of each other during rebuild
  - Implemented shuffle-to-front logic (remaining cards occupy slots 0→1→2)
  - Added smart card selection (prioritizes cards matching current album page)
  - Pending cards now removed from queue immediately on drag start
  - Corner cards rebuild after each placement with proper slot reassignment

- **Enhanced Album Card Viewing**
  - Added dramatic scale increase when viewing cards from album slots
  - Implemented shrink animation when dismissing enlarged cards
  - Cards transition: `PlacedInSlotState` → `AlbumEnlargedState` → `PlacedInSlotState`
  - Backdrop shows/hides with card enlarge/shrink cycle
  - Cards reparent to enlarged container while viewing, return to slot after

- **State Machine Improvements**
  - Added `CardStateNames` constants for type-safe state transitions
  - Implemented `ICardClickHandler` and `ICardStateDragHandler` interfaces
  - State transitions now use cached property indices
  - `BoosterCardContext` separated from `CardContext` for single responsibility

- **Code Cleanup**
  - Identified unused `SlotContainerHelper.cs` (superseded by `CornerCardManager`)
  - Identified unused `BoosterPackDraggable.canOpenOnDrop` field
  - Identified unused `AlbumViewPage._previousInputMode` (hardcoded value)
  - Identified unused `Card.OnPlacedInAlbumSlot` event (no subscribers)

Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com>
Reviewed-on: #59
This commit is contained in:
2025-11-18 08:40:59 +00:00
parent 06cc3bde3b
commit 235fa04eba
161 changed files with 18057 additions and 5743 deletions

View File

@@ -243,6 +243,10 @@ namespace UI.DragAndDrop.Core
public virtual void OnDrag(PointerEventData eventData)
{
// Just ship all this shit when disabled
if (!_isDraggingEnabled)
return;
if (!_isDragging)
return;
@@ -269,6 +273,9 @@ namespace UI.DragAndDrop.Core
public virtual void OnEndDrag(PointerEventData eventData)
{
if (!_isDraggingEnabled)
return;
if (!_isDragging)
return;
@@ -282,14 +289,7 @@ namespace UI.DragAndDrop.Core
if (_canvasGroup != null)
_canvasGroup.blocksRaycasts = true;
// Find closest slot and snap
FindAndSnapToSlot();
// Snap base rotation back to slot rotation (if in a slot)
if (_currentSlot != null)
{
Tween.Rotation(transform, _currentSlot.transform.rotation, 0.3f, 0f, Tween.EaseOutBack);
}
// No auto-slotting - derived classes handle placement logic via OnDragEndedHook()
OnDragEnded?.Invoke(this);
OnDragEndedHook();
@@ -344,70 +344,8 @@ namespace UI.DragAndDrop.Core
#region Slot Management
protected virtual void FindAndSnapToSlot()
{
SlotContainer[] containers = FindObjectsByType<SlotContainer>(FindObjectsSortMode.None);
DraggableSlot closestSlot = null;
float closestDistance = float.MaxValue;
// Use RectTransform.position for overlay, transform.position for others
Vector3 myPosition = (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay && RectTransform != null)
? RectTransform.position
: transform.position;
foreach (var container in containers)
{
DraggableSlot slot = container.FindClosestSlot(myPosition, this);
if (slot != null)
{
Vector3 slotPosition = slot.RectTransform != null ? slot.RectTransform.position : slot.transform.position;
float distance = Vector3.Distance(myPosition, slotPosition);
if (distance < closestDistance)
{
closestDistance = distance;
closestSlot = slot;
}
}
}
if (closestSlot != null)
{
// Check if slot is occupied
if (closestSlot.IsOccupied && closestSlot.Occupant != this)
{
// Swap with occupant
SwapWithSlot(closestSlot);
}
else
{
// Move to empty slot
AssignToSlot(closestSlot, true);
}
}
else if (_currentSlot != null)
{
// Return to current slot if no valid slot found
SnapToCurrentSlot();
}
}
protected virtual void SwapWithSlot(DraggableSlot targetSlot)
{
DraggableSlot mySlot = _currentSlot;
DraggableObject otherObject = targetSlot.Occupant;
if (otherObject != null)
{
// Both objects swap slots
targetSlot.Vacate();
if (mySlot != null)
mySlot.Vacate();
AssignToSlot(targetSlot, true);
if (mySlot != null)
otherObject.AssignToSlot(mySlot, true);
}
}
// Auto-slotting removed - derived classes (Card, etc.) handle placement via state machines
// AssignToSlot() and SnapToSlot() kept for explicit slot assignment
public virtual void AssignToSlot(DraggableSlot slot, bool animate)
{
@@ -461,14 +399,6 @@ namespace UI.DragAndDrop.Core
}
}
protected virtual void SnapToCurrentSlot()
{
if (_currentSlot != null)
{
SnapToSlot(_currentSlot);
}
}
#endregion
#region Selection