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

@@ -169,6 +169,7 @@ namespace Core
var playerSettings = SettingsProvider.Instance.LoadSettingsSynchronous<PlayerFollowerSettings>();
var interactionSettings = SettingsProvider.Instance.LoadSettingsSynchronous<InteractionSettings>();
var minigameSettings = SettingsProvider.Instance.LoadSettingsSynchronous<DivingMinigameSettings>();
var cardSystemSettings = SettingsProvider.Instance.LoadSettingsSynchronous<CardSystemSettings>();
// Register settings with service locator
if (playerSettings != null)
@@ -200,9 +201,19 @@ namespace Core
{
Debug.LogError("Failed to load MinigameSettings");
}
if (cardSystemSettings != null)
{
ServiceLocator.Register<ICardSystemSettings>(cardSystemSettings);
Logging.Debug("CardSystemSettings registered successfully");
}
else
{
Debug.LogError("Failed to load CardSystemSettings");
}
// Log success
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null;
_settingsLoaded = playerSettings != null && interactionSettings != null && minigameSettings != null && cardSystemSettings != null;
if (_settingsLoaded)
{
Logging.Debug("All settings loaded and registered with ServiceLocator");

View File

@@ -82,7 +82,7 @@ namespace Core.SaveLoad
private void Start()
{
// Direct registration - SaveLoadManager guaranteed available (priority 25)
if (SaveLoadManager.Instance != null)
if (SaveLoadManager.Instance != null && ShouldParticipateInSave())
{
SaveLoadManager.Instance.RegisterParticipant(this);
}
@@ -126,6 +126,15 @@ namespace Core.SaveLoad
// Match ManagedBehaviour convention: SceneName/GameObjectName/ComponentType
return $"{sceneName}/{gameObject.name}/AppleMachine";
}
/// <summary>
/// Returns true to participate in save/load system.
/// Override this in derived classes to opt out (return false).
/// </summary>
public virtual bool ShouldParticipateInSave()
{
return true;
}
private string GetSceneName()
{

View File

@@ -29,6 +29,13 @@
/// Used to prevent double-restoration when inactive objects become active.
/// </summary>
bool HasBeenRestored { get; }
/// <summary>
/// Returns true if this participant wants to participate in save/load system.
/// Return false to opt out (participant will not be saved or restored).
/// Useful for transient objects like UI elements that don't need persistence.
/// </summary>
bool ShouldParticipateInSave();
}
}

View File

