Slotting cards in album after revealing

This commit is contained in:
Michal Pikulski
2025-11-07 01:51:03 +01:00
parent debe70c9b1
commit 3e607f3857
20 changed files with 2986 additions and 1459 deletions

View File

@@ -0,0 +1,328 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1275563675283742273
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4689562168107797719}
- component: {fileID: 1691790559549813443}
m_Layer: 0
m_Name: AlbumCard
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4689562168107797719
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1275563675283742273}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7701563473015326516}
m_Father: {fileID: 0}
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_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 400, y: 540}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1691790559549813443
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1275563675283742273}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 706803638ea24880bae19c87d3851ce6, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.AlbumCardDraggable
moveSpeed: 1500
smoothMovement: 0
snapDuration: 0.3
visual: {fileID: 0}
isSelectable: 1
selectionOffset: 10
flippableCard: {fileID: 3814888273534605961}
holdRevealDelay: 0.3
--- !u!1001 &9020921157083249943
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 4689562168107797719}
m_Modifications:
- target: {fileID: 1217866542791508741, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_fontSize
value: 55
objectReference: {fileID: 0}
- target: {fileID: 1571786155082116174, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1571786155082116174, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1571786155082116174, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1571786155082116174, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1571786155082116174, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1571786155082116174, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1657266364921102667, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1657266364921102667, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1657266364921102667, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1657266364921102667, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1657266364921102667, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1657266364921102667, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2170561336081419296, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2170561336081419296, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2170561336081419296, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2170561336081419296, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2705687956353102842, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2705687956353102842, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2705687956353102842, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2705687956353102842, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2705687956353102842, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2705687956353102842, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4874164524383443800, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4874164524383443800, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4874164524383443800, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4874164524383443800, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4874164524383443800, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4874164524383443800, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5605972516071022591, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7971762251737130074, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595097391291779023, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595097391291779023, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595097391291779023, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595097391291779023, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595097391291779023, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595097391291779023, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9030846195633449058, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 9060030918047515996, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_Name
value: FlippableCardPrefab
objectReference: {fileID: 0}
- target: {fileID: 9060030918047515996, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
--- !u!114 &3814888273534605961 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 5314682225669040030, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
m_PrefabInstance: {fileID: 9020921157083249943}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ffa05ec4ecbd4cc485e2127683c29f09, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.FlippableCard
--- !u!224 &7701563473015326516 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 1716378143019989539, guid: e16716863eca4704fbfabef5a699b5aa, type: 3}
m_PrefabInstance: {fileID: 9020921157083249943}
m_PrefabAsset: {fileID: 0}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: aca553283b12f314795f62d785d01912
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,186 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &3676527493138140132
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8845385832003591182}
- component: {fileID: 4571551530005540356}
- component: {fileID: 5228938363013129740}
m_Layer: 5
m_Name: BackgroundImage
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8845385832003591182
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3676527493138140132}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8880693373090345290}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4571551530005540356
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3676527493138140132}
m_CullTransparentMesh: 1
--- !u!114 &5228938363013129740
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3676527493138140132}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
m_Material: {fileID: 0}
m_Color: {r: 0.3018868, g: 0.3018868, b: 0.3018868, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &3697348702925017591
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8576570241677955255}
- component: {fileID: 2515648508668674600}
- component: {fileID: 5397984527285824388}
m_Layer: 5
m_Name: AlbumCardSlot
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8576570241677955255
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3697348702925017591}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8880693373090345290}
m_Father: {fileID: 0}
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_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 400, y: 540}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2515648508668674600
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3697348702925017591}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 514a349ba18d4842bc4292cb034f0d76, type: 3}
m_Name:
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.AlbumCardSlot
slotIndex: -1
isLocked: 0
hideImageOnPlay: 0
filterByType: 0
allowedTypeNames: []
occupantSizeMode: 1
occupantScale: {x: 1, y: 1, z: 1}
scaleTransitionDuration: 0.3
targetCardDefinition: {fileID: 0}
--- !u!114 &5397984527285824388
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3697348702925017591}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 86710e43de46f6f4bac7c8e50813a599, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.AspectRatioFitter
m_AspectMode: 2
m_AspectRatio: 0.7407407
--- !u!1 &3957516808955976615
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8880693373090345290}
m_Layer: 5
m_Name: Visuals
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8880693373090345290
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3957516808955976615}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8845385832003591182}
m_Father: {fileID: 8576570241677955255}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c260b7a90f2306840a21b8a898010e5a
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &666061725648654652
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8002664199913995372}
m_Layer: 0
m_Name: EmptyPrefab
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8002664199913995372
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 666061725648654652}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a608484c63e38d643b8a0f094b30e7ed
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -119,6 +119,107 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &112295772
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 112295776}
- component: {fileID: 112295775}
- component: {fileID: 112295774}
- component: {fileID: 112295773}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &112295773
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 112295772}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 4294967295
--- !u!114 &112295774
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 112295772}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.CanvasScaler
m_UiScaleMode: 0
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 800, y: 600}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!223 &112295775
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 112295772}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 0
m_Camera: {fileID: 0}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 0
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!224 &112295776
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 112295772}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!1 &205824278
GameObject:
m_ObjectHideFlags: 0
@@ -178,6 +279,14 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 595272504056402413, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 825332419457559166, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 986705326607357273, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMax.y
value: 0
@@ -226,6 +335,26 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1015781254734888848, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1015781254734888848, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2195640027358863122, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2473734400733064735, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2933317804819996964, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3283099424197577028, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMax.y
value: 0
@@ -250,10 +379,70 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3387604480156603269, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5914855795585001110, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5990642783362064446, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6305745178180749265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6745067370585802576, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6840374429432680527, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6850526187527119454, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6972626947951714265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6972626947951714265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6972626947951714265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6972626947951714265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6972626947951714265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6972626947951714265, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7104076737882304566, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7142802033631930748, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7561458520277380541, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7727649523464928408, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMax.y
value: 0
@@ -382,6 +571,42 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8264872370053686409, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8595621610758973760, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8686904468228409787, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMax.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8686904468228409787, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8686904468228409787, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8686904468228409787, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8686904468228409787, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8686904468228409787, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8742263962805741772, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8962991431592255329, guid: 4684df63af6398f4f9f624a35023f8d2, type: 3}
propertyPath: m_AnchorMax.y
value: 0
@@ -643,3 +868,4 @@ SceneRoots:
- {fileID: 205824280}
- {fileID: 2067824743}
- {fileID: 445623521}
- {fileID: 112295776}

