This commit is contained in:
DamianCorazza
2025-11-06 23:43:52 +01:00
29 changed files with 3885 additions and 818 deletions

View File

@@ -14,7 +14,37 @@ MonoBehaviour:
m_EditorClassIdentifier: Unity.Addressables.Editor::UnityEditor.AddressableAssets.Settings.AddressableAssetGroup
m_GroupName: BlokkemonCards
m_GUID: 0d5d36d6da388314b92b9c6967d23f39
m_SerializeEntries: []
m_SerializeEntries:
- m_GUID: 0046d7c3ed6b85245af4ce4144b60dfb
m_Address: Assets/Data/Cards/Card_New Card 1.asset
m_ReadOnly: 0
m_SerializedLabels:
- BlokkemonCard
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 1114248d35c978848be75d204ba67c84
m_Address: Assets/Data/Cards/Card_New Card.asset
m_ReadOnly: 0
m_SerializedLabels:
- BlokkemonCard
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 91031de62f795884e8e2ccbaebeebf9b
m_Address: Assets/Data/Cards/Card_Marmormormor.asset
m_ReadOnly: 0
m_SerializedLabels:
- BlokkemonCard
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: ac75dd9d27a925f4c90bbc3b255820e2
m_Address: Assets/Data/Cards/Card_Brosten.asset
m_ReadOnly: 0
m_SerializedLabels:
- BlokkemonCard
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: e2d5a81487e00e2489357c877fa484db
m_Address: Assets/Data/Cards/Card_New Card.asset
m_ReadOnly: 0
m_SerializedLabels:
- BlokkemonCard
FlaggedDuringContentUpdateRestriction: 0
m_ReadOnly: 0
m_Settings: {fileID: 11400000, guid: 11da9bb90d9dd5848b4f7629415a6937, type: 2}
m_SchemaSet:

View File

@@ -0,0 +1,23 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2a80cc88c9884512b8b633110d838780, type: 3}
m_Name: Card_AnnaLise
m_EditorClassIdentifier: AppleHillsScripts::AppleHills.Data.CardSystem.CardDefinition
Id: aac7e84e-43ff-4c51-bee2-5cfd6fdc526b
Name: AnnaLise
UseCustomFileName: 0
CustomFileName:
Description: Card description
Rarity: 1
Zone: 0
CardImage: {fileID: 2133529702, guid: 99d4c3083e9c24142bc20deaeaf95720, type: 3}
CollectionIndex: 2

View File

@@ -1,7 +1,8 @@
fileFormatVersion: 2
guid: 8c268bc0f4efc034db35b348994f2a52
TextScriptImporter:
guid: 1114248d35c978848be75d204ba67c84
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2a80cc88c9884512b8b633110d838780, type: 3}
m_Name: Card_NewCard
m_EditorClassIdentifier: AppleHillsScripts::AppleHills.Data.CardSystem.CardDefinition
Id: 3d07b51f-5b22-44d8-bc34-688b3e6c1516
Name: New Card
UseCustomFileName: 0
CustomFileName:
Description: Card description
Rarity: 0
Zone: 0
CardImage: {fileID: 0}
CollectionIndex: 3

View File

@@ -1,7 +1,8 @@
fileFormatVersion: 2
guid: 341eafcd16915de49acdfe16b2ffaba4
TextScriptImporter:
guid: e2d5a81487e00e2489357c877fa484db
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2a80cc88c9884512b8b633110d838780, type: 3}
m_Name: Card_Pulver
m_EditorClassIdentifier: AppleHillsScripts::AppleHills.Data.CardSystem.CardDefinition
Id: 96ac041f-3632-41a6-89b4-de1a9c80bed4
Name: Pulver
UseCustomFileName: 0
CustomFileName:
Description: Card description
Rarity: 2
Zone: 0
CardImage: {fileID: -46950309, guid: 8af0253099943e84e960c630bafbbdb4, type: 3}
CollectionIndex: 4

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0046d7c3ed6b85245af4ce4144b60dfb
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using AppleHills.Data.CardSystem;
using AppleHills.Editor.Utilities;
using UI.CardSystem;
using UnityEditor;
using UnityEngine;
@@ -17,7 +18,7 @@ namespace Editor.CardSystem
// Paths
private const string CardDefinitionsPath = "Assets/Data/Cards";
private const string MenuPath = "AppleHills/Card Editor";
private const string CardUIPrefabPath = "Assets/Prefabs/UI/Cards/Card.prefab";
private const string CardUIPrefabPath = "Assets/Prefabs/UI/CardsSystem/Cards/Card.prefab";
private const string CardVisualConfigPath = CardDefinitionsPath + "/CardVisualConfig.asset";
// Preview settings
@@ -501,6 +502,17 @@ namespace Editor.CardSystem
AssetDatabase.CreateAsset(newCard, path);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
// Add to Addressables group "BlokkemonCards" and apply "BlokkemonCard" label
if (AddressablesUtility.EnsureAssetInGroupWithLabel(path, "BlokkemonCards", "BlokkemonCard"))
{
AddressablesUtility.SaveAddressableAssets();
Debug.Log($"[CardEditorWindow] Added new card to Addressables with 'BlokkemonCard' label");
}
else
{
Debug.LogWarning("[CardEditorWindow] Failed to add new card to Addressables. Please ensure Addressables are set up in this project.");
}
LoadCardDefinitions();
SelectCard(newCard);
@@ -524,6 +536,7 @@ namespace Editor.CardSystem
string desiredFileName = _selectedCard.UseCustomFileName && !string.IsNullOrEmpty(_selectedCard.CustomFileName)
? _selectedCard.CustomFileName
: _selectedCard.Name;
string finalPath = oldPath;
if (!string.IsNullOrEmpty(desiredFileName))
{
@@ -547,12 +560,26 @@ namespace Editor.CardSystem
Debug.LogError($"[CardEditorWindow] Failed to rename asset: {error}");
}
else
finalPath = uniquePath;
{
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
// Add to Addressables group "BlokkemonCards" and apply "BlokkemonCard" label
if (!string.IsNullOrEmpty(finalPath))
{
if (AddressablesUtility.EnsureAssetInGroupWithLabel(finalPath, "BlokkemonCards", "BlokkemonCard"))
{
AddressablesUtility.SaveAddressableAssets();
Debug.Log($"[CardEditorWindow] Added {_selectedCard.Name} to Addressables with 'BlokkemonCard' label");
}
else
{
Debug.LogError("[CardEditorWindow] Failed to add card to Addressables. Please ensure Addressables are set up in this project.");
}
}
_isDirty = false;

View File

@@ -0,0 +1,264 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using Data.CardSystem;
using AppleHills.Data.CardSystem;
using System.Collections.Generic;
using System.Linq;
namespace AppleHills.Editor
{
/// <summary>
/// Live preview window for the Card System. Shows real-time collection status.
/// </summary>
public class CardSystemLivePreview : EditorWindow
{
private Vector2 scrollPosition;
private bool isSubscribed = false;
private double lastUpdateTime = 0;
private const double UPDATE_INTERVAL = 1.0; // Poll every 1 second as backup
// Cache for display
private Dictionary<CardRarity, List<CardData>> cardsByRarity = new Dictionary<CardRarity, List<CardData>>();
private int totalCards = 0;
private int totalUniqueCards = 0;
private int boosterCount = 0;
private string lastEventMessage = "";
[MenuItem("Tools/Card System/Live Collection Preview")]
public static void ShowWindow()
{
CardSystemLivePreview window = GetWindow<CardSystemLivePreview>("Card Collection Live");
window.minSize = new Vector2(400, 300);
window.Show();
}
private void OnEnable()
{
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
if (Application.isPlaying)
{
SubscribeToEvents();
RefreshData();
}
}
private void OnDisable()
{
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
UnsubscribeFromEvents();
}
private void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.EnteredPlayMode)
{
SubscribeToEvents();
RefreshData();
}
else if (state == PlayModeStateChange.ExitingPlayMode)
{
UnsubscribeFromEvents();
}
}
private void SubscribeToEvents()
{
if (isSubscribed || !Application.isPlaying) return;
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
CardSystemManager.Instance.OnCardRarityUpgraded += OnCardRarityUpgraded;
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
isSubscribed = true;
Debug.Log("[CardSystemLivePreview] Subscribed to CardSystemManager events");
}
}
private void UnsubscribeFromEvents()
{
if (!isSubscribed) return;
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
CardSystemManager.Instance.OnCardRarityUpgraded -= OnCardRarityUpgraded;
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
}
isSubscribed = false;
}
// Event Handlers
private void OnBoosterOpened(List<CardData> cards)
{
lastEventMessage = $"Booster opened! Drew {cards.Count} cards";
RefreshData();
Repaint();
}
private void OnCardCollected(CardData card)
{
lastEventMessage = $"New card collected: {card.Name} ({card.Rarity})";
RefreshData();
Repaint();
}
private void OnCardRarityUpgraded(CardData card)
{
lastEventMessage = $"Card upgraded: {card.Name} → {card.Rarity}!";
RefreshData();
Repaint();
}
private void OnBoosterCountChanged(int count)
{
boosterCount = count;
lastEventMessage = $"Booster count changed: {count}";
Repaint();
}
private void RefreshData()
{
if (!Application.isPlaying || CardSystemManager.Instance == null) return;
var allCards = CardSystemManager.Instance.GetAllCollectedCards();
// Group by rarity
cardsByRarity.Clear();
cardsByRarity[CardRarity.Normal] = allCards.Where(c => c.Rarity == CardRarity.Normal).ToList();
cardsByRarity[CardRarity.Rare] = allCards.Where(c => c.Rarity == CardRarity.Rare).ToList();
cardsByRarity[CardRarity.Legendary] = allCards.Where(c => c.Rarity == CardRarity.Legendary).ToList();
totalCards = allCards.Sum(c => c.CopiesOwned);
totalUniqueCards = allCards.Count;
boosterCount = CardSystemManager.Instance.GetBoosterPackCount();
}
private void Update()
{
if (!Application.isPlaying) return;
// Poll every second as backup (in case events are missed)
if (EditorApplication.timeSinceStartup - lastUpdateTime > UPDATE_INTERVAL)
{
lastUpdateTime = EditorApplication.timeSinceStartup;
if (!isSubscribed)
{
SubscribeToEvents();
}
RefreshData();
Repaint();
}
}
private void OnGUI()
{
if (!Application.isPlaying)
{
EditorGUILayout.HelpBox("Enter Play Mode to view live collection data.", MessageType.Info);
return;
}
if (CardSystemManager.Instance == null)
{
EditorGUILayout.HelpBox("CardSystemManager instance not found!", MessageType.Warning);
return;
}
// Header
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("Live Collection Preview", EditorStyles.boldLabel);
EditorGUILayout.Space();
// Summary Stats
EditorGUILayout.LabelField("Total Unique Cards:", totalUniqueCards.ToString());
EditorGUILayout.LabelField("Total Cards Owned:", totalCards.ToString());
EditorGUILayout.LabelField("Booster Packs:", boosterCount.ToString());
if (!string.IsNullOrEmpty(lastEventMessage))
{
EditorGUILayout.Space();
EditorGUILayout.HelpBox(lastEventMessage, MessageType.Info);
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
// Collection by Rarity
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
DrawRaritySection(CardRarity.Legendary, Color.yellow);
DrawRaritySection(CardRarity.Rare, new Color(0.5f, 0.5f, 1f)); // Light blue
DrawRaritySection(CardRarity.Normal, Color.white);
EditorGUILayout.EndScrollView();
EditorGUILayout.Space();
// Actions
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Refresh Now"))
{
RefreshData();
}
if (GUILayout.Button("Clear Event Log"))
{
lastEventMessage = "";
}
EditorGUILayout.EndHorizontal();
}
private void DrawRaritySection(CardRarity rarity, Color color)
{
if (!cardsByRarity.ContainsKey(rarity) || cardsByRarity[rarity].Count == 0)
return;
var cards = cardsByRarity[rarity];
int totalCopies = cards.Sum(c => c.CopiesOwned);
EditorGUILayout.Space();
// Header
var oldColor = GUI.backgroundColor;
GUI.backgroundColor = color;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUI.backgroundColor = oldColor;
GUILayout.Label($"{rarity} ({cards.Count} unique, {totalCopies} total)", EditorStyles.boldLabel);
// Cards
foreach (var card in cards.OrderBy(c => c.Name))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(card.Name, GUILayout.Width(200));
EditorGUILayout.LabelField($"x{card.CopiesOwned}", GUILayout.Width(50));
EditorGUILayout.LabelField(card.Zone.ToString(), GUILayout.Width(100));
// Progress to next tier (if not Legendary)
if (rarity < CardRarity.Legendary)
{
int copiesNeeded = 5;
float progress = Mathf.Clamp01((float)card.CopiesOwned / copiesNeeded);
Rect progressRect = GUILayoutUtility.GetRect(100, 18);
EditorGUI.ProgressBar(progressRect, progress, $"{card.CopiesOwned}/{copiesNeeded}");
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8c45a4455c1440069cd9c1ee815f32e0
timeCreated: 1762464117

View File

@@ -63,7 +63,7 @@ Material:
- _WeightNormal: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _FaceColor: {r: 0.0038706942, g: 0.4528302, b: 0, a: 1}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
m_BuildTextureStacks: []

View File

@@ -11,6 +11,7 @@ GameObject:
- component: {fileID: 5228380266581535650}
- component: {fileID: 7671014600744692184}
- component: {fileID: 4981820558408988033}
- component: {fileID: 4448843358972162772}
m_Layer: 0
m_Name: BoosterOpeningPage
m_TagString: Untagged
@@ -35,7 +36,6 @@ RectTransform:
- {fileID: 2081116343754364062}
- {fileID: 4830022034953347571}
- {fileID: 7968396929263690413}
- {fileID: 6421996619962684991}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -71,17 +71,69 @@ MonoBehaviour:
transitionDuration: 0.3
canvasGroup: {fileID: 7671014600744692184}
closeButton: {fileID: 9195578165816196696}
boosterPackInstances:
- {fileID: 705840642806816058}
- {fileID: 2278032343563919548}
- {fileID: 5022511008917093447}
boosterPackPrefab: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
bottomRightSlots: {fileID: 415627682025321105}
centerOpeningSlot: {fileID: 3371630871680769077}
cardDisplayContainer: {fileID: 4830022034953347571}
flippableCardPrefab: {fileID: 9060030918047515996, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
cardSpacing: 50
cardSpacing: 500
cardRevealDelay: 0.5
boosterDisappearDuration: 0.5
impulseSource: {fileID: 4448843358972162772}
openingParticleSystem: {fileID: 0}
--- !u!114 &4448843358972162772
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 498445838423597154}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 180ecf9b41d478f468eb3e9083753217, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Cinemachine::Unity.Cinemachine.CinemachineImpulseSource
ImpulseDefinition:
ImpulseChannel: -1
ImpulseShape: 2
CustomImpulseShape:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
ImpulseDuration: 0.2
ImpulseType: 0
DissipationRate: 0.25
RawSignal: {fileID: 0}
AmplitudeGain: 1
FrequencyGain: 1
RepeatMode: 0
Randomize: 1
TimeEnvelope:
AttackShape:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
DecayShape:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
AttackTime: 0
SustainTime: 0.2
DecayTime: 0.7
ScaleWithImpact: 1
HoldForever: 0
ImpactRadius: 100
DirectionMode: 0
DissipationMode: 2
DissipationDistance: 100
PropagationSpeed: 343
DefaultVelocity: {x: -0.2, y: -1, z: 0}
--- !u!1 &2154569789549533728
GameObject:
m_ObjectHideFlags: 0
@@ -193,10 +245,10 @@ RectTransform:
m_Children: []
m_Father: {fileID: 5228380266581535650}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 1200, y: 1000}
m_SizeDelta: {x: 0, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &5906478951385585403
GameObject:
@@ -586,10 +638,7 @@ PrefabInstance:
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects:
- targetCorrespondingSourceObject: {fileID: 4310919426181576387, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
insertIndex: -1
addedObject: {fileID: 106385575138511979}
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
--- !u!224 &3723208489285683019 stripped
@@ -597,121 +646,6 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 4310919426181576387, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
m_PrefabInstance: {fileID: 610246570171281800}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &893571048183901601
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 1313588858854015996}
m_Modifications:
- target: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Name
value: BoosterPackPrefab3
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalScale.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalScale.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
--- !u!224 &1670660734182863341 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
m_PrefabInstance: {fileID: 893571048183901601}
m_PrefabAsset: {fileID: 0}
--- !u!1 &2278032343563919548 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
m_PrefabInstance: {fileID: 893571048183901601}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1867557053725785213
PrefabInstance:
m_ObjectHideFlags: 0
@@ -822,10 +756,7 @@ PrefabInstance:
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects:
- targetCorrespondingSourceObject: {fileID: 4310919426181576387, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
insertIndex: -1
addedObject: {fileID: 5548954158011133206}
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
--- !u!224 &2466164304475032254 stripped
@@ -833,113 +764,6 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 4310919426181576387, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
m_PrefabInstance: {fileID: 1867557053725785213}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &1887023816125829159
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 3723208489285683019}
m_Modifications:
- target: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Name
value: BoosterPackPrefab2
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
--- !u!224 &106385575138511979 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
m_PrefabInstance: {fileID: 1887023816125829159}
m_PrefabAsset: {fileID: 0}
--- !u!1 &705840642806816058 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
m_PrefabInstance: {fileID: 1887023816125829159}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &3020147864556123455
PrefabInstance:
m_ObjectHideFlags: 0
@@ -1042,10 +866,7 @@ PrefabInstance:
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects:
- targetCorrespondingSourceObject: {fileID: 4310919426181576387, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
insertIndex: -1
addedObject: {fileID: 1670660734182863341}
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
--- !u!224 &1313588858854015996 stripped
@@ -1053,231 +874,6 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 4310919426181576387, guid: 561f7c561a416e54e9bf1c2af2f3f4ef, type: 3}
m_PrefabInstance: {fileID: 3020147864556123455}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &5916465061944119399
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 5228380266581535650}
m_Modifications:
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalRotation.w
value: 0.7071068
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalRotation.x
value: -0.7071068
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: -90
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2325046327999825244, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_Name
value: VFX_ConfettiBurst_WIP
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: looping
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.size3D
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSize.scalar
value: 10
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSizeY.scalar
value: 10
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSizeZ.scalar
value: 10
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSpeed.scalar
value: 300
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSize.minScalar
value: 20
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSizeY.minScalar
value: 20
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSizeZ.minScalar
value: 20
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.startSpeed.minScalar
value: 200
objectReference: {fileID: 0}
- target: {fileID: 4983040125055815969, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: InitialModule.gravityModifier.scalar
value: 10
objectReference: {fileID: 0}
- target: {fileID: 5980722886308389101, guid: a04075d06f231594292595e90a69cbb1, type: 3}
propertyPath: m_SortingLayer
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: a04075d06f231594292595e90a69cbb1, type: 3}
--- !u!224 &6421996619962684991 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 793761934373733976, guid: a04075d06f231594292595e90a69cbb1, type: 3}
m_PrefabInstance: {fileID: 5916465061944119399}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &6217258165417062746
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 2466164304475032254}
m_Modifications:
- target: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Name
value: BoosterPackPrefab1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalScale.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalScale.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
--- !u!1 &5022511008917093447 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 1439929750438628637, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
m_PrefabInstance: {fileID: 6217258165417062746}
m_PrefabAsset: {fileID: 0}
--- !u!224 &5548954158011133206 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1966378914653314124, guid: cff5eaa9e8cc26a439e7b36345916468, type: 3}
m_PrefabInstance: {fileID: 6217258165417062746}
m_PrefabAsset: {fileID: 0}
--- !u!1001 &8252956905557640428
PrefabInstance:
m_ObjectHideFlags: 0