@@ -130,6 +130,13 @@ namespace Core.SaveLoad
participants[saveId] = participant;
Logging.Debug($"[SaveLoadManager] Registered participant: {saveId}");
// Skip restoration for participants that opt out
if (!participant.ShouldParticipateInSave())
{
Logging.Debug($"[SaveLoadManager] Participant opted out of save/load: {saveId}");
return;
}
// If we have save data loaded and the participant hasn't been restored yet
if (IsSaveDataLoaded && currentSaveData != null && !participant.HasBeenRestored)
{
@@ -446,6 +453,13 @@ namespace Core.SaveLoad
{
string saveId = kvp.Key;
ISaveParticipant participant = kvp.Value;
// Skip participants that opt out of save system
if (!participant.ShouldParticipateInSave())
{
Logging.Debug($"[SaveLoadManager] Skipping participant (opted out): {saveId}");
continue;
}
try
{
@@ -630,6 +644,13 @@ namespace Core.SaveLoad
{
string saveId = kvp.Key;
ISaveParticipant participant = kvp.Value;
// Skip participants that opt out of save system
if (!participant.ShouldParticipateInSave())
{
Logging.Debug($"[SaveLoadManager] Skipping participant (opted out): {saveId}");
continue;
}
try
{

View File

@@ -0,0 +1,102 @@
using UnityEngine;
namespace AppleHills.Core.Settings
{
/// <summary>
/// Settings for the card system - controls animations, interactions, and progression
/// </summary>
[CreateAssetMenu(fileName = "CardSystemSettings", menuName = "AppleHills/Settings/Card System", order = 4)]
public class CardSystemSettings : BaseSettings, ICardSystemSettings
{
[Header("Idle Hover Animations")]
[Tooltip("Height of the idle hover animation in pixels")]
[SerializeField] private float idleHoverHeight = 10f;
[Tooltip("Duration of one complete hover cycle in seconds")]
[SerializeField] private float idleHoverDuration = 1.5f;
[Tooltip("Scale multiplier when hovering over a card (1.05 = 5% larger)")]
[SerializeField] private float hoverScaleMultiplier = 1.05f;
[Header("Flip Animations")]
[Tooltip("Duration of the card flip animation in seconds")]
[SerializeField] private float flipDuration = 0.6f;
[Tooltip("Scale punch amount during flip (1.1 = 10% larger at peak)")]
[SerializeField] private float flipScalePunch = 1.1f;
[Header("Enlarge/Shrink Animations")]
[Tooltip("Scale for new cards when enlarged (1.5 = 150% of normal size)")]
[SerializeField] private float newCardEnlargedScale = 1.5f;
[Tooltip("Scale for album cards when enlarged (4.0 = 400% of normal size - dramatic showcase)")]
[SerializeField] private float albumCardEnlargedScale = 4.0f;
[Tooltip("Duration of scale animations in seconds")]
[SerializeField] private float scaleDuration = 0.3f;
[Header("Drag & Drop")]
[Tooltip("Scale multiplier when dragging a card (1.1 = 10% larger)")]
[SerializeField] private float dragScale = 1.1f;
[Header("Progression System")]
[Tooltip("Number of duplicate cards needed to upgrade rarity")]
[SerializeField] private int cardsToUpgrade = 5;
[Header("General Animation")]
[Tooltip("Default animation duration when not specified in seconds")]
[SerializeField] private float defaultAnimationDuration = 0.3f;
// ICardSystemSettings implementation - Idle Hover Animations
public float IdleHoverHeight => idleHoverHeight;
public float IdleHoverDuration => idleHoverDuration;
public float HoverScaleMultiplier => hoverScaleMultiplier;
// ICardSystemSettings implementation - Flip Animations
public float FlipDuration => flipDuration;
public float FlipScalePunch => flipScalePunch;
// ICardSystemSettings implementation - Enlarge/Shrink Animations
public float NewCardEnlargedScale => newCardEnlargedScale;
public float AlbumCardEnlargedScale => albumCardEnlargedScale;
public float ScaleDuration => scaleDuration;
// ICardSystemSettings implementation - Drag & Drop
public float DragScale => dragScale;
// ICardSystemSettings implementation - Progression System
public int CardsToUpgrade => cardsToUpgrade;
// ICardSystemSettings implementation - General Animation
public float DefaultAnimationDuration => defaultAnimationDuration;
public override void OnValidate()
{
base.OnValidate();
// Validate idle hover animations
idleHoverHeight = Mathf.Max(0f, idleHoverHeight);
idleHoverDuration = Mathf.Max(0.1f, idleHoverDuration);
hoverScaleMultiplier = Mathf.Max(1.0f, hoverScaleMultiplier);
// Validate flip animations
flipDuration = Mathf.Max(0.1f, flipDuration);
flipScalePunch = Mathf.Max(1.0f, flipScalePunch);
// Validate enlarge/shrink animations
newCardEnlargedScale = Mathf.Max(1.0f, newCardEnlargedScale);
albumCardEnlargedScale = Mathf.Max(1.0f, albumCardEnlargedScale);
scaleDuration = Mathf.Max(0.1f, scaleDuration);
// Validate drag & drop
dragScale = Mathf.Max(1.0f, dragScale);
// Validate progression system
cardsToUpgrade = Mathf.Max(1, cardsToUpgrade);
// Validate general animation
defaultAnimationDuration = Mathf.Max(0.1f, defaultAnimationDuration);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ce6f8e26f4e74a9ab16c190529e67638
timeCreated: 1762934668

View File

@@ -128,9 +128,36 @@ namespace AppleHills.Core.Settings
float[] ViewfinderProgressThresholds { get; }
float PaddingFactor { get; }
float MaxSizePercent { get; }
float MinSizePercent { get; }
public float MinSizePercent { get; }
public PhotoInputModes PhotoInputMode { get; }
}
/// <summary>
/// Interface for card system settings
/// </summary>
public interface ICardSystemSettings
{
// Idle Hover Animations
float IdleHoverHeight { get; }
float IdleHoverDuration { get; }
float HoverScaleMultiplier { get; }
// Photo Input Settings
PhotoInputModes PhotoInputMode { get; }
// Flip Animations
float FlipDuration { get; }
float FlipScalePunch { get; }
// Enlarge/Shrink Animations
float NewCardEnlargedScale { get; }
float AlbumCardEnlargedScale { get; }
float ScaleDuration { get; }
// Drag & Drop
float DragScale { get; }
// Progression System
int CardsToUpgrade { get; }
// General Animation
float DefaultAnimationDuration { get; }
}
}