View File

@@ -11,6 +11,8 @@ namespace Data.CardSystem
{
public int boosterPackCount;
public List<SavedCardEntry> cards = new List<SavedCardEntry>();
public List<SavedCardEntry> pendingRevealCards = new List<SavedCardEntry>();
public List<string> placedInAlbumCardIds = new List<string>();
}
/// <summary>

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AppleHills.Data.CardSystem;
using Bootstrap;
using Core;
@@ -24,6 +25,10 @@ namespace Data.CardSystem
// Runtime data - will be serialized for save/load
[SerializeField] private CardInventory playerInventory = new CardInventory();
// Album system - cards waiting to be placed in album
private List<CardData> _pendingRevealCards = new List<CardData>();
private HashSet<string> _placedInAlbumCardIds = new HashSet<string>();
// Dictionary to quickly look up card definitions by ID
private Dictionary<string, CardDefinition> _definitionLookup = new Dictionary<string, CardDefinition>();
@@ -32,6 +37,8 @@ namespace Data.CardSystem
public event Action<CardData> OnCardCollected;
public event Action<CardData> OnCardRarityUpgraded;
public event Action<int> OnBoosterCountChanged;
public event Action<CardData> OnPendingCardAdded;
public event Action<CardData> OnCardPlacedInAlbum;
private void Awake()
{
@@ -175,19 +182,34 @@ namespace Data.CardSystem
/// <summary>
/// Check if a card is new to the player's collection at the specified rarity
/// Checks both owned inventory and pending reveal queue
/// </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>
/// <returns>True if this is a new card at this rarity, false if already owned or pending</returns>
public bool IsCardNew(CardData cardData, out CardData existingCard)
{
// First check inventory (cards already placed in album)
if (playerInventory.HasCard(cardData.DefinitionId, cardData.Rarity))
{
existingCard = playerInventory.GetCard(cardData.DefinitionId, cardData.Rarity);
return false;
}
// Then check pending reveal queue (cards waiting to be placed)
CardData pendingCard = _pendingRevealCards.FirstOrDefault(c =>
c.DefinitionId == cardData.DefinitionId && c.Rarity == cardData.Rarity);
if (pendingCard != null)
{
// Return the actual pending card with its real CopiesOwned count
// Pending status is just about placement location, not copy count
existingCard = pendingCard;
return false; // Not new - already in pending queue
}
existingCard = null;
return true;
return true; // Truly new - not in inventory or pending
}
/// <summary>
@@ -201,28 +223,46 @@ namespace Data.CardSystem
/// <summary>
/// Adds a card to the player's inventory, handles duplicates
/// Checks both inventory and pending lists to find existing cards
/// </summary>
private void AddCardToInventory(CardData card)
{
// Check if the player already has this card at this rarity
// Guard: Ensure card has at least 1 copy
if (card.CopiesOwned <= 0)
{
card.CopiesOwned = 1;
Logging.Warning($"[CardSystemManager] Card '{card.Name}' had {card.CopiesOwned} copies, setting to 1");
}
// First check inventory (cards already placed in album)
if (playerInventory.HasCard(card.DefinitionId, card.Rarity))
{
CardData existingCard = playerInventory.GetCard(card.DefinitionId, card.Rarity);
existingCard.CopiesOwned++;
// Note: Upgrades are now handled separately in BoosterOpeningPage
// We don't auto-upgrade here anymore
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}' ({card.Rarity}). Now have {existingCard.CopiesOwned} copies.");
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}' ({card.Rarity}) to INVENTORY. Now have {existingCard.CopiesOwned} copies.");
return;
}
else
// Then check pending reveal queue
CardData pendingCard = _pendingRevealCards.FirstOrDefault(c =>
c.DefinitionId == card.DefinitionId && c.Rarity == card.Rarity);
if (pendingCard != null)
{
// Add new card at this rarity
playerInventory.AddCard(card);
OnCardCollected?.Invoke(card);
// Card already in pending - increment its copy count
pendingCard.CopiesOwned++;
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' ({card.Rarity}) to collection.");
Logging.Debug($"[CardSystemManager] Added duplicate card '{card.Name}' ({card.Rarity}) to PENDING. Now have {pendingCard.CopiesOwned} copies pending.");
return;
}
// This is a NEW card (never owned at this rarity before)
// Add to pending reveal list instead of inventory
_pendingRevealCards.Add(card);
OnPendingCardAdded?.Invoke(card);
Logging.Debug($"[CardSystemManager] Added new card '{card.Name}' ({card.Rarity}) to pending reveal queue.");
}
/// <summary>
@@ -286,27 +326,33 @@ namespace Data.CardSystem
}
/// <summary>
/// Returns all cards from the player's collection
/// Returns all cards from the player's collection (both owned and pending)
/// </summary>
public List<CardData> GetAllCollectedCards()
{
return playerInventory.GetAllCards();
List<CardData> allCards = new List<CardData>(playerInventory.GetAllCards());
allCards.AddRange(_pendingRevealCards);
return allCards;
}
/// <summary>
/// Returns cards from a specific zone
/// Returns cards from a specific zone (both owned and pending)
/// </summary>
public List<CardData> GetCardsByZone(CardZone zone)
{
return playerInventory.GetCardsByZone(zone);
List<CardData> zoneCards = new List<CardData>(playerInventory.GetCardsByZone(zone));
zoneCards.AddRange(_pendingRevealCards.Where(c => c.Zone == zone));
return zoneCards;
}
/// <summary>
/// Returns cards of a specific rarity
/// Returns cards of a specific rarity (both owned and pending)
/// </summary>
public List<CardData> GetCardsByRarity(CardRarity rarity)
{
return playerInventory.GetCardsByRarity(rarity);
List<CardData> rarityCards = new List<CardData>(playerInventory.GetCardsByRarity(rarity));
rarityCards.AddRange(_pendingRevealCards.Where(c => c.Rarity == rarity));
return rarityCards;
}
/// <summary>
@@ -318,25 +364,38 @@ namespace Data.CardSystem
}
/// <summary>
/// Returns whether a specific card definition has been collected (at any rarity)
/// Returns whether a specific card definition has been collected (at any rarity, in inventory or pending)
/// </summary>
public bool IsCardCollected(string definitionId)
{
// Check if the card exists at any rarity
// Check inventory at any rarity
foreach (CardRarity rarity in System.Enum.GetValues(typeof(CardRarity)))
{
if (playerInventory.HasCard(definitionId, rarity))
return true;
}
// Check pending reveal queue
if (_pendingRevealCards.Any(c => c.DefinitionId == definitionId))
return true;
return false;
}
/// <summary>
/// Gets total unique card count
/// Gets total unique card count (both owned and pending)
/// </summary>
public int GetUniqueCardCount()
{
return playerInventory.GetUniqueCardCount();
int inventoryCount = playerInventory.GetUniqueCardCount();
// Count unique cards in pending that aren't already in inventory
int pendingUniqueCount = _pendingRevealCards
.Select(c => new { c.DefinitionId, c.Rarity })
.Distinct()
.Count(pc => !playerInventory.HasCard(pc.DefinitionId, pc.Rarity));
return inventoryCount + pendingUniqueCount;
}
/// <summary>
@@ -381,19 +440,23 @@ namespace Data.CardSystem
}
/// <summary>
/// Returns the count of cards by rarity
/// Returns the count of cards by rarity (both owned and pending)
/// </summary>
public int GetCardCountByRarity(CardRarity rarity)
{
return playerInventory.GetCardsByRarity(rarity).Count;
int inventoryCount = playerInventory.GetCardsByRarity(rarity).Count;
int pendingCount = _pendingRevealCards.Count(c => c.Rarity == rarity);
return inventoryCount + pendingCount;
}
/// <summary>
/// Returns the count of cards by zone
/// Returns the count of cards by zone (both owned and pending)
/// </summary>
public int GetCardCountByZone(CardZone zone)
{
return playerInventory.GetCardsByZone(zone).Count;
int inventoryCount = playerInventory.GetCardsByZone(zone).Count;
int pendingCount = _pendingRevealCards.Count(c => c.Zone == zone);
return inventoryCount + pendingCount;
}
/// <summary>
@@ -428,6 +491,121 @@ namespace Data.CardSystem
return (float)collectedOfRarity / totalOfRarity * 100f;
}
#region Album System
/// <summary>
/// Returns all cards waiting to be placed in the album
/// </summary>
public List<CardData> GetPendingRevealCards()
{
return new List<CardData>(_pendingRevealCards);
}
/// <summary>
/// Get a card by definition ID and rarity from either inventory or pending
/// Returns the actual card reference so changes persist
/// </summary>
/// <param name="definitionId">Card definition ID</param>
/// <param name="rarity">Card rarity</param>
/// <param name="isFromPending">Out parameter - true if card is from pending, false if from inventory</param>
/// <returns>The card data if found, null otherwise</returns>
public CardData GetCard(string definitionId, CardRarity rarity, out bool isFromPending)
{
// Check inventory first
if (playerInventory.HasCard(definitionId, rarity))
{
isFromPending = false;
return playerInventory.GetCard(definitionId, rarity);
}
// Check pending
CardData pendingCard = _pendingRevealCards.FirstOrDefault(c =>
c.DefinitionId == definitionId && c.Rarity == rarity);
if (pendingCard != null)
{
isFromPending = true;
return pendingCard;
}
isFromPending = false;
return null;
}
/// <summary>
/// Get a card by definition ID and rarity from either inventory or pending (simplified overload)
/// </summary>
public CardData GetCard(string definitionId, CardRarity rarity)
{
return GetCard(definitionId, rarity, out _);
}
/// <summary>
/// Update a card's data in whichever list it's in (inventory or pending)
/// Useful for incrementing CopiesOwned, upgrading rarity, etc.
/// </summary>
/// <param name="definitionId">Card definition ID</param>
/// <param name="rarity">Card rarity</param>
/// <param name="updateAction">Action to perform on the card</param>
/// <returns>True if card was found and updated, false otherwise</returns>
public bool UpdateCard(string definitionId, CardRarity rarity, System.Action<CardData> updateAction)
{
CardData card = GetCard(definitionId, rarity, out bool isFromPending);
if (card != null)
{
updateAction?.Invoke(card);
Logging.Debug($"[CardSystemManager] Updated card '{card.Name}' in {(isFromPending ? "pending" : "inventory")}");
return true;
}
Logging.Warning($"[CardSystemManager] Could not find card with ID '{definitionId}' and rarity '{rarity}' to update");
return false;
}
/// <summary>
/// Marks a card as placed in the album
/// Moves it from pending reveal to owned inventory
/// </summary>
public void MarkCardAsPlaced(CardData card)
{
if (_pendingRevealCards.Remove(card))
{
// Add to owned inventory
playerInventory.AddCard(card);
// Track as placed
_placedInAlbumCardIds.Add(card.Id);
OnCardPlacedInAlbum?.Invoke(card);
OnCardCollected?.Invoke(card);
Logging.Debug($"[CardSystemManager] Card '{card.Name}' placed in album and added to inventory.");
}
else
{
Logging.Warning($"[CardSystemManager] Attempted to place card '{card.Name}' but it wasn't in pending reveal list.");
}
}
/// <summary>
/// Checks if a card has been placed in the album
/// </summary>
public bool IsCardPlacedInAlbum(string cardId)
{
return _placedInAlbumCardIds.Contains(cardId);
}
/// <summary>
/// Gets count of cards waiting to be revealed
/// </summary>
public int GetPendingRevealCount()
{
return _pendingRevealCards.Count;
}
#endregion
/// <summary>
/// Export current card collection to a serializable snapshot
/// </summary>
@@ -436,7 +614,9 @@ namespace Data.CardSystem
var state = new CardCollectionState
{
boosterPackCount = playerInventory.BoosterPackCount,
cards = new List<SavedCardEntry>()
cards = new List<SavedCardEntry>(),
pendingRevealCards = new List<SavedCardEntry>(),
placedInAlbumCardIds = new List<string>(_placedInAlbumCardIds)
};
foreach (var card in playerInventory.CollectedCards.Values)
@@ -449,6 +629,18 @@ namespace Data.CardSystem
copiesOwned = card.CopiesOwned
});
}
foreach (var card in _pendingRevealCards)
{
if (string.IsNullOrEmpty(card.DefinitionId)) continue;
state.pendingRevealCards.Add(new SavedCardEntry
{
definitionId = card.DefinitionId,
rarity = card.Rarity,
copiesOwned = card.CopiesOwned
});
}
return state;
}
@@ -460,6 +652,9 @@ namespace Data.CardSystem
if (state == null) return;
playerInventory.ClearAllCards();
_pendingRevealCards.Clear();
_placedInAlbumCardIds.Clear();
playerInventory.BoosterPackCount = state.boosterPackCount;
OnBoosterCountChanged?.Invoke(playerInventory.BoosterPackCount);
@@ -479,6 +674,31 @@ namespace Data.CardSystem
Logging.Warning($"[CardSystemManager] Saved card definition not found: {entry.definitionId}");
}
}
// Restore pending reveal cards
if (state.pendingRevealCards != null)
{
foreach (var entry in state.pendingRevealCards)
{
if (string.IsNullOrEmpty(entry.definitionId)) continue;
if (_definitionLookup.TryGetValue(entry.definitionId, out var def))
{
var cd = def.CreateCardData();
cd.Rarity = entry.rarity;
cd.CopiesOwned = entry.copiesOwned;
_pendingRevealCards.Add(cd);
}
}
}
// Restore placed in album tracking
if (state.placedInAlbumCardIds != null)
{
foreach (var cardId in state.placedInAlbumCardIds)
{
_placedInAlbumCardIds.Add(cardId);
}
}
}
#region ISaveParticipant Implementation

