Merge branch 'main' of https://homelab.tailf7f81b.ts.net/tschesky/AppleHillsProduction
This commit is contained in:
@@ -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:
|
||||
|
||||
23
Assets/Data/Cards/Card_AnnaLise.asset
Normal file
23
Assets/Data/Cards/Card_AnnaLise.asset
Normal 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
|
||||
@@ -1,7 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c268bc0f4efc034db35b348994f2a52
|
||||
TextScriptImporter:
|
||||
guid: 1114248d35c978848be75d204ba67c84
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/Data/Cards/Card_NewCard.asset
Normal file
23
Assets/Data/Cards/Card_NewCard.asset
Normal 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
|
||||
@@ -1,7 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 341eafcd16915de49acdfe16b2ffaba4
|
||||
TextScriptImporter:
|
||||
guid: e2d5a81487e00e2489357c877fa484db
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/Data/Cards/Card_Pulver.asset
Normal file
23
Assets/Data/Cards/Card_Pulver.asset
Normal 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
|
||||
8
Assets/Data/Cards/Card_Pulver.asset.meta
Normal file
8
Assets/Data/Cards/Card_Pulver.asset.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0046d7c3ed6b85245af4ce4144b60dfb
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
|
||||
|
||||
264
Assets/Editor/CardSystem/CardSystemLivePreview.cs
Normal file
264
Assets/Editor/CardSystem/CardSystemLivePreview.cs
Normal 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
|
||||
|
||||
3
Assets/Editor/CardSystem/CardSystemLivePreview.cs.meta
Normal file
3
Assets/Editor/CardSystem/CardSystemLivePreview.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c45a4455c1440069cd9c1ee815f32e0
|
||||
timeCreated: 1762464117
|
||||
@@ -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: []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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":[]}
|
||||
@@ -1 +0,0 @@
|
||||
{"MeasurementCount":-1}
|
||||
@@ -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
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
"Unity.InputSystem",
|
||||
"Unity.TextMeshPro",
|
||||
"OptimizedRope",
|
||||
"Unity.Cinemachine",
|
||||
"AudioSourceEvents",
|
||||
"NewAssembly",
|
||||
"SkiaSharp.Unity",
|
||||
"SkiaSharp.Editor",
|
||||
"SkiaSharp"
|
||||
"SkiaSharp",
|
||||
"Unity.Cinemachine"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
633
Assets/Scripts/UI/CardSystem/FlippableCard.cs
Normal file
633
Assets/Scripts/UI/CardSystem/FlippableCard.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/UI/CardSystem/FlippableCard.cs.meta
Normal file
3
Assets/Scripts/UI/CardSystem/FlippableCard.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa05ec4ecbd4cc485e2127683c29f09
|
||||
timeCreated: 1762454507
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user