View File

@@ -96,8 +96,8 @@ MonoBehaviour:
canOpenOnDoubleClick: 0
canTapToOpen: 0
maxTapsToOpen: 3
tapPulseScale: 1.15
tapPulseDuration: 0.2
tapPulseScale: 2
tapPulseDuration: 0.5
openingParticleSystem: {fileID: 0}
--- !u!1 &7174819457781356441
GameObject:

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"MultiThreaded","GpuSkinning":false,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"IL2CPP","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"Android","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.2.6f1","Branch":"6000.2/staging","Changeset":"cc51a95c0300","Date":1758053328},"Dependencies":["com.moolt.packages.net@0.0.3","com.unity.2d.sprite@1.0.0","com.unity.2d.spriteshape@12.0.1","com.unity.addressables@2.7.3","com.unity.addressables.android@1.0.7","com.unity.cinemachine@3.1.4","com.unity.device-simulator.devices@1.0.0","com.unity.feature.2d@2.0.1","com.unity.film-internal-utilities@0.18.4-preview","com.unity.graphtoolkit@0.4.0-exp.2","com.unity.ide.rider@3.0.38","com.unity.ide.visualstudio@2.0.23","com.unity.inputsystem@1.14.2","com.unity.multiplayer.center@1.0.0","com.unity.render-pipelines.universal@17.2.0","com.unity.timeline@1.8.9","com.unity.ugui@2.0.0","com.unity.modules.accessibility@1.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.cloth@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.vr@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.unity.modules.subsystems@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.render-pipelines.core@17.2.0","com.unity.shadergraph@17.2.0","com.unity.render-pipelines.universal-config@17.0.3","com.unity.test-framework@1.6.0","com.unity.ext.nunit@2.0.5","com.unity.2d.animation@12.0.2","com.unity.2d.pixel-perfect@5.1.0","com.unity.2d.psdimporter@11.0.1","com.unity.2d.tilemap@1.0.0","com.unity.2d.tilemap.extras@5.0.1","com.unity.2d.aseprite@2.0.1","com.unity.splines@2.8.2","com.unity.profiling.core@1.0.2","com.unity.scriptablebuildpipeline@2.4.2","com.unity.2d.common@11.0.1","com.unity.mathematics@1.3.2","com.unity.searcher@4.9.3","com.unity.burst@1.8.24","com.unity.collections@2.5.7","com.unity.rendering.light-transport@1.0.1","com.unity.settings-manager@2.1.0","com.unity.nuget.mono-cecil@1.11.5","com.unity.test-framework.performance@3.1.0"],"Results":[]}

View File

@@ -1 +0,0 @@
{"MeasurementCount":-1}

View File

@@ -450309,6 +450309,7 @@ GameObject:
- component: {fileID: 1137411210}
- component: {fileID: 1137411214}
- component: {fileID: 1137411213}
- component: {fileID: 1137411215}
m_Layer: 0
m_Name: CinemachineCamera
m_TagString: Untagged
@@ -450413,6 +450414,30 @@ MonoBehaviour:
RotationDamping: {x: 1, y: 1, z: 1}
QuaternionDamping: 1
FollowOffset: {x: 0, y: 0, z: -10}
--- !u!114 &1137411215
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1137411209}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 00b2d199b96b516448144ab30fb26aed, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Cinemachine::Unity.Cinemachine.CinemachineImpulseListener
ApplyAfter: 2
ChannelMask: 1
Gain: 1
Use2DDistance: 0
UseCameraSpace: 1
SignalCombinationMode: 0
ReactionSettings:
m_SecondaryNoise: {fileID: 0}
AmplitudeGain: 1
FrequencyGain: 1
Duration: 1
m_NoiseOffsets: {x: 0, y: 0, z: 0}
--- !u!1 &1137721150
GameObject:
m_ObjectHideFlags: 0