View File

@@ -0,0 +1,191 @@
using System;
using System.Collections;
using AppleHills.Data.CardSystem;
using Data.CardSystem;
using UI.DragAndDrop.Core;
using UnityEngine;
namespace UI.CardSystem
{
/// <summary>
/// Draggable card for album reveal system.
/// Handles both tap and drag-hold interactions for revealing cards.
/// Auto-snaps to matching album slot on release/tap.
/// </summary>
public class AlbumCardDraggable : DraggableObject
{
[Header("Album Card Settings")]
[SerializeField] private FlippableCard flippableCard;
[SerializeField] private float holdRevealDelay = 0.1f;
private CardData _cardData;
private bool _isRevealed = false;
private bool _isDragRevealing = false;
private bool _waitingForPlacementTap = false;
private Coroutine _holdRevealCoroutine;
private bool _isHolding = false; // Track if pointer is currently down
// Events
public event Action<AlbumCardDraggable, CardData> OnCardRevealed;
public event Action<AlbumCardDraggable, CardData> OnCardPlacedInAlbum;
public CardData CardData => _cardData;
public bool IsRevealed => _isRevealed;
public CardZone Zone => _cardData?.Zone ?? CardZone.AppleHills;
protected override void Initialize()
{
base.Initialize();
// Auto-find FlippableCard if not assigned
if (flippableCard == null)
{
flippableCard = GetComponent<FlippableCard>();
}
}
/// <summary>
/// Setup the card data (stores it but doesn't reveal until tapped/dragged)
/// </summary>
public void SetupCard(CardData data)
{
_cardData = data;
if (flippableCard != null)
{
flippableCard.SetupCard(data);
}
}
/// <summary>
/// Reveal the card (flip to show front)
/// </summary>
public void RevealCard()
{
if (_isRevealed) return;
_isRevealed = true;
if (flippableCard != null)
{
flippableCard.FlipToReveal();
}
OnCardRevealed?.Invoke(this, _cardData);
}
/// <summary>
/// Snap to the matching album slot
/// </summary>
public void SnapToAlbumSlot()
{
if (_cardData == null)
{
Debug.LogWarning("[AlbumCardDraggable] Cannot snap to slot - no card data assigned.");
return;
}
// Find all album card slots in the scene
AlbumCardSlot[] allSlots = FindObjectsByType<AlbumCardSlot>(FindObjectsSortMode.None);
AlbumCardSlot matchingSlot = null;
foreach (var slot in allSlots)
{
if (slot.CanAcceptCard(_cardData))
{
matchingSlot = slot;
break;
}
}
if (matchingSlot != null)
{
// Assign to slot with animation
AssignToSlot(matchingSlot, true);
// Mark slot as permanently occupied
matchingSlot.OnCardPlaced();
// Disable dragging - card is now static in album
SetDraggingEnabled(false);
// Notify that card was placed
// Note: Card already moved from pending to inventory in OnCardRevealed
OnCardPlacedInAlbum?.Invoke(this, _cardData);
}
else
{
Debug.LogWarning($"[AlbumCardDraggable] Could not find matching slot for card '{_cardData.Name}' (Zone: {_cardData.Zone}, Index: {_cardData.CollectionIndex})");
}
}
protected override void OnPointerDownHook()
{
base.OnPointerDownHook();
_isHolding = true;
// Start hold-reveal timer if card not yet revealed
if (!_isRevealed && _holdRevealCoroutine == null)
{
_holdRevealCoroutine = StartCoroutine(HoldRevealTimer());
}
}
protected override void OnPointerUpHook(bool longPress)
{
base.OnPointerUpHook(longPress);
_isHolding = false;
// Cancel hold timer if running
if (_holdRevealCoroutine != null)
{
StopCoroutine(_holdRevealCoroutine);
_holdRevealCoroutine = null;
}
// Handle tap (not dragged)
if (!_wasDragged)
{
if (!_isRevealed)
{
// First tap: reveal the card
RevealCard();
_waitingForPlacementTap = true;
}
else if (_waitingForPlacementTap)
{
// Second tap: snap to slot
_waitingForPlacementTap = false;
SnapToAlbumSlot();
}
}
else if (_isDragRevealing)
{
// Was drag-revealed, auto-snap on release
_isDragRevealing = false;
SnapToAlbumSlot();
}
}
/// <summary>
/// Coroutine to reveal card after holding for specified duration
/// </summary>
private IEnumerator HoldRevealTimer()
{
yield return new WaitForSeconds(holdRevealDelay);
// If still holding after delay, reveal the card
if (!_isRevealed && _isHolding)
{
RevealCard();
_isDragRevealing = true;
Debug.Log("[AlbumCardDraggable] Card revealed via hold");
}
_holdRevealCoroutine = null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 706803638ea24880bae19c87d3851ce6
timeCreated: 1762470947

View File

@@ -0,0 +1,57 @@
using AppleHills.Data.CardSystem;
using UI.DragAndDrop.Core;
using UnityEngine;
namespace UI.CardSystem
{
/// <summary>
/// Specialized slot for album pages that only accepts a specific card.
/// Validates cards based on their CardDefinition.
/// </summary>
public class AlbumCardSlot : DraggableSlot
{
[Header("Album Slot Configuration")]
[SerializeField] private CardDefinition targetCardDefinition; // Which card this slot accepts
private bool _isOccupiedPermanently = false;
/// <summary>
/// Set the target card this slot should accept
/// </summary>
public void SetTargetCard(CardDefinition definition)
{
targetCardDefinition = definition;
}
/// <summary>
/// Check if this slot can accept a specific card
/// </summary>
public bool CanAcceptCard(CardData cardData)
{
if (cardData == null || targetCardDefinition == null) return false;
if (_isOccupiedPermanently) return false;
// Card must match this slot's target definition
return cardData.DefinitionId == targetCardDefinition.Id;
}
/// <summary>
/// Called when a card is successfully placed in this slot
/// </summary>
public void OnCardPlaced()
{
_isOccupiedPermanently = true;
}
/// <summary>
/// Check if this slot has been permanently filled
/// </summary>
public bool IsOccupiedPermanently => _isOccupiedPermanently;
/// <summary>
/// Get the target card definition for this slot
/// </summary>
public CardDefinition TargetCardDefinition => targetCardDefinition;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 514a349ba18d4842bc4292cb034f0d76
timeCreated: 1762470924

View File

@@ -1,7 +1,11 @@
using Bootstrap;
using System.Collections.Generic;
using System.Linq;
using AppleHills.Data.CardSystem;
using Bootstrap;
using Data.CardSystem;
using Pixelplacement;
using UI.Core;
using UI.DragAndDrop.Core;
using UnityEngine;
using UnityEngine.UI;
@@ -18,11 +22,20 @@ namespace UI.CardSystem
[SerializeField] private Button exitButton;
[SerializeField] private BookCurlPro.BookPro book;
[Header("Zone Navigation")]
[SerializeField] private BookTabButton[] zoneTabs; // All zone tab buttons
[Header("Album Card Reveal")]
[SerializeField] private SlotContainer bottomRightSlots;
[SerializeField] private GameObject albumCardPrefab;
[Header("Booster Pack UI")]
[SerializeField] private GameObject[] boosterPackButtons;
[SerializeField] private BoosterOpeningPage boosterOpeningPage;
private Input.InputMode _previousInputMode;
private List<AlbumCardDraggable> _activeCards = new List<AlbumCardDraggable>();
private const int MAX_VISIBLE_CARDS = 3;
private void Awake()
{
@@ -54,6 +67,8 @@ namespace UI.CardSystem
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
// NOTE: OnPendingCardAdded is subscribed in TransitionIn, not here
// This prevents spawning cards when page is not active
// Update initial button visibility
int initialCount = CardSystemManager.Instance.GetBoosterPackCount();
@@ -83,6 +98,7 @@ namespace UI.CardSystem
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
// NOTE: OnPendingCardAdded is unsubscribed in TransitionOut
}
// Clean up exit button
@@ -105,6 +121,9 @@ namespace UI.CardSystem
}
}
}
// Clean up active cards
CleanupActiveCards();
}
private void OnExitButtonClicked()
@@ -182,11 +201,26 @@ namespace UI.CardSystem
Debug.Log("[AlbumViewPage] Switched to UI-only input mode on first entry");
}
// Subscribe to pending card events while page is active
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnPendingCardAdded += OnPendingCardAdded;
}
// Spawn pending cards when opening album
SpawnPendingCards();
base.TransitionIn();
}
public override void TransitionOut()
{
// Unsubscribe from pending card events when page closes
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.OnPendingCardAdded -= OnPendingCardAdded;
}
// Don't restore input mode here - only restore when actually exiting (in OnExitButtonClicked)
base.TransitionOut();
}
@@ -219,5 +253,312 @@ namespace UI.CardSystem
onComplete?.Invoke();
}
}
#region Album Card Reveal System
/// <summary>
/// Spawn pending cards from CardSystemManager
/// Only spawns unique cards (one per definition+rarity, not one per copy)
/// </summary>
private void SpawnPendingCards()
{
if (CardSystemManager.Instance == null || bottomRightSlots == null || albumCardPrefab == null)
return;
var pending = CardSystemManager.Instance.GetPendingRevealCards();
// Get unique cards only (by DefinitionId + Rarity)
// Filter out cards with CopiesOwned = 0 (shouldn't happen but guard against it)
var uniquePending = pending
.Where(c => c.CopiesOwned > 0) // Guard: exclude zero-count cards
.GroupBy(c => new { c.DefinitionId, c.Rarity })
.Select(g => g.First()) // Take first instance of each unique card
.ToList();
int spawnCount = Mathf.Min(uniquePending.Count, MAX_VISIBLE_CARDS);
Debug.Log($"[AlbumViewPage] Spawning {spawnCount} unique pending cards (total pending: {pending.Count})");
for (int i = 0; i < spawnCount; i++)
{
SpawnCardInSlot(i, uniquePending[i]);
}
}
/// <summary>
/// Spawn a card in a specific slot
/// </summary>
private void SpawnCardInSlot(int slotIndex, CardData cardData)
{
// Guard: Don't spawn cards with zero copies
if (cardData.CopiesOwned <= 0)
{
Debug.LogWarning($"[AlbumViewPage] Skipping spawn of card '{cardData.Name}' with {cardData.CopiesOwned} copies");
return;
}
DraggableSlot slot = FindSlotByIndex(slotIndex);
if (slot == null)
{
Debug.LogWarning($"[AlbumViewPage] Could not find slot with SlotIndex {slotIndex}");
return;
}
// Instantiate card directly as child of the slot container (not the slot itself, not canvas root)
// This keeps it in the correct UI hierarchy
GameObject cardObj = Instantiate(albumCardPrefab, bottomRightSlots.transform);
AlbumCardDraggable card = cardObj.GetComponent<AlbumCardDraggable>();
if (card != null)
{
// Setup card data
card.SetupCard(cardData);
// Subscribe to events
card.OnCardRevealed += OnCardRevealed;
card.OnCardPlacedInAlbum += OnCardPlacedInAlbum;
// NOW assign to slot - this will:
// 1. Reparent to slot
// 2. Apply slot's occupantSizeMode scaling
// 3. Animate to slot position
card.AssignToSlot(slot, true);
// Track it
_activeCards.Add(card);
Debug.Log($"[AlbumViewPage] Spawned card '{cardData.Name}' (CopiesOwned: {cardData.CopiesOwned}) in slot {slotIndex}");
}
else
{
Debug.LogWarning($"[AlbumViewPage] Spawned card has no AlbumCardDraggable component!");
Destroy(cardObj);
}
}
/// <summary>
/// Handle when a new card is added to pending queue
/// Only spawn if this unique card isn't already visualized
/// </summary>
private void OnPendingCardAdded(CardData card)
{
// Guard: Don't spawn cards with zero copies
if (card.CopiesOwned <= 0)
{
Debug.LogWarning($"[AlbumViewPage] Ignoring pending card '{card.Name}' with {card.CopiesOwned} copies");
return;
}
// Check if we already have a card with this definition + rarity spawned
bool alreadySpawned = _activeCards.Any(c =>
c.CardData.DefinitionId == card.DefinitionId &&
c.CardData.Rarity == card.Rarity);
if (alreadySpawned)
{
Debug.Log($"[AlbumViewPage] Card '{card.Name}' already spawned, skipping duplicate spawn");
return; // Don't spawn duplicates
}
// Try to spawn if we have space
if (_activeCards.Count < MAX_VISIBLE_CARDS)
{
int nextSlotIndex = _activeCards.Count;
SpawnCardInSlot(nextSlotIndex, card);
}
}
/// <summary>
/// Handle when a card is revealed (flipped)
/// </summary>
private void OnCardRevealed(AlbumCardDraggable card, CardData cardData)
{
Debug.Log($"[AlbumViewPage] Card revealed: {cardData.Name} (Zone: {cardData.Zone}, CopiesOwned: {cardData.CopiesOwned})");
// IMMEDIATELY move card from pending to inventory upon reveal
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.MarkCardAsPlaced(cardData);
Debug.Log($"[AlbumViewPage] Moved card '{cardData.Name}' from pending to inventory on reveal");
}
// Remove this card from active cards list
_activeCards.Remove(card);
// Check if we're currently viewing the correct zone for this card
CardZone currentZone = GetCurrentZone();
if (currentZone != cardData.Zone)
{
// Card is from a different zone - navigate to its zone
Debug.Log($"[AlbumViewPage] Card zone ({cardData.Zone}) doesn't match current zone ({currentZone}). Navigating to card's zone...");
NavigateToZone(cardData.Zone);
}
else
{
Debug.Log($"[AlbumViewPage] Card zone ({cardData.Zone}) matches current zone - no navigation needed.");
}
// Shuffle remaining cards to front and spawn next unique card
ShuffleCardsToFront();
TrySpawnNextCard();
}
/// <summary>
/// Handle when a card is placed in the album (from AlbumCardDraggable)
/// Card data already moved to inventory in OnCardRevealed
/// This just handles cleanup
/// </summary>
private void OnCardPlacedInAlbum(AlbumCardDraggable card, CardData cardData)
{
Debug.Log($"[AlbumViewPage] Card placed in album slot: {cardData.Name}");
// Unsubscribe from events (card is now static in album)
card.OnCardRevealed -= OnCardRevealed;
card.OnCardPlacedInAlbum -= OnCardPlacedInAlbum;
// Note: Card already removed from _activeCards in OnCardRevealed
// Note: Shuffle and spawn already done in OnCardRevealed
}
/// <summary>
/// Shuffle active cards to occupy front slots
/// </summary>
private void ShuffleCardsToFront()
{
if (bottomRightSlots == null || _activeCards.Count == 0)
return;
// Convert to base DraggableObject list for helper method
List<DraggableObject> draggableList = _activeCards.Cast<DraggableObject>().ToList();
SlotContainerHelper.ShuffleToFront(bottomRightSlots, draggableList, animate: true);
}
/// <summary>
/// Try to spawn the next pending card
/// Only spawns unique cards (not duplicates)
/// </summary>
private void TrySpawnNextCard()
{
if (CardSystemManager.Instance == null)
return;
if (_activeCards.Count >= MAX_VISIBLE_CARDS)
return; // Already at max
var pending = CardSystemManager.Instance.GetPendingRevealCards();
// Get unique pending cards, excluding zero-count cards
var uniquePending = pending
.Where(c => c.CopiesOwned > 0) // Guard: exclude zero-count cards
.GroupBy(c => new { c.DefinitionId, c.Rarity })
.Select(g => g.First())
.ToList();
// Find first unique card that's not already spawned
foreach (var cardData in uniquePending)
{
bool alreadySpawned = _activeCards.Any(c =>
c.CardData.DefinitionId == cardData.DefinitionId &&
c.CardData.Rarity == cardData.Rarity);
if (!alreadySpawned)
{
int nextSlotIndex = _activeCards.Count;
SpawnCardInSlot(nextSlotIndex, cardData);
break;
}
}
}
/// <summary>
/// Find a slot by its SlotIndex property
/// </summary>
private DraggableSlot FindSlotByIndex(int slotIndex)
{
if (bottomRightSlots == null)
return null;
foreach (var slot in bottomRightSlots.Slots)
{
if (slot.SlotIndex == slotIndex)
{
return slot;
}
}
return null;
}
/// <summary>
/// Get the current zone based on book page
/// </summary>
public CardZone GetCurrentZone()
{
if (book == null || zoneTabs == null || zoneTabs.Length == 0)
return CardZone.AppleHills; // Default
int currentPage = book.CurrentPaper;
// Find tab with matching target page
foreach (var tab in zoneTabs)
{
if (tab.TargetPage == currentPage)
{
return tab.Zone;
}
}
// Fallback to first zone
return zoneTabs[0].Zone;
}
/// <summary>
/// Get tab for a specific zone
/// </summary>
public BookTabButton GetTabForZone(CardZone zone)
{
if (zoneTabs == null)
return null;
foreach (var tab in zoneTabs)
{
if (tab.Zone == zone)
{
return tab;
}
}
return null;
}
/// <summary>
/// Navigate to a specific zone
/// </summary>
public void NavigateToZone(CardZone zone)
{
BookTabButton tab = GetTabForZone(zone);
if (tab != null)
{
tab.ActivateTab();
}
}
/// <summary>
/// Clean up all active cards
/// </summary>
private void CleanupActiveCards()
{
foreach (var card in _activeCards)
{
if (card != null && card.gameObject != null)
{
card.OnCardRevealed -= OnCardRevealed;
card.OnCardPlacedInAlbum -= OnCardPlacedInAlbum;
Destroy(card.gameObject);
}
}
_activeCards.Clear();
}
#endregion
}
}

View File

@@ -1,4 +1,5 @@
using System;
using AppleHills.Data.CardSystem;
using BookCurlPro;
using UnityEngine;
using UnityEngine.UI;
@@ -18,6 +19,7 @@ namespace UI.CardSystem
[Header("Tab Configuration")]
[SerializeField] private int targetPage;
[SerializeField] private CardZone zone;
[Header("Visual Settings")]
[SerializeField] private bool enableScaling = true;
@@ -32,6 +34,10 @@ namespace UI.CardSystem
// Static dispatcher for coordinating all tabs
private static event Action<BookTabButton> OnTabClicked;
// Public properties to access this tab's configuration
public CardZone Zone => zone;
public int TargetPage => targetPage;
private void Awake()
{
// Get required components

View File

@@ -0,0 +1,70 @@
 using System.Collections.Generic;
using UI.DragAndDrop.Core;
using UnityEngine;
namespace UI.CardSystem
{
/// <summary>
/// Helper utility for shuffling draggable objects in a SlotContainer.
/// Moves objects to occupy the first available slots (0, 1, 2, etc.)
/// </summary>
public static class SlotContainerHelper
{
/// <summary>
/// Shuffles draggable objects to always occupy the first available slots.
/// Unassigns all objects from their current slots, then reassigns them starting from slot 0.
/// </summary>
/// <param name="container">The slot container holding the slots</param>
/// <param name="objects">List of draggable objects to shuffle</param>
/// <param name="animate">Whether to animate the movement</param>
public static void ShuffleToFront(SlotContainer container, List<DraggableObject> objects, bool animate = true)
{
if (container == null || objects == null || objects.Count == 0)
return;
Debug.Log($"[SlotContainerHelper] Shuffling {objects.Count} objects to front slots");
// Unassign all objects from their current slots
foreach (var obj in objects)
{
if (obj.CurrentSlot != null)
{
obj.CurrentSlot.Vacate();
}
}
// Reassign objects to first N slots starting from slot 0
for (int i = 0; i < objects.Count; i++)
{
DraggableSlot targetSlot = FindSlotByIndex(container, i);
DraggableObject obj = objects[i];
if (targetSlot != null)
{
Debug.Log($"[SlotContainerHelper] Assigning object to slot with SlotIndex {i}");
obj.AssignToSlot(targetSlot, animate);
}
else
{
Debug.LogWarning($"[SlotContainerHelper] Could not find slot with SlotIndex {i}");
}
}
}
/// <summary>
/// Find a slot by its SlotIndex property (not list position)
/// </summary>
private static DraggableSlot FindSlotByIndex(SlotContainer container, int slotIndex)
{
foreach (var slot in container.Slots)
{
if (slot.SlotIndex == slotIndex)
{
return slot;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cad44f85ab1a4672ab4bb14e2f919413
timeCreated: 1762470959