View File

@@ -9,12 +9,12 @@
"Unity.InputSystem",
"Unity.TextMeshPro",
"OptimizedRope",
"Unity.Cinemachine",
"AudioSourceEvents",
"NewAssembly",
"SkiaSharp.Unity",
"SkiaSharp.Editor",
"SkiaSharp"
"SkiaSharp",
"Unity.Cinemachine"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -11,9 +11,17 @@ namespace AppleHills.Data.CardSystem
[Serializable]
public class CardInventory
{
// Dictionary of collected cards indexed by definition ID
// Dictionary of collected cards indexed by definition ID + rarity (e.g., "Pikachu_Normal", "Pikachu_Rare")
[SerializeField] private Dictionary<string, CardData> collectedCards = new Dictionary<string, CardData>();
/// <summary>
/// Generate a unique key for a card based on definition ID and rarity
/// </summary>
private string GetCardKey(string definitionId, CardRarity rarity)
{
return $"{definitionId}_{rarity}";
}
// Number of unopened booster packs the player has
[SerializeField] private int boosterPackCount;
@@ -96,7 +104,9 @@ namespace AppleHills.Data.CardSystem
{
if (card == null) return;
if (collectedCards.TryGetValue(card.DefinitionId, out CardData existingCard))
string key = GetCardKey(card.DefinitionId, card.Rarity);
if (collectedCards.TryGetValue(key, out CardData existingCard))
{
// Increase copies of existing card
existingCard.CopiesOwned++;
@@ -105,7 +115,7 @@ namespace AppleHills.Data.CardSystem
{
// Add new card to collection
var newCard = new CardData(card);
collectedCards[card.DefinitionId] = newCard;
collectedCards[key] = newCard;
// Add to lookup dictionaries
cardsByZone[newCard.Zone].Add(newCard);
@@ -133,19 +143,21 @@ namespace AppleHills.Data.CardSystem
}
/// <summary>
/// Get a specific card from the collection by definition ID
/// Get a specific card from the collection by definition ID and rarity
/// </summary>
public CardData GetCard(string definitionId)
public CardData GetCard(string definitionId, CardRarity rarity)
{
return collectedCards.TryGetValue(definitionId, out CardData card) ? card : null;
string key = GetCardKey(definitionId, rarity);
return collectedCards.TryGetValue(key, out CardData card) ? card : null;
}
/// <summary>
/// Check if the player has a specific card
/// Check if the player has a specific card at a specific rarity
/// </summary>
public bool HasCard(string definitionId)
public bool HasCard(string definitionId, CardRarity rarity)
{
return collectedCards.ContainsKey(definitionId);
string key = GetCardKey(definitionId, rarity);
return collectedCards.ContainsKey(key);
}
/// <summary>

View File

@@ -1,14 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AppleHills.Data.CardSystem;
using Bootstrap;
using Core;
using Core.SaveLoad;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Data.CardSystem
{
@@ -150,6 +146,7 @@ namespace Data.CardSystem
/// <summary>
/// Opens a booster pack and returns the newly obtained cards
/// NOTE: Cards are NOT added to inventory immediately - they're added after the reveal interaction
/// </summary>
public List<CardData> OpenBoosterPack()
{
@@ -165,11 +162,9 @@ namespace Data.CardSystem
// Draw 3 cards based on rarity distribution
List<CardData> drawnCards = DrawRandomCards(3);
// Add cards to the inventory
foreach (var card in drawnCards)
{
AddCardToInventory(card);
}
// NOTE: Cards are NOT added to inventory here anymore
// They will be added after the player interacts with each revealed card
// This allows us to show new/repeat status before adding to collection
// Notify listeners
OnBoosterOpened?.Invoke(drawnCards);
@@ -177,33 +172,56 @@ namespace Data.CardSystem
Logging.Debug($"[CardSystemManager] Opened a booster pack and obtained {drawnCards.Count} cards. Remaining boosters: {playerInventory.BoosterPackCount}");
return drawnCards;
}
/// <summary>
/// Check if a card is new to the player's collection at the specified rarity
/// </summary>
/// <param name="cardData">The card to check</param>
/// <param name="existingCard">Out parameter - the existing card if found, null otherwise</param>
/// <returns>True if this is a new card at this rarity, false if already owned</returns>
public bool IsCardNew(CardData cardData, out CardData existingCard)
{
if (playerInventory.HasCard(cardData.DefinitionId, cardData.Rarity))
{
existingCard = playerInventory.GetCard(cardData.DefinitionId, cardData.Rarity);
return false;
}
existingCard = null;
return true;
}
/// <summary>
/// Adds a card to the player's inventory after reveal (delayed add)
/// Public wrapper for AddCardToInventory to support delayed inventory updates
/// </summary>
public void AddCardToInventoryDelayed(CardData card)
{
AddCardToInventory(card);
}
/// <summary>
/// Adds a card to the player's inventory, handles duplicates
/// </summary>
private void AddCardToInventory(CardData card)
{
// Check if the player already has this card type (definition)
if (playerInventory.HasCard(card.DefinitionId))
// Check if the player already has this card at this rarity
if (playerInventory.HasCard(card.DefinitionId, card.Rarity))
{
CardData existingCard = playerInventory.GetCard(card.DefinitionId);
CardData existingCard = playerInventory.GetCard(card.DefinitionId, card.Rarity);
existingCard.CopiesOwned++;
// Check if the card can be upgraded
if (existingCard.TryUpgradeRarity())
{
OnCardRarityUpgraded?.Invoke(existingCard);
}
// Note: Upgrades are now handled separately in BoosterOpeningPage
// We don't auto-upgrade here anymore
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}'. Now have {existingCard.CopiesOwned} copies.");
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}' ({card.Rarity}). Now have {existingCard.CopiesOwned} copies.");
}
else
{
// Add new card
// Add new card at this rarity
playerInventory.AddCard(card);
OnCardCollected?.Invoke(card);
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' to collection.");
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' ({card.Rarity}) to collection.");
}
}
@@ -300,11 +318,17 @@ namespace Data.CardSystem
}
/// <summary>
/// Returns whether a specific card definition has been collected
/// Returns whether a specific card definition has been collected (at any rarity)
/// </summary>
public bool IsCardCollected(string definitionId)
{
return playerInventory.HasCard(definitionId);
// Check if the card exists at any rarity
foreach (CardRarity rarity in System.Enum.GetValues(typeof(CardRarity)))
{
if (playerInventory.HasCard(definitionId, rarity))
return true;
}
return false;
}
/// <summary>

View File

@@ -1,4 +1,4 @@
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AppleHills.Data.CardSystem;
@@ -7,6 +7,7 @@ using Pixelplacement;
using UI.Core;
using UI.CardSystem.DragDrop;
using UI.DragAndDrop.Core;
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.UI;
@@ -23,8 +24,8 @@ namespace UI.CardSystem
[SerializeField] private Button closeButton;
[Header("Booster Management")]
[SerializeField] private GameObject[] boosterPackInstances; // Booster prefabs/instances
[SerializeField] private SlotContainer bottomRightSlots; // Holds waiting boosters
[SerializeField] private GameObject boosterPackPrefab; // Prefab to instantiate new boosters
[SerializeField] private SlotContainer bottomRightSlots; // Holds waiting boosters (max 3)
[SerializeField] private DraggableSlot centerOpeningSlot; // Where booster goes to open
[Header("Card Display")]
@@ -35,14 +36,20 @@ namespace UI.CardSystem
[Header("Settings")]
[SerializeField] private float cardRevealDelay = 0.5f;
[SerializeField] private float boosterDisappearDuration = 0.5f;
[SerializeField] private CinemachineImpulseSource impulseSource;
[SerializeField] private ParticleSystem openingParticleSystem;
[SerializeField] private Transform albumIcon; // Target for card fly-away animation
private int _availableBoosterCount;
private BoosterPackDraggable _currentBoosterInCenter;
private List<BoosterPackDraggable> _activeBoostersInSlots = new List<BoosterPackDraggable>();
private List<GameObject> _currentRevealedCards = new List<GameObject>();
private CardData[] _currentCardData;
private int _revealedCardCount;
private int _cardsCompletedInteraction; // Track how many cards finished their new/repeat interaction
private bool _isProcessingOpening;
private const int MAX_VISIBLE_BOOSTERS = 3;
private FlippableCard _currentActiveCard; // The card currently awaiting interaction
private void Awake()
{
// Make sure we have a CanvasGroup for transitions
@@ -72,6 +79,7 @@ namespace UI.CardSystem
if (centerOpeningSlot != null)
{
centerOpeningSlot.OnOccupied -= OnBoosterPlacedInCenter;
centerOpeningSlot.OnVacated -= OnBoosterRemovedFromCenter;
}
// Unsubscribe from booster events
@@ -113,71 +121,38 @@ namespace UI.CardSystem
{
Debug.Log($"[BoosterOpeningPage] InitializeBoosterDisplay called with {_availableBoosterCount} boosters available");
if (boosterPackInstances == null || boosterPackInstances.Length == 0)
if (boosterPackPrefab == null)
{
Debug.LogWarning("BoosterOpeningPage: No booster pack instances assigned!");
Debug.LogWarning("BoosterOpeningPage: No booster pack prefab assigned!");
return;
}
// Calculate how many boosters to show (capped by array size)
int visibleCount = Mathf.Min(_availableBoosterCount, boosterPackInstances.Length);
Debug.Log($"[BoosterOpeningPage] Will show {visibleCount} boosters out of {boosterPackInstances.Length} instances");
// Show/hide boosters and assign to slots
for (int i = 0; i < boosterPackInstances.Length; i++)
if (bottomRightSlots == null || bottomRightSlots.SlotCount == 0)
{
if (boosterPackInstances[i] == null) continue;
bool shouldShow = i < visibleCount;
Debug.Log($"[BoosterOpeningPage] Booster {i} ({boosterPackInstances[i].name}): shouldShow={shouldShow}, position={boosterPackInstances[i].transform.position}");
boosterPackInstances[i].SetActive(shouldShow);
if (shouldShow)
{
// Get the booster draggable component
BoosterPackDraggable booster = boosterPackInstances[i].GetComponent<BoosterPackDraggable>();
if (booster != null)
{
// Reset state
booster.ResetTapCount();
booster.SetTapToOpenEnabled(false); // Disable tap-to-open until in center
// Subscribe to events
booster.OnReadyToOpen += OnBoosterReadyToOpen;
// Assign to bottom-right slot if slots available
if (bottomRightSlots != null && i < bottomRightSlots.SlotCount)
{
DraggableSlot slot = bottomRightSlots.GetSlotAtIndex(i);
if (slot != null)
{
Debug.Log($"[BoosterOpeningPage] Assigning booster {i} to slot {slot.name} at {slot.transform.position}");
booster.AssignToSlot(slot, false);
}
else
{
Debug.LogWarning($"[BoosterOpeningPage] Slot {i} is null in bottomRightSlots!");
}
}
else
{
Debug.LogWarning($"[BoosterOpeningPage] No slot available for booster {i}. bottomRightSlots={bottomRightSlots}, SlotCount={bottomRightSlots?.SlotCount}");
}
}
else
{
Debug.LogWarning($"[BoosterOpeningPage] Booster {i} has no BoosterPackDraggable component!");
}
}
Debug.LogWarning("BoosterOpeningPage: No slots available!");
return;
}
// Clear any existing boosters
_activeBoostersInSlots.Clear();
// Calculate how many boosters to show (max 3, or available count, whichever is lower)
int visibleCount = Mathf.Min(_availableBoosterCount, MAX_VISIBLE_BOOSTERS);
Debug.Log($"[BoosterOpeningPage] Will spawn {visibleCount} boosters");
// Spawn boosters and assign to slots
for (int i = 0; i < visibleCount; i++)
{
SpawnBoosterInSlot(i);
}
// Subscribe to center slot events
if (centerOpeningSlot != null)
{
centerOpeningSlot.OnOccupied += OnBoosterPlacedInCenter;
Debug.Log($"[BoosterOpeningPage] Subscribed to center slot {centerOpeningSlot.name} at {centerOpeningSlot.transform.position}");
centerOpeningSlot.OnVacated += OnBoosterRemovedFromCenter;
Debug.Log($"[BoosterOpeningPage] Subscribed to center slot events");
}
else
{
@@ -185,6 +160,137 @@ namespace UI.CardSystem
}
}
/// <summary>
/// Spawn a new booster and place it in the specified slot index
/// </summary>
private void SpawnBoosterInSlot(int slotIndex)
{
if (slotIndex >= bottomRightSlots.SlotCount)
{
Debug.LogWarning($"[BoosterOpeningPage] Slot index {slotIndex} out of range!");
return;
}
DraggableSlot slot = bottomRightSlots.GetSlotAtIndex(slotIndex);
if (slot == null)
{
Debug.LogWarning($"[BoosterOpeningPage] Slot {slotIndex} is null!");
return;
}
// Instantiate booster
GameObject boosterObj = Instantiate(boosterPackPrefab, slot.transform);
BoosterPackDraggable booster = boosterObj.GetComponent<BoosterPackDraggable>();
if (booster != null)
{
// Reset state
booster.ResetTapCount();
booster.SetTapToOpenEnabled(false);
// Subscribe to events
booster.OnReadyToOpen += OnBoosterReadyToOpen;
// Assign to slot with animation
booster.AssignToSlot(slot, true);
// Track it
_activeBoostersInSlots.Add(booster);
Debug.Log($"[BoosterOpeningPage] Spawned booster in slot {slotIndex}");
}
else
{
Debug.LogWarning($"[BoosterOpeningPage] Spawned booster has no BoosterPackDraggable component!");
Destroy(boosterObj);
}
}
/// <summary>
/// Remove and destroy the booster from the specified slot
/// </summary>
private void RemoveBoosterFromSlot(int slotIndex)
{
if (slotIndex >= _activeBoostersInSlots.Count)
return;
BoosterPackDraggable booster = _activeBoostersInSlots[slotIndex];
if (booster != null)
{
// Unsubscribe from events
booster.OnReadyToOpen -= OnBoosterReadyToOpen;
// Animate out and destroy
Transform boosterTransform = booster.transform;
Tween.LocalScale(boosterTransform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack,
completeCallback: () =>
{
if (booster != null && booster.gameObject != null)
{
Destroy(booster.gameObject);
}
});
// Remove from slot
if (booster.CurrentSlot != null)
{
booster.CurrentSlot.Vacate();
}
Debug.Log($"[BoosterOpeningPage] Removed booster from slot {slotIndex}");
}
_activeBoostersInSlots.RemoveAt(slotIndex);
}
/// <summary>
/// Update visible boosters based on available count
/// </summary>
private void UpdateVisibleBoosters()
{
int targetCount = Mathf.Min(_availableBoosterCount, MAX_VISIBLE_BOOSTERS);
// Remove excess boosters (from the end)
while (_activeBoostersInSlots.Count > targetCount)
{
int lastIndex = _activeBoostersInSlots.Count - 1;
RemoveBoosterFromSlot(lastIndex);
}
Debug.Log($"[BoosterOpeningPage] Updated visible boosters: {_activeBoostersInSlots.Count}/{targetCount}");
}
/// <summary>
/// Try to spawn a new booster to maintain up to 3 visible
/// Pass decrementCount=true when called after placing a booster in center slot
/// (accounts for the booster that will be consumed)
/// </summary>
private void TrySpawnNewBooster(bool decrementCount = false)
{
// Can we spawn more boosters?
if (_activeBoostersInSlots.Count >= MAX_VISIBLE_BOOSTERS)
return; // Already at max
// Use decremented count if this is called after placing in center
// (the booster in center will be consumed, so we check against count - 1)
int effectiveCount = decrementCount ? _availableBoosterCount - 1 : _availableBoosterCount;
if (_activeBoostersInSlots.Count >= effectiveCount)
return; // No more boosters available
// Find first available slot
for (int i = 0; i < MAX_VISIBLE_BOOSTERS; i++)
{
DraggableSlot slot = bottomRightSlots.GetSlotAtIndex(i);
if (slot != null && !slot.IsOccupied)
{
SpawnBoosterInSlot(i);
Debug.Log($"[BoosterOpeningPage] Spawned new booster in slot {i}");
break;
}
}
}
/// <summary>
/// Handle when a booster is placed in the center opening slot
/// </summary>
@@ -195,29 +301,93 @@ namespace UI.CardSystem
_currentBoosterInCenter = booster;
// Lock the slot so it can't be dragged out
centerOpeningSlot.SetLocked(true);
// Remove from active slots list
_activeBoostersInSlots.Remove(booster);
// Lock the slot so it can't be dragged out
Debug.Log($"[BoosterOpeningPage] Locking center slot. IsLocked before: {centerOpeningSlot.IsLocked}");
centerOpeningSlot.SetLocked(true);
Debug.Log($"[BoosterOpeningPage] IsLocked after: {centerOpeningSlot.IsLocked}");
// Configure booster for opening (disables drag, enables tapping, resets tap count)
Debug.Log($"[BoosterOpeningPage] Calling SetInOpeningSlot(true) on booster");
booster.SetInOpeningSlot(true);
// Subscribe to tap events for visual feedback
booster.OnTapped += OnBoosterTapped;
booster.OnReadyToOpen += OnBoosterReadyToOpen;
booster.OnBoosterOpened += OnBoosterOpened;
Debug.Log($"[BoosterOpeningPage] Booster placed in center, ready for {booster.CurrentTapCount} taps");
// Try to spawn a new booster to maintain 3 visible
// Use decrementCount=true because this booster will be consumed
TrySpawnNewBooster(decrementCount: true);
Debug.Log($"[BoosterOpeningPage] Booster placed in center, ready for taps. Active boosters in slots: {_activeBoostersInSlots.Count}");
}
/// <summary>
/// Handle when a booster is removed from the center opening slot
/// </summary>
private void OnBoosterRemovedFromCenter(DraggableObject draggable)
{
BoosterPackDraggable booster = draggable as BoosterPackDraggable;
if (booster == null) return;
// If it's being removed back to a corner slot, add it back to tracking
if (booster.CurrentSlot != null && bottomRightSlots.HasSlot(booster.CurrentSlot))
{
_activeBoostersInSlots.Add(booster);
booster.SetInOpeningSlot(false);
}
_currentBoosterInCenter = null;
Debug.Log($"[BoosterOpeningPage] Booster removed from center");
}
private void OnBoosterTapped(BoosterPackDraggable booster, int currentTaps, int maxTaps)
{
Debug.Log($"[BoosterOpeningPage] Booster tapped: {currentTaps}/{maxTaps}");
// Calculate shake intensity (increases with each tap)
float shakeIntensity = currentTaps / (float)maxTaps;
float shakeAmount = 10f + (shakeIntensity * 30f); // 10 to 40 units
// Fire Cinemachine impulse with random velocity (excluding Z)
if (impulseSource != null)
{
// Generate random velocity vector (X and Y only, Z = 0)
Vector3 randomVelocity = new Vector3(
Random.Range(-1f, 1f),
Random.Range(-1f, 1f),
0f
);
// Normalize to ensure consistent strength
randomVelocity.Normalize();
// Generate the impulse with strength 1 and random velocity
impulseSource.GenerateImpulse(randomVelocity);
}
}
/// <summary>
/// Handle when booster is opened - play particle effects
/// </summary>
private void OnBoosterOpened(BoosterPackDraggable booster)
{
Debug.Log($"[BoosterOpeningPage] Booster opened, playing particle effect");
// TODO: Shake visual feedback
// This would be handled by BoosterPackVisual if we add a shake method
// Reset and play particle system
if (openingParticleSystem != null)
{
// Stop any existing playback
if (openingParticleSystem.isPlaying)
{
openingParticleSystem.Stop();
}
// Clear existing particles
openingParticleSystem.Clear();
// Play the particle system
openingParticleSystem.Play();
}
}
/// <summary>
@@ -262,21 +432,19 @@ namespace UI.CardSystem
// Animate booster disappearing
yield return StartCoroutine(AnimateBoosterDisappear(booster));
// Decrement available count
_availableBoosterCount--;
// Update visible boosters (remove from end if we drop below thresholds)
UpdateVisibleBoosters();
// Show card backs
SpawnCardBacks(_currentCardData.Length);
// Wait for player to reveal all cards
yield return StartCoroutine(WaitForCardReveals());
// Check if more boosters available
_availableBoosterCount--;
if (_availableBoosterCount > 0)
{
// Show next booster
yield return StartCoroutine(ShowNextBooster());
}
else
if (_availableBoosterCount <= 0)
{
// No more boosters, auto-close page
yield return new WaitForSeconds(1f);
@@ -314,7 +482,7 @@ namespace UI.CardSystem
// Destroy the booster
Destroy(booster.gameObject);
_currentBoosterInCenter = null;
// Unlock center slot
centerOpeningSlot.SetLocked(false);
}
@@ -332,6 +500,7 @@ namespace UI.CardSystem
_currentRevealedCards.Clear();
_revealedCardCount = 0;
_cardsCompletedInteraction = 0; // Reset interaction count
// Calculate positions
float totalWidth = (count - 1) * cardSpacing;
@@ -347,15 +516,30 @@ namespace UI.CardSystem
cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0);
}
// Add button to handle reveal on click
Button cardButton = cardObj.GetComponent<Button>();
if (cardButton == null)
// Get FlippableCard component and setup the card data
FlippableCard flippableCard = cardObj.GetComponent<FlippableCard>();
if (flippableCard != null)
{
cardButton = cardObj.AddComponent<Button>();
// Setup the card data (stored but not revealed yet)
flippableCard.SetupCard(_currentCardData[i]);
// Subscribe to flip started event (to disable other cards IMMEDIATELY)
int cardIndex = i; // Capture for closure
flippableCard.OnFlipStarted += OnCardFlipStarted;
// Subscribe to reveal event to track when flipped
flippableCard.OnCardRevealed += (card, data) => OnCardRevealed(cardIndex);
// Subscribe to inactive click event (for jiggle effect)
flippableCard.OnClickedWhileInactive += OnCardClickedWhileInactive;
// Initially, all cards are clickable (for flipping)
flippableCard.SetClickable(true);
}
else
{
Debug.LogWarning($"[BoosterOpeningPage] FlippableCard component not found on card {i}!");
}
int cardIndex = i; // Capture for closure
cardButton.onClick.AddListener(() => OnCardClicked(cardIndex, cardObj));
_currentRevealedCards.Add(cardObj);
@@ -366,56 +550,206 @@ namespace UI.CardSystem
}
/// <summary>
/// Handle card click to reveal
/// Handle when a card flip starts (disable all other cards IMMEDIATELY)
/// </summary>
private void OnCardClicked(int cardIndex, GameObject cardObj)
private void OnCardFlipStarted(FlippableCard flippingCard)
{
if (cardIndex >= _currentCardData.Length) return;
Debug.Log($"[BoosterOpeningPage] Card flip started, disabling all other cards.");
// Flip/reveal animation (placeholder - just show card data for now)
CardDisplay cardDisplay = cardObj.GetComponent<CardDisplay>();
if (cardDisplay != null)
// Disable ALL cards immediately to prevent multi-flip
foreach (GameObject cardObj in _currentRevealedCards)
{
cardDisplay.SetupCard(_currentCardData[cardIndex]);
FlippableCard card = cardObj.GetComponent<FlippableCard>();
if (card != null)
{
card.SetClickable(false);
}
}
// Disable button so it can't be clicked again
Button cardButton = cardObj.GetComponent<Button>();
if (cardButton != null)
{
cardButton.interactable = false;
}
// Scale punch animation
Tween.LocalScale(cardObj.transform, Vector3.one * 1.2f, 0.15f, 0f, Tween.EaseOutBack,
completeCallback: () => {
Tween.LocalScale(cardObj.transform, Vector3.one, 0.15f, 0f, Tween.EaseInBack);
});
_revealedCardCount++;
}
/// <summary>
/// Wait until all cards are revealed
/// Handle card reveal (when flipped)
/// </summary>
private void OnCardRevealed(int cardIndex)
{
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} revealed!");
_revealedCardCount++;
// Get the flippable card and card data
FlippableCard flippableCard = _currentRevealedCards[cardIndex].GetComponent<FlippableCard>();
if (flippableCard == null)
{
Debug.LogWarning($"[BoosterOpeningPage] FlippableCard not found for card {cardIndex}!");
return;
}
CardData cardData = flippableCard.CardData;
// Check if this is a new card using CardSystemManager
bool isNew = Data.CardSystem.CardSystemManager.Instance.IsCardNew(cardData, out CardData existingCard);
if (isNew)
{
Debug.Log($"[BoosterOpeningPage] Card '{cardData.Name}' is NEW!");
flippableCard.ShowAsNew();
}
else
{
// Check if card is already Legendary - if so, skip progress bar and auto-progress
if (existingCard.Rarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
{
Debug.Log($"[BoosterOpeningPage] Card '{cardData.Name}' is LEGENDARY - auto-progressing!");
// Add to inventory immediately and move to next card
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(cardData);
_cardsCompletedInteraction++;
_revealedCardCount++; // This was already incremented earlier, but we need to track completion
EnableUnrevealedCards();
return; // Skip showing the card enlarged
}
int ownedCount = existingCard.CopiesOwned;
Debug.Log($"[BoosterOpeningPage] Card '{cardData.Name}' is a REPEAT! Owned: {ownedCount}");
// Check if this card will trigger an upgrade (ownedCount + 1 >= threshold)
bool willUpgrade = (ownedCount + 1) >= flippableCard.CardsToUpgrade && existingCard.Rarity < AppleHills.Data.CardSystem.CardRarity.Legendary;
if (willUpgrade)
{
Debug.Log($"[BoosterOpeningPage] This card will trigger upgrade! ({ownedCount + 1}/{flippableCard.CardsToUpgrade})");
// Show as repeat - progress bar will fill and auto-trigger upgrade
flippableCard.ShowAsRepeatWithUpgrade(ownedCount, existingCard);
}
else
{
// Normal repeat, no upgrade
flippableCard.ShowAsRepeat(ownedCount);
}
}
// Set this card as the active one (only this card is clickable now)
SetActiveCard(flippableCard);
// Subscribe to tap event to know when interaction is complete
flippableCard.OnCardTappedAfterReveal += (card) => OnCardCompletedInteraction(card, cardIndex);
}
/// <summary>
/// Handle when a card's interaction is complete (tapped after reveal)
/// </summary>
private void OnCardCompletedInteraction(FlippableCard card, int cardIndex)
{
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} interaction complete!");
// Add card to inventory NOW (after player saw it)
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(card.CardData);
// Return card to normal size
card.ReturnToNormalSize();
// Increment completed interaction count
_cardsCompletedInteraction++;
// Clear active card
_currentActiveCard = null;
// Re-enable all unrevealed cards (they can be flipped now)
EnableUnrevealedCards();
Debug.Log($"[BoosterOpeningPage] Cards completed interaction: {_cardsCompletedInteraction}/{_currentCardData.Length}");
}
/// <summary>
/// Set which card is currently active (only this card can be clicked)
/// </summary>
private void SetActiveCard(FlippableCard activeCard)
{
_currentActiveCard = activeCard;
// Disable all other cards
foreach (GameObject cardObj in _currentRevealedCards)
{
FlippableCard card = cardObj.GetComponent<FlippableCard>();
if (card != null)
{
// Only the active card is clickable
card.SetClickable(card == activeCard);
}
}
Debug.Log($"[BoosterOpeningPage] Set active card. Only one card is now clickable.");
}
/// <summary>
/// Re-enable all unrevealed cards (allow them to be flipped)
/// </summary>
private void EnableUnrevealedCards()
{
foreach (GameObject cardObj in _currentRevealedCards)
{
FlippableCard card = cardObj.GetComponent<FlippableCard>();
if (card != null && !card.IsFlipped)
{
card.SetClickable(true);
}
}
Debug.Log($"[BoosterOpeningPage] Re-enabled unrevealed cards for flipping.");
}
/// <summary>
/// Handle when a card is clicked while not active (jiggle the active card)
/// </summary>
private void OnCardClickedWhileInactive(FlippableCard inactiveCard)
{
Debug.Log($"[BoosterOpeningPage] Inactive card clicked, jiggling active card.");
if (_currentActiveCard != null)
{
_currentActiveCard.Jiggle();
}
}
/// <summary>
/// Wait until all cards are revealed AND all interactions are complete
/// </summary>
private IEnumerator WaitForCardReveals()
{
// Wait until all cards are flipped
while (_revealedCardCount < _currentCardData.Length)
{
yield return null;
}
// All cards revealed, wait a moment
yield return new WaitForSeconds(1f);
Debug.Log($"[BoosterOpeningPage] All cards revealed! Waiting for interactions...");
// Clear cards
foreach (GameObject card in _currentRevealedCards)
// Wait until all cards have completed their new/repeat interaction
while (_cardsCompletedInteraction < _currentCardData.Length)
{
if (card != null)
yield return null;
}
Debug.Log($"[BoosterOpeningPage] All interactions complete! Animating cards to album...");
// All cards revealed and interacted with, wait a moment
yield return new WaitForSeconds(0.5f);
// Animate cards to album icon (or center if no icon assigned) with staggered delays
Vector3 targetPosition = albumIcon != null ? albumIcon.position : Vector3.zero;
int cardIndex = 0;
foreach (GameObject cardObj in _currentRevealedCards)
{
if (cardObj != null)
{
// Animate out
Tween.LocalScale(card.transform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack,
completeCallback: () => Destroy(card));
// Stagger each card with 0.5s delay
float delay = cardIndex * 0.5f;
// Animate to album icon position, then destroy
Tween.Position(cardObj.transform, targetPosition, 0.5f, delay, Tween.EaseInBack);
Tween.LocalScale(cardObj.transform, Vector3.zero, 0.5f, delay, Tween.EaseInBack,
completeCallback: () => Destroy(cardObj));
cardIndex++;
}
}
@@ -424,40 +758,6 @@ namespace UI.CardSystem
yield return new WaitForSeconds(0.5f);
}
/// <summary>
/// Show the next booster pack
/// </summary>
private IEnumerator ShowNextBooster()
{
// Find the next inactive booster and activate it
for (int i = 0; i < boosterPackInstances.Length; i++)
{
if (boosterPackInstances[i] != null && !boosterPackInstances[i].activeSelf)
{
boosterPackInstances[i].SetActive(true);
BoosterPackDraggable booster = boosterPackInstances[i].GetComponent<BoosterPackDraggable>();
if (booster != null)
{
booster.ResetTapCount();
booster.SetTapToOpenEnabled(false);
booster.OnReadyToOpen += OnBoosterReadyToOpen;
// Assign to first available slot
DraggableSlot slot = bottomRightSlots?.GetAvailableSlots().FirstOrDefault();
if (slot != null)
{
booster.AssignToSlot(slot, true);
}
}
break;
}
}
yield return null;
}
/// <summary>
/// Clean up the page when hidden
/// </summary>
@@ -465,6 +765,14 @@ namespace UI.CardSystem
{
UnsubscribeFromAllBoosters();
// Destroy all active boosters
foreach (var booster in _activeBoostersInSlots.ToList())
{
if (booster != null && booster.gameObject != null)
Destroy(booster.gameObject);
}
_activeBoostersInSlots.Clear();
// Clear any remaining cards
foreach (GameObject card in _currentRevealedCards)
{
@@ -482,18 +790,24 @@ namespace UI.CardSystem
/// </summary>
private void UnsubscribeFromAllBoosters()
{
if (boosterPackInstances == null) return;
foreach (GameObject boosterObj in boosterPackInstances)
// Unsubscribe from active boosters in slots
foreach (var booster in _activeBoostersInSlots)
{
if (boosterObj == null) continue;
BoosterPackDraggable booster = boosterObj.GetComponent<BoosterPackDraggable>();
if (booster != null)
{
booster.OnReadyToOpen -= OnBoosterReadyToOpen;
booster.OnTapped -= OnBoosterTapped;
booster.OnBoosterOpened -= OnBoosterOpened;
}
}
// Unsubscribe from center booster
if (_currentBoosterInCenter != null)
{
_currentBoosterInCenter.OnReadyToOpen -= OnBoosterReadyToOpen;
_currentBoosterInCenter.OnTapped -= OnBoosterTapped;
_currentBoosterInCenter.OnBoosterOpened -= OnBoosterOpened;
}
}
protected override void DoTransitionIn(System.Action onComplete)

View File

@@ -18,7 +18,6 @@ namespace UI.CardSystem.DragDrop
[SerializeField] private int maxTapsToOpen = 3;
[SerializeField] private float tapPulseScale = 1.15f;
[SerializeField] private float tapPulseDuration = 0.2f;
[SerializeField] private ParticleSystem openingParticleSystem;
// ...existing code...
public event System.Action<BoosterPackDraggable> OnBoosterOpened;
@@ -106,12 +105,6 @@ namespace UI.CardSystem.DragDrop
_isOpening = true;
// Play particle effect
if (openingParticleSystem != null)
{
openingParticleSystem.Play();
}
OnBoosterOpened?.Invoke(this);
// The actual opening logic (calling CardSystemManager) should be handled
@@ -134,16 +127,47 @@ namespace UI.CardSystem.DragDrop
/// </summary>
public void SetInOpeningSlot(bool inSlot)
{
Debug.Log($"[BoosterPackDraggable] SetInOpeningSlot({inSlot}) called on {name}");
SetDraggingEnabled(!inSlot); // Disable dragging when in opening slot
Debug.Log($"[BoosterPackDraggable] SetDraggingEnabled({!inSlot}) called");
canTapToOpen = inSlot; // Enable tap-to-open when in opening slot
if (inSlot)
{
_currentTapCount = 0; // Reset tap counter when placed
// Suppress visual effects (idle animations, glow, etc.) when in opening slot
// But allow slot tween and tap pulse to still work
if (Visual != null)
{
Visual.SuppressEffects();
// Play one-time placement tween to animate into slot
// The visual will follow the parent to its slot position, then lock in place
// Get target scale from current slot if it has scale mode
Vector3 targetScale = Vector3.one;
if (CurrentSlot != null && CurrentSlot.GetComponent<DraggableSlot>() != null)
{
// Access the slot's occupant scale if it's in Scale mode
// For now, use Vector3.one as default
targetScale = Vector3.one;
}
Visual.PlayPlacementTween(0.3f, targetScale);
}
}
else
{
ResetOpeningState(); // Reset completely when removed
// Resume visual effects when removed from opening slot
if (Visual != null)
{
Visual.StopPlacementTween(); // Stop any ongoing placement tween
Visual.ResumeEffects();
}
}
}

View File

@@ -1,4 +1,5 @@
using Pixelplacement;
using Pixelplacement.TweenSystem;
using UI.DragAndDrop.Core;
using UnityEngine;
using UnityEngine.UI;
@@ -24,6 +25,10 @@ namespace UI.CardSystem.DragDrop
private BoosterPackDraggable _boosterDraggable;
// Effect tracking
private int _glowRotationTweenId = -1;
private float _defaultGlowRate = 10f;
public override void Initialize(DraggableObject parent)
{
base.Initialize(parent);
@@ -58,8 +63,8 @@ namespace UI.CardSystem.DragDrop
protected override void UpdateVisualContent()
{
// Update glow rotation for visual interest
if (glowTransform != null)
// Update glow rotation for visual interest (skip if effects suppressed)
if (glowTransform != null && !_effectsSuppressed)
{
glowTransform.Rotate(Vector3.forward * 30f * Time.deltaTime);
}
@@ -77,6 +82,7 @@ namespace UI.CardSystem.DragDrop
/// <summary>
/// Play progressive shake animation based on tap intensity
/// This is always allowed, even when effects are suppressed
/// </summary>
public void PlayShakeAnimation(int intensity, int maxIntensity)
{
@@ -98,17 +104,11 @@ namespace UI.CardSystem.DragDrop
shakeDuration, 0f, Tween.EaseInBack);
});
// Scale punch (gets bigger with each tap)
float punchScale = 1f + (normalizedIntensity * 0.2f);
Tween.LocalScale(transform, Vector3.one * punchScale,
shakeDuration / 2f, 0f, Tween.EaseOutBack,
completeCallback: () => {
Tween.LocalScale(transform, Vector3.one,
shakeDuration / 2f, 0f, Tween.EaseInBack);
});
// NOTE: Scale pulse is handled by BoosterPackDraggable.OnPointerUpHook()
// We don't need a duplicate scale tween here - it would conflict!
// Extra glow burst on final tap
if (intensity == maxIntensity && glowEffect != null)
// Extra glow burst on final tap (only if effects not suppressed)
if (intensity == maxIntensity && glowEffect != null && !_effectsSuppressed)
{
var emission = glowEffect.emission;
emission.rateOverTimeMultiplier = 50f;
@@ -155,8 +155,8 @@ namespace UI.CardSystem.DragDrop
{
base.OnPointerEnterVisual();
// Extra glow when hovering
if (glowEffect != null)
// Extra glow when hovering (skip if effects suppressed)
if (glowEffect != null && !_effectsSuppressed)
{
var emission = glowEffect.emission;
emission.rateOverTimeMultiplier = 20f;
@@ -167,14 +167,147 @@ namespace UI.CardSystem.DragDrop
{
base.OnPointerExitVisual();
// Restore normal glow
if (glowEffect != null)
// Restore normal glow (skip if effects suppressed)
if (glowEffect != null && !_effectsSuppressed)
{
var emission = glowEffect.emission;
emission.rateOverTimeMultiplier = 10f;
emission.rateOverTimeMultiplier = _defaultGlowRate;
}
}
#region Effect Management Overrides
protected override void OnEffectsSuppressed()
{
base.OnEffectsSuppressed();
// Stop glow effect
if (glowEffect != null)
{
glowEffect.Stop();
}
// Cancel glow rotation tween if tracked
if (_glowRotationTweenId != -1)
{
Tween.Stop(_glowRotationTweenId);
_glowRotationTweenId = -1;
}
// Reset glow rotation to zero
if (glowTransform != null)
{
glowTransform.localRotation = Quaternion.identity;
}
}
protected override void OnEffectsResumed()
{
base.OnEffectsResumed();
// Resume glow effect
if (glowEffect != null && !glowEffect.isPlaying)
{
glowEffect.Play();
}
}
protected override void OnEffectsReset()
{
base.OnEffectsReset();
// Stop glow effect
if (glowEffect != null)
{
glowEffect.Stop();
}
// Reset glow rotation
if (glowTransform != null)
{
glowTransform.localRotation = Quaternion.identity;
}
// NOTE: Tap pulse scale is handled by BoosterPackDraggable, not here
}
#endregion
#region Event Handler Overrides (Block when in Opening Slot)
protected override void HandleDragStarted(DraggableObject draggable)
{
// Don't call base if effects are suppressed (in opening slot)
if (_effectsSuppressed)
return;
base.HandleDragStarted(draggable);
}
protected override void HandleDragEnded(DraggableObject draggable)
{
// ALWAYS reset canvas sorting, even when effects are suppressed
if (canvas != null)
{
canvas.overrideSorting = false;
}
// Skip other drag end effects if suppressed (in opening slot)
if (_effectsSuppressed)
return;
base.HandleDragEnded(draggable);
}
protected override void HandlePointerEnter(DraggableObject draggable)
{
// Don't call base if effects are suppressed (in opening slot)
if (_effectsSuppressed)
return;
base.HandlePointerEnter(draggable);
}
protected override void HandlePointerExit(DraggableObject draggable)
{
// Don't call base if effects are suppressed (in opening slot)
if (_effectsSuppressed)
return;
base.HandlePointerExit(draggable);
}
protected override void HandlePointerDown(DraggableObject draggable)
{
// Don't call base if effects are suppressed (in opening slot)
// This allows the BoosterPackDraggable to handle tap pulse itself
if (_effectsSuppressed)
return;
base.HandlePointerDown(draggable);
}
protected override void HandlePointerUp(DraggableObject draggable, bool longPress)
{
// Don't call base if effects are suppressed (in opening slot)
// This allows the BoosterPackDraggable to handle tap pulse itself
if (_effectsSuppressed)
return;
base.HandlePointerUp(draggable, longPress);
}
protected override void HandleSelection(DraggableObject draggable, bool selected)
{
// Don't call base if effects are suppressed (in opening slot)
if (_effectsSuppressed)
return;
base.HandleSelection(draggable, selected);
}
#endregion
protected override void OnDestroy()
{
base.OnDestroy();

View File

@@ -0,0 +1,633 @@
using System;
using AppleHills.Data.CardSystem;
using Pixelplacement;
using Pixelplacement.TweenSystem;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace UI.CardSystem
{
/// <summary>
/// Flippable card wrapper that shows a card back, then flips to reveal the CardDisplay front.
/// This component nests an existing CardDisplay prefab to reuse card visuals everywhere.
/// </summary>
public class FlippableCard : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
[Header("Card References")]
[SerializeField] private GameObject cardBackObject; // The card back visual
[SerializeField] private GameObject cardFrontObject; // Your CardDisplay prefab instance
[SerializeField] private CardDisplay cardDisplay; // Reference to CardDisplay component
[Header("Idle Hover Animation")]
[SerializeField] private bool enableIdleHover = true;
[SerializeField] private float idleHoverHeight = 10f;
[SerializeField] private float idleHoverDuration = 1.5f;
[SerializeField] private float hoverScaleMultiplier = 1.05f;
[Header("Flip Animation")]
[SerializeField] private float flipDuration = 0.6f;
[SerializeField] private float flipScalePunch = 1.1f;
[Header("New/Repeat Card Display")]
[SerializeField] private GameObject newCardText;
[SerializeField] private GameObject newCardIdleText;
[SerializeField] private GameObject repeatText;
[SerializeField] private GameObject progressBarContainer;
[SerializeField] private int cardsToUpgrade = 5;
[SerializeField] private float enlargedScale = 1.5f;
// State
private bool _isFlipped = false;
private bool _isFlipping = false;
private bool _isHovering = false;
private TweenBase _idleHoverTween;
private CardData _cardData;
private Vector2 _originalPosition; // Track original spawn position
private bool _isWaitingForTap = false; // Waiting for tap after reveal
private bool _isNew = false; // Is this a new card
private int _ownedCount = 0; // Owned count for repeat cards
private bool _isClickable = true; // Can this card be clicked
// Events
public event Action<FlippableCard, CardData> OnCardRevealed;
public event Action<FlippableCard> OnCardTappedAfterReveal;
public event Action<FlippableCard> OnClickedWhileInactive; // Fired when clicked but not clickable
public event Action<FlippableCard> OnFlipStarted; // Fired when flip animation begins
public bool IsFlipped => _isFlipped;
public CardData CardData => _cardData;
public int CardsToUpgrade => cardsToUpgrade; // Expose upgrade threshold
private void Awake()
{
// Auto-find CardDisplay if not assigned
if (cardDisplay == null && cardFrontObject != null)
{
cardDisplay = cardFrontObject.GetComponent<CardDisplay>();
}
// Card back: starts at 0° rotation (normal, facing camera, clickable)
// Card front: starts at 180° rotation (flipped away, will rotate to 0° when revealed)
if (cardBackObject != null)
{
cardBackObject.transform.localRotation = Quaternion.Euler(0, 0, 0);
cardBackObject.SetActive(true);
}
if (cardFrontObject != null)
{
cardFrontObject.transform.localRotation = Quaternion.Euler(0, 180, 0);
cardFrontObject.SetActive(false);
}
// Hide all new/repeat UI elements initially
if (newCardText != null)
newCardText.SetActive(false);
if (newCardIdleText != null)
newCardIdleText.SetActive(false);
if (repeatText != null)
repeatText.SetActive(false);
if (progressBarContainer != null)
progressBarContainer.SetActive(false);
}
private void Start()
{
// Save the original position so we can return to it after hover
RectTransform rectTransform = GetComponent<RectTransform>();
if (rectTransform != null)
{
_originalPosition = rectTransform.anchoredPosition;
}
// Start idle hover animation
if (enableIdleHover && !_isFlipped)
{
StartIdleHover();
}
}
/// <summary>
/// Setup the card data (stores it but doesn't reveal until flipped)
/// </summary>
public void SetupCard(CardData data)
{
_cardData = data;
// Setup the CardDisplay but keep it hidden
if (cardDisplay != null)
{
cardDisplay.SetupCard(data);
}
}
/// <summary>
/// Flip the card to reveal the front
/// </summary>
public void FlipToReveal()
{
if (_isFlipped || _isFlipping)
return;
_isFlipping = true;
// Fire flip started event IMMEDIATELY (before animations)
OnFlipStarted?.Invoke(this);
// Stop idle hover
StopIdleHover();
// Flip animation: Rotate the visual children (back from 0→90, front from 180→0)
// ...existing code...
// Card back: 0° → 90° (rotates away)
// Card front: 180° → 90° → 0° (rotates into view)
// Phase 1: Rotate both to 90 degrees (edge view)
if (cardBackObject != null)
{
Tween.LocalRotation(cardBackObject.transform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut);
}
if (cardFrontObject != null)
{
Tween.LocalRotation(cardFrontObject.transform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
completeCallback: () =>
{
// At edge (90°), switch visibility
if (cardBackObject != null)
cardBackObject.SetActive(false);
if (cardFrontObject != null)
cardFrontObject.SetActive(true);
// Phase 2: Rotate front from 90 to 0 (show at correct orientation)
Tween.LocalRotation(cardFrontObject.transform, Quaternion.Euler(0, 0, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
completeCallback: () =>
{
_isFlipped = true;
_isFlipping = false;
// Fire revealed event
OnCardRevealed?.Invoke(this, _cardData);
});
});
}
// Scale punch during flip for extra juice
Vector3 originalScale = transform.localScale;
Tween.LocalScale(transform, originalScale * flipScalePunch, flipDuration * 0.5f, 0f, Tween.EaseOutBack,
completeCallback: () =>
{
Tween.LocalScale(transform, originalScale, flipDuration * 0.5f, 0f, Tween.EaseInBack);
});
}
/// <summary>
/// Start idle hover animation (gentle bobbing)
/// </summary>
private void StartIdleHover()
{
if (_idleHoverTween != null)
return;
RectTransform rectTransform = GetComponent<RectTransform>();
if (rectTransform == null)
return;
Vector2 originalPos = rectTransform.anchoredPosition;
Vector2 targetPos = originalPos + Vector2.up * idleHoverHeight;
_idleHoverTween = Tween.Value(0f, 1f,
(val) =>
{
if (rectTransform != null)
{
float t = Mathf.Sin(val * Mathf.PI * 2f) * 0.5f + 0.5f; // Smooth sine wave
rectTransform.anchoredPosition = Vector2.Lerp(originalPos, targetPos, t);
}
},
idleHoverDuration, 0f, Tween.EaseInOut, Tween.LoopType.Loop);
}
/// <summary>
/// Stop idle hover animation
/// </summary>
private void StopIdleHover()
{
if (_idleHoverTween != null)
{
_idleHoverTween.Stop();
_idleHoverTween = null;
// Reset to ORIGINAL position (not Vector2.zero!)
RectTransform rectTransform = GetComponent<RectTransform>();
if (rectTransform != null)
{
Tween.AnchoredPosition(rectTransform, _originalPosition, 0.3f, 0f, Tween.EaseOutBack);
}
}
}
#region Pointer Event Handlers
public void OnPointerEnter(PointerEventData eventData)
{
if (_isFlipped || _isFlipping)
return;
_isHovering = true;
// Scale up slightly on hover
Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack);
}
public void OnPointerExit(PointerEventData eventData)
{
if (_isFlipped || _isFlipping)
return;
_isHovering = false;
// Scale back to normal
Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack);
}
public void OnPointerClick(PointerEventData eventData)
{
// If not clickable, notify and return
if (!_isClickable)
{
OnClickedWhileInactive?.Invoke(this);
return;
}
// If waiting for tap after reveal, handle that
if (_isWaitingForTap)
{
OnCardTappedAfterReveal?.Invoke(this);
_isWaitingForTap = false;
return;
}
if (_isFlipped || _isFlipping)
return;
// Flip on click
FlipToReveal();
}
#endregion
#region New/Repeat Card Display
/// <summary>
/// Show this card as a new card (enlarge, show "NEW CARD" text, wait for tap)
/// </summary>
public void ShowAsNew()
{
_isNew = true;
_isWaitingForTap = true;
// Show new card text
if (newCardText != null)
newCardText.SetActive(true);
// Enlarge the card
EnlargeCard();
}
/// <summary>
/// Show this card as a repeat that will trigger an upgrade (enlarge, show progress, auto-transition to upgrade)
/// </summary>
/// <param name="ownedCount">Number of copies owned BEFORE this one</param>
/// <param name="lowerRarityCard">The existing card data at lower rarity (for upgrade reference)</param>
public void ShowAsRepeatWithUpgrade(int ownedCount, AppleHills.Data.CardSystem.CardData lowerRarityCard)
{
_isNew = false;
_ownedCount = ownedCount;
_isWaitingForTap = false; // Don't wait yet - upgrade will happen automatically
// Show repeat text
if (repeatText != null)
repeatText.SetActive(true);
// Enlarge the card
EnlargeCard();
// Show progress bar with owned count, then auto-trigger upgrade
ShowProgressBar(ownedCount, () =>
{
// Progress animation complete - trigger upgrade!
TriggerUpgradeTransition(lowerRarityCard);
});
}
/// <summary>
/// Trigger the upgrade transition (called after progress bar fills)
/// </summary>
private void TriggerUpgradeTransition(AppleHills.Data.CardSystem.CardData lowerRarityCard)
{
Debug.Log($"[FlippableCard] Triggering upgrade transition from {lowerRarityCard.Rarity}!");
AppleHills.Data.CardSystem.CardRarity oldRarity = lowerRarityCard.Rarity;
AppleHills.Data.CardSystem.CardRarity newRarity = oldRarity + 1;
// Reset the lower rarity count to 0
lowerRarityCard.CopiesOwned = 0;
// Create upgraded card data
AppleHills.Data.CardSystem.CardData upgradedCardData = new AppleHills.Data.CardSystem.CardData(_cardData);
upgradedCardData.Rarity = newRarity;
upgradedCardData.CopiesOwned = 1;
// Check if we already have this card at the higher rarity
bool isNewAtHigherRarity = Data.CardSystem.CardSystemManager.Instance.IsCardNew(upgradedCardData, out AppleHills.Data.CardSystem.CardData existingHigherRarity);
// Add the higher rarity card to inventory
Data.CardSystem.CardSystemManager.Instance.GetCardInventory().AddCard(upgradedCardData);
// Update our displayed card data
_cardData.Rarity = newRarity;
// Transition to appropriate display
if (isNewAtHigherRarity || newRarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
{
// Show as NEW at higher rarity
TransitionToNewCardView(newRarity);
}
else
{
// Show progress for higher rarity, then transition to NEW
int ownedAtHigherRarity = existingHigherRarity.CopiesOwned;
ShowProgressBar(ownedAtHigherRarity, () =>
{
TransitionToNewCardView(newRarity);
});
}
}
/// <summary>
/// Show this card as a repeat (enlarge, show progress bar, wait for tap)
/// </summary>
/// <param name="ownedCount">Number of copies owned BEFORE this one</param>
public void ShowAsRepeat(int ownedCount)
{
_isNew = false;
_ownedCount = ownedCount;
_isWaitingForTap = true;
// Show repeat text
if (repeatText != null)
repeatText.SetActive(true);
// Enlarge the card
EnlargeCard();
// Show progress bar with owned count, then blink new element
ShowProgressBar(ownedCount, () =>
{
// Progress animation complete
});
}
/// <summary>
/// Show this card as upgraded (hide progress bar, show as new with upgraded rarity)
/// </summary>
public void ShowAsUpgraded(AppleHills.Data.CardSystem.CardRarity oldRarity, AppleHills.Data.CardSystem.CardRarity newRarity)
{
_isNew = true;
_isWaitingForTap = true;
// Update the CardDisplay to show new rarity
if (cardDisplay != null && _cardData != null)
{
_cardData.Rarity = newRarity;
cardDisplay.SetupCard(_cardData);
}
// Hide progress bar and repeat text
if (progressBarContainer != null)
progressBarContainer.SetActive(false);
if (repeatText != null)
repeatText.SetActive(false);
// Show new card text (it's now a "new" card at the higher rarity)
if (newCardText != null)
newCardText.SetActive(true);
Debug.Log($"[FlippableCard] Card upgraded from {oldRarity} to {newRarity}! Showing as NEW.");
// Card is already enlarged from the repeat display, so no need to enlarge again
}
/// <summary>
/// Show this card as upgraded with progress bar (already have copies at higher rarity)
/// </summary>
public void ShowAsUpgradedWithProgress(AppleHills.Data.CardSystem.CardRarity oldRarity, AppleHills.Data.CardSystem.CardRarity newRarity, int ownedAtNewRarity)
{
_isNew = false;
_isWaitingForTap = false; // Don't wait for tap yet, progress bar will complete first
// Hide new card text
if (newCardText != null)
newCardText.SetActive(false);
// Show repeat text (it's a repeat at the new rarity)
if (repeatText != null)
repeatText.SetActive(true);
// Show progress bar for the new rarity
ShowProgressBar(ownedAtNewRarity, () =>
{
// Progress animation complete - now transition to "NEW CARD" view
TransitionToNewCardView(newRarity);
});
Debug.Log($"[FlippableCard] Card upgraded from {oldRarity} to {newRarity}! Showing progress {ownedAtNewRarity}/5");
}
/// <summary>
/// Transition to "NEW CARD" view after upgrade progress completes
/// </summary>
private void TransitionToNewCardView(AppleHills.Data.CardSystem.CardRarity newRarity)
{
Debug.Log($"[FlippableCard] Transitioning to NEW CARD view at {newRarity} rarity");
// Update the CardDisplay to show new rarity
if (cardDisplay != null && _cardData != null)
{
_cardData.Rarity = newRarity;
cardDisplay.SetupCard(_cardData);
}
// Hide progress bar and repeat text
if (progressBarContainer != null)
progressBarContainer.SetActive(false);
if (repeatText != null)
repeatText.SetActive(false);
// Show "NEW CARD" text
if (newCardText != null)
newCardText.SetActive(true);
// Now wait for tap
_isNew = true;
_isWaitingForTap = true;
Debug.Log($"[FlippableCard] Now showing as NEW CARD at {newRarity}, waiting for tap");
}
/// <summary>
/// Enlarge the card
/// </summary>
private void EnlargeCard()
{
Tween.LocalScale(transform, Vector3.one * enlargedScale, 0.3f, 0f, Tween.EaseOutBack);
}
/// <summary>
/// Return card to normal size
/// </summary>
public void ReturnToNormalSize()
{
Tween.LocalScale(transform, Vector3.one, 0.3f, 0f, Tween.EaseOutBack, completeCallback: () =>
{
// After returning to normal, hide new card text, show idle text
if (_isNew)
{
if (newCardText != null)
newCardText.SetActive(false);
if (newCardIdleText != null)
newCardIdleText.SetActive(true);
}
// Keep repeat text visible
});
}
/// <summary>
/// Show progress bar with owned count, then blink the new element
/// </summary>
private void ShowProgressBar(int ownedCount, System.Action onComplete)
{
if (progressBarContainer == null)
{
onComplete?.Invoke();
return;
}
progressBarContainer.SetActive(true);
// Get all child Image components
UnityEngine.UI.Image[] progressElements = progressBarContainer.GetComponentsInChildren<UnityEngine.UI.Image>(true);
// Check if we have the required number of elements (should match cardsToUpgrade)
if (progressElements.Length < cardsToUpgrade)
{
Debug.LogWarning($"[FlippableCard] Not enough Image components in progress bar! Expected {cardsToUpgrade}, found {progressElements.Length}");
onComplete?.Invoke();
return;
}
// Disable all elements first
foreach (var img in progressElements)
{
img.enabled = false;
}
// Show owned count (from the END, going backwards)
// E.g., if owned 3 cards, enable elements at index [4], [3], [2] (last 3 elements)
int startIndex = Mathf.Max(0, cardsToUpgrade - ownedCount);
for (int i = startIndex; i < cardsToUpgrade && i < progressElements.Length; i++)
{
progressElements[i].enabled = true;
}
// Wait a moment, then blink the new element
// New element is at index (cardsToUpgrade - ownedCount - 1)
int newElementIndex = Mathf.Max(0, cardsToUpgrade - ownedCount - 1);
if (newElementIndex >= 0 && newElementIndex < progressElements.Length)
{
Tween.Value(0f, 1f, (val) => { }, 0.3f, 0f, completeCallback: () =>
{
BlinkProgressElement(newElementIndex, progressElements, onComplete);
});
}
else
{
onComplete?.Invoke();
}
}
/// <summary>
/// Blink a progress element (enable/disable rapidly)
/// </summary>
private void BlinkProgressElement(int index, UnityEngine.UI.Image[] elements, System.Action onComplete)
{
if (index < 0 || index >= elements.Length)
{
onComplete?.Invoke();
return;
}
UnityEngine.UI.Image element = elements[index];
int blinkCount = 0;
const int maxBlinks = 3;
void Blink()
{
element.enabled = !element.enabled;
blinkCount++;
if (blinkCount >= maxBlinks * 2)
{
element.enabled = true; // End on enabled
onComplete?.Invoke();
}
else
{
Tween.Value(0f, 1f, (val) => { }, 0.15f, 0f, completeCallback: Blink);
}
}
Blink();
}
/// <summary>
/// Enable or disable clickability of this card
/// </summary>
public void SetClickable(bool clickable)
{
_isClickable = clickable;
}
/// <summary>
/// Jiggle the card (shake animation)
/// </summary>
public void Jiggle()
{
// Quick shake animation - rotate left, then right, then center
Transform cardTransform = transform;
Quaternion originalRotation = cardTransform.localRotation;
// Shake sequence: 0 -> -5 -> +5 -> 0
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 0, -5), 0.05f, 0f, Tween.EaseInOut,
completeCallback: () =>
{
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 0, 5), 0.1f, 0f, Tween.EaseInOut,
completeCallback: () =>
{
Tween.LocalRotation(cardTransform, originalRotation, 0.05f, 0f, Tween.EaseInOut);
});
});
}
#endregion
private void OnDestroy()
{
StopIdleHover();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ffa05ec4ecbd4cc485e2127683c29f09
timeCreated: 1762454507

View File

@@ -190,13 +190,20 @@ namespace UI.DragAndDrop.Core
if (eventData.button != PointerEventData.InputButton.Left)
return;
Debug.Log($"[DraggableObject] OnBeginDrag called on {name}. _isDraggingEnabled={_isDraggingEnabled}");
// Check if dragging is enabled BEFORE setting any state
if (!_isDraggingEnabled)
{
Debug.Log($"[DraggableObject] OnBeginDrag blocked - dragging disabled on {name}");
return;
}
_isDragging = true;
_wasDragged = true;
Debug.Log($"[DraggableObject] Drag started on {name}");
// ...existing code...
if (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay && RectTransform != null)
{
@@ -438,6 +445,11 @@ namespace UI.DragAndDrop.Core
if (RectTransform != null)
{
// Pop-in animation: start slightly scaled down, then bounce to normal
Vector3 startScale = transform.localScale * 0.8f;
transform.localScale = startScale;
Tween.LocalScale(transform, Vector3.one, snapDuration, 0f, Tween.EaseOutBack);
Tween.LocalPosition(RectTransform, targetLocalPos, snapDuration, 0f, Tween.EaseOutBack);
Tween.LocalRotation(transform, Quaternion.identity, snapDuration, 0f, Tween.EaseOutBack);
}
@@ -498,7 +510,26 @@ namespace UI.DragAndDrop.Core
/// </summary>
public virtual void SetDraggingEnabled(bool enabled)
{
Debug.Log($"[DraggableObject] SetDraggingEnabled({enabled}) called on {name}. _isDragging={_isDragging}, _isDraggingEnabled={_isDraggingEnabled}");
_isDraggingEnabled = enabled;
// If disabling dragging while actively dragging, stop the drag
if (!enabled && _isDragging)
{
Debug.Log($"[DraggableObject] Stopping active drag on {name}");
_isDragging = false;
// Re-enable raycasting
if (_raycaster != null)
_raycaster.enabled = true;
if (_imageComponent != null)
_imageComponent.raycastTarget = true;
if (_canvasGroup != null)
_canvasGroup.blocksRaycasts = true;
}
Debug.Log($"[DraggableObject] After SetDraggingEnabled: _isDragging={_isDragging}, _isDraggingEnabled={_isDraggingEnabled}");
}
#endregion

View File

@@ -1,4 +1,5 @@
using Pixelplacement;
using Pixelplacement.TweenSystem;
using UnityEngine;
using UnityEngine.InputSystem; // Added for new Input System
@@ -46,9 +47,17 @@ namespace UI.DragAndDrop.Core
protected int _savedSlotIndex;
protected Vector3 _lastPosition;
// Effect tracking
protected bool _effectsSuppressed;
protected bool _isPlayingPlacementTween; // Tracks if a one-time placement tween is active
protected TweenBase _placementPositionTween;
protected TweenBase _placementScaleTween;
protected TweenBase _activeTweenId; // Track active scale tween for cancellation
// Properties
public DraggableObject ParentDraggable => _parentDraggable;
public bool IsInitialized => _isInitialized;
public bool EffectsSuppressed => _effectsSuppressed;
/// <summary>
/// Initialize the visual with its parent draggable object
@@ -136,11 +145,18 @@ namespace UI.DragAndDrop.Core
{
if (!_isInitialized || _parentDraggable == null)
return;
UpdateFollowPosition();
UpdateFollowRotation(); // Track base rotation changes
UpdateRotation();
UpdateTilt();
// Skip follow position/rotation when effects are suppressed (e.g., in opening slot)
// UNLESS we're currently playing a placement tween (allow it to complete)
// The visual should stay locked in place after placement tween completes
if (!_effectsSuppressed || _isPlayingPlacementTween)
{
UpdateFollowPosition();
UpdateFollowRotation(); // Track base rotation changes
UpdateRotation();
UpdateTilt();
}
UpdateVisualContent();
}
@@ -275,7 +291,10 @@ namespace UI.DragAndDrop.Core
: _parentDraggable.GetSlotIndex();
// Idle animation (sine/cosine wobble with different frequencies)
// Disabled if effects are suppressed
float idleMultiplier = _parentDraggable.IsHovering ? 0.2f : 1f;
idleMultiplier = _effectsSuppressed ? 0f : idleMultiplier;
float time = Time.time * idleAnimationSpeed + _savedSlotIndex;
// Use sine for X wobble, cosine for Y wobble (different phases)
@@ -346,7 +365,10 @@ namespace UI.DragAndDrop.Core
{
if (useScaleAnimations)
{
Tween.LocalScale(transform, Vector3.one * scaleOnDrag, scaleTransitionDuration, 0f, Tween.EaseOutBack);
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one * scaleOnDrag, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
if (canvas != null)
@@ -382,7 +404,11 @@ namespace UI.DragAndDrop.Core
// Only reset scale if NOT in a slot (let slots handle their own scaling)
if (draggable.CurrentSlot == null)
{
Tween.LocalScale(transform, Vector3.one, scaleTransitionDuration, 0f, Tween.EaseOutBack);
// TODO: Fix this repetetive stuff
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
@@ -405,7 +431,10 @@ namespace UI.DragAndDrop.Core
{
if (useScaleAnimations)
{
Tween.LocalScale(transform, Vector3.one * scaleOnHover, scaleTransitionDuration, 0f, Tween.EaseOutBack);
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one * scaleOnHover, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
// Punch rotation effect
@@ -421,7 +450,10 @@ namespace UI.DragAndDrop.Core
{
if (!draggable.WasDragged && useScaleAnimations)
{
Tween.LocalScale(transform, Vector3.one, scaleTransitionDuration, 0f, Tween.EaseOutBack);
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
OnPointerExitVisual();
@@ -431,7 +463,10 @@ namespace UI.DragAndDrop.Core
{
if (useScaleAnimations)
{
Tween.LocalScale(transform, Vector3.one * scaleOnDrag, scaleTransitionDuration, 0f, Tween.EaseOutBack);
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one * scaleOnDrag, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
OnPointerDownVisual();
@@ -443,7 +478,10 @@ namespace UI.DragAndDrop.Core
if (useScaleAnimations)
{
Tween.LocalScale(transform, Vector3.one * targetScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one * targetScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
OnPointerUpVisual(longPress);
@@ -462,7 +500,11 @@ namespace UI.DragAndDrop.Core
if (useScaleAnimations)
{
float targetScale = selected ? scaleOnDrag : scaleOnHover;
Tween.LocalScale(transform, Vector3.one * targetScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
if (_activeTweenId != null)
_activeTweenId.Stop();
_activeTweenId = Tween.LocalScale(transform, Vector3.one * targetScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
}
OnSelectionVisual(selected);
@@ -520,6 +562,146 @@ namespace UI.DragAndDrop.Core
#endregion
#region Effect Management API
/// <summary>
/// Suppress all ongoing visual effects (idle animations, glow, etc.)
/// This does NOT affect tweens initiated externally (like slot placement).
/// Subclasses should override this to stop their specific effects.
/// </summary>
public virtual void SuppressEffects()
{
_effectsSuppressed = true;
// Cancel any active scale tween we're tracking
if (_activeTweenId != null)
{
_activeTweenId.Stop();
_activeTweenId = null;
}
// Cancel ALL tweens on this transform to prevent slot scale tween from conflicting
Tween.Cancel(transform.GetInstanceID());
OnEffectsSuppressed();
}
/// <summary>
/// Resume all visual effects that were suppressed
/// </summary>
public virtual void ResumeEffects()
{
_effectsSuppressed = false;
OnEffectsResumed();
}
/// <summary>
/// Reset all visual effects to their default state
/// </summary>
public virtual void ResetEffects()
{
_effectsSuppressed = false;
// Cancel any active scale tween we're tracking
if (_activeTweenId != null)
{
_activeTweenId.Stop();
_activeTweenId = null;
}
// Reset scale to default
transform.localScale = Vector3.one;
// Reset shake parent rotation
if (shakeParent != null)
{
shakeParent.localRotation = Quaternion.identity;
}
// Reset tilt parent rotation
if (tiltParent != null)
{
tiltParent.localRotation = Quaternion.identity;
}
OnEffectsReset();
}
/// <summary>
/// Play a one-time placement tween to move visual to target position and scale.
/// This works even when effects are suppressed, and allows the visual to follow
/// the parent during the tween, then locks in place after completion.
/// </summary>
/// <param name="duration">Duration of the tween</param>
/// <param name="targetScale">Target scale (if null, uses current parent scale)</param>
public virtual void PlayPlacementTween(float duration = 0.3f, Vector3? targetScale = null)
{
// Cancel any existing placement tweens
StopPlacementTween();
_isPlayingPlacementTween = true;
// The position tween is handled by UpdateFollowPosition naturally
// We just need to wait for the duration, then lock in place
// If target scale provided, tween the scale
if (targetScale.HasValue)
{
_placementScaleTween = Tween.LocalScale(transform, targetScale.Value, duration, 0f, Tween.EaseOutBack);
}
// Use a dummy tween to track completion
_placementPositionTween = Tween.Value(0f, 1f, (val) => { }, duration, 0f, Tween.EaseOutBack,
completeCallback: () =>
{
_isPlayingPlacementTween = false;
_placementPositionTween = null;
OnPlacementTweenComplete();
});
}
/// <summary>
/// Stop any active placement tween and immediately lock in place
/// </summary>
public virtual void StopPlacementTween()
{
if (_placementPositionTween != null)
{
_placementPositionTween.Stop();
_placementPositionTween = null;
}
if (_placementScaleTween != null)
{
_placementScaleTween.Stop();
_placementScaleTween = null;
}
_isPlayingPlacementTween = false;
}
/// <summary>
/// Called when placement tween completes. Override for custom behavior.
/// </summary>
protected virtual void OnPlacementTweenComplete() { }
/// <summary>
/// Called when effects are suppressed. Override to stop specific effects.
/// </summary>
protected virtual void OnEffectsSuppressed() { }
/// <summary>
/// Called when effects are resumed. Override to restart specific effects.
/// </summary>
protected virtual void OnEffectsResumed() { }
/// <summary>
/// Called when effects are reset. Override to reset specific effects.
/// </summary>
protected virtual void OnEffectsReset() { }
#endregion
protected virtual void OnDestroy()
{
UnsubscribeFromParentEvents();

View File

@@ -153,6 +153,14 @@ namespace UI.DragAndDrop.Core
return _slots[index];
}
/// <summary>
/// Check if this container contains the specified slot
/// </summary>
public bool HasSlot(DraggableSlot slot)
{
return _slots.Contains(slot);
}
/// <summary>
/// Update the layout of all slots based on layout type
/// </summary>