This commit is contained in:
2025-12-08 14:57:56 +01:00
20 changed files with 3166 additions and 2248 deletions

View File

@@ -172,6 +172,7 @@ SkinnedMeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
serializedVersion: 2
m_Quality: 0
m_UpdateWhenOffscreen: 0
@@ -456,6 +457,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &1477914804333655978
MonoBehaviour:
@@ -558,6 +560,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &6279009652162230510
MonoBehaviour:
@@ -660,6 +663,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &7522599432703993887
MonoBehaviour:
@@ -796,6 +800,7 @@ SkinnedMeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
serializedVersion: 2
m_Quality: 0
m_UpdateWhenOffscreen: 0
@@ -912,6 +917,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &4521180984547725349
MonoBehaviour:

View File

@@ -34,6 +34,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &4110666412151536905
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -79,6 +80,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -88,7 +90,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 0
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &7249528695393012044
GameObject:
@@ -131,6 +132,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &6258593095132504700
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -176,6 +178,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_Sprite: {fileID: -8438005379329254897, guid: fe735eb4f8856904caec179520dcb92f, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -185,7 +188,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!61 &5475802662781903683
BoxCollider2D:

View File

@@ -121,6 +121,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &1799170203493421407
MonoBehaviour:
@@ -169,6 +170,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &2343214996212089369
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -214,6 +216,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -1125559343802010594, guid: 46b2fe6896b27cc4c8bd9f0da3f0de50, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -223,7 +226,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!61 &7660380594239590698
BoxCollider2D:
@@ -360,6 +362,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &6348852419369233976
MonoBehaviour:
@@ -407,6 +410,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &7990414055343410434
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -452,6 +456,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -461,7 +466,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 0
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &2265449145943364229
GameObject:
@@ -552,6 +556,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &220335373367130193
MonoBehaviour:
@@ -685,6 +690,7 @@ MeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &972451099498184359
MonoBehaviour:
@@ -790,6 +796,7 @@ SkinnedMeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
serializedVersion: 2
m_Quality: 0
m_UpdateWhenOffscreen: 0
@@ -909,6 +916,7 @@ SkinnedMeshRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
serializedVersion: 2
m_Quality: 0
m_UpdateWhenOffscreen: 0
@@ -1393,6 +1401,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &5292193662800385283
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -1438,6 +1447,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 2
m_MaskInteraction: 0
m_Sprite: {fileID: 779209807, guid: b215bf5a0a0f5db4da48c686489d8cab, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -1447,7 +1457,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!95 &2858293142053250427
Animator:

View File

@@ -86,6 +86,7 @@ PolygonCollider2D:
m_UseDelaunayMesh: 0
--- !u!212 &2825253017896168654
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -131,6 +132,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -642587728066523507, guid: 95d6dbee5cb1f694c971791ee60cad14, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -140,7 +142,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!1 &4316370833098727305
GameObject:
@@ -176,6 +177,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &3806274462998212361
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -221,6 +223,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -230,7 +233,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 0
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!1 &7145022056631397938
GameObject:

View File

@@ -57,6 +57,7 @@ BoxCollider:
m_Center: {x: 0, y: 0, z: 0}
--- !u!212 &4055726361761331703
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -102,6 +103,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -3019416885370694485, guid: ed57eef56cafff0428d08bce28798f58, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -111,7 +113,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!114 &1226703179564388091
MonoBehaviour:

View File

@@ -57,6 +57,7 @@ BoxCollider:
m_Center: {x: 0, y: 0, z: 0}
--- !u!212 &4774534086162962138
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -102,6 +103,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: 4799961156935096247, guid: 74289c1614ce2b2439a5d3ff60a90d66, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -111,7 +113,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!114 &5483561632297398438
MonoBehaviour:

View File

@@ -57,6 +57,7 @@ BoxCollider:
m_Center: {x: 0, y: 0, z: 0}
--- !u!212 &4986096986936361008
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -102,6 +103,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -2082989857396687135, guid: f106ad45f67e9e74895c64edbc1e4614, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -111,7 +113,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!114 &2753389442270867527
MonoBehaviour:

View File

@@ -57,6 +57,7 @@ BoxCollider:
m_Center: {x: 0, y: 0, z: 0}
--- !u!212 &4266110216568578813
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -102,6 +103,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -4183208330383030991, guid: 26abd62d79d435f438c281aa83ee274a, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -111,7 +113,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 1
--- !u!114 &1707774147108908574
MonoBehaviour:

View File

@@ -69,10 +69,14 @@ MonoBehaviour:
itemData: {fileID: 11400000, guid: ea20e322caf6eb441b580775bf1bf2c8, type: 2}
iconRenderer: {fileID: 1311388821310946158}
slottedItemRenderers:
- {fileID: 4363732810493563330}
- {fileID: 4765283125670404640}
- {fileID: 6453868228671073978}
- {fileID: 5502334101811892095}
- renderer: {fileID: 4363732810493563330}
assignedItem: {fileID: 11400000, guid: f5a6df00a3840924a8e5b5052a8c9b05, type: 2}
- renderer: {fileID: 4765283125670404640}
assignedItem: {fileID: 11400000, guid: 0f94d1cbd3735424ea37b6125154ed37, type: 2}
- renderer: {fileID: 6453868228671073978}
assignedItem: {fileID: 11400000, guid: d0e8554a0463a924e803732e9209c92a, type: 2}
- renderer: {fileID: 5502334101811892095}
assignedItem: {fileID: 11400000, guid: 45c190ffad50b994ba53f64a9838751d, type: 2}
onItemSlotted:
m_PersistentCalls:
m_Calls: []

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -34,6 +34,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &3593498626556091915
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -79,6 +80,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: 277634131431117102, guid: 2cf4a5af200e48347b7dcd0dbd84d9ca, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -88,7 +90,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &563765657355903966
GameObject:
@@ -454,6 +455,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &4677446939953396014
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -499,6 +501,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -2418630947029704618, guid: fbbc627f5a8f06e47a23369fd19f3998, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -508,7 +511,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &2312511629901843318
GameObject:
@@ -544,6 +546,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &5126475362876787783
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -589,6 +592,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: 277634131431117102, guid: 2cf4a5af200e48347b7dcd0dbd84d9ca, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -598,7 +602,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &2391935521422290070
GameObject:
@@ -637,6 +640,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &6338084184716153992
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -682,6 +686,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -3811075571101519331, guid: cf5af87e6a25e9442b89f069b894a5fd, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -691,7 +696,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!61 &253472492358066383
BoxCollider2D:
@@ -813,6 +817,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &1081162597868568544
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -858,6 +863,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_Sprite: {fileID: 9105513792776133627, guid: 140f82f54aeb07e409575fe55dc6df14, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -867,7 +873,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!60 &4857176284453307382
PolygonCollider2D:
@@ -955,6 +960,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &7072755111968586092
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -1000,6 +1006,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_Sprite: {fileID: 9105513792776133627, guid: 140f82f54aeb07e409575fe55dc6df14, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -1009,7 +1016,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &8095936409589519396
GameObject:
@@ -1045,6 +1051,7 @@ Transform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &7713018619525726688
SpriteRenderer:
serializedVersion: 2
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
@@ -1090,6 +1097,7 @@ SpriteRenderer:
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 1
m_MaskInteraction: 0
m_Sprite: {fileID: -2418630947029704618, guid: fbbc627f5a8f06e47a23369fd19f3998, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
@@ -1099,7 +1107,6 @@ SpriteRenderer:
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!1 &8607415572972099566
GameObject:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,17 @@ namespace Interactions
Forbidden
}
/// <summary>
/// Maps a sprite renderer to an optional item assignment
/// </summary>
[Serializable]
public class SlotRendererMapping
{
public SpriteRenderer renderer;
[Tooltip("Optional: If set, this renderer slot is dedicated to this specific item. Leave null for flexible slots.")]
public PickupItemData assignedItem;
}
/// <summary>
/// Saveable data for ItemSlot state
/// </summary>
@@ -41,7 +52,7 @@ namespace Interactions
// Multi-slot item tracking
private List<PickupItemData> slottedItemsData = new List<PickupItemData>();
public SpriteRenderer[] slottedItemRenderers; // Array of renderers for multiple items
public SlotRendererMapping[] slottedItemRenderers; // Array of renderers with optional item assignments
private List<GameObject> slottedItemObjects = new List<GameObject>();
private List<bool> slottedItemCorrectness = new List<bool>(); // Track which items are correct
@@ -63,12 +74,24 @@ namespace Interactions
/// <summary>
/// Number of items currently slotted (correct or incorrect)
/// </summary>
public int CurrentSlottedCount => slottedItemObjects.Count;
public int CurrentSlottedCount => slottedItemObjects.Count(obj => obj != null);
/// <summary>
/// Number of CORRECT items currently slotted
/// </summary>
public int CurrentCorrectCount => slottedItemCorrectness.Count(correct => correct);
public int CurrentCorrectCount
{
get
{
int count = 0;
for (int i = 0; i < slottedItemCorrectness.Count; i++)
{
if (i < slottedItemObjects.Count && slottedItemObjects[i] != null && slottedItemCorrectness[i])
count++;
}
return count;
}
}
/// <summary>
/// Number of items required to complete this slot
@@ -288,16 +311,44 @@ namespace Interactions
// For multi-slots: only allow swap if not complete yet (allows fixing mistakes)
if (!IsMultiSlot || !IsComplete)
{
// LIFO swap - swap with the last item
int lastIndex = CurrentSlottedCount - 1;
var itemToReturn = slottedItemObjects[lastIndex];
var itemDataToReturn = slottedItemsData[lastIndex];
// Determine target index for the new item
int targetIndex = GetTargetSlotIndexForItem(heldItemData);
int indexToSwap;
if (targetIndex >= 0 && targetIndex < slottedItemObjects.Count && slottedItemObjects[targetIndex] != null)
{
// Swap with the item in the assigned slot
indexToSwap = targetIndex;
}
else
{
// LIFO swap - swap with the last non-null item
indexToSwap = -1;
for (int i = slottedItemObjects.Count - 1; i >= 0; i--)
{
if (slottedItemObjects[i] != null)
{
indexToSwap = i;
break;
}
}
if (indexToSwap < 0)
{
// No items to swap (shouldn't happen, but fallback)
SlotItem(heldItemObj, heldItemData);
return false;
}
}
var itemToReturn = slottedItemObjects[indexToSwap];
var itemDataToReturn = slottedItemsData[indexToSwap];
// Step 1: Give old item to follower
FollowerController.TryPickupItem(itemToReturn, itemDataToReturn, dropItem: false);
// Step 2: Remove old item from slot
RemoveItemAtIndex(lastIndex);
RemoveItemAtIndex(indexToSwap);
// Step 3: Slot the new item
SlotItem(heldItemObj, heldItemData);
@@ -317,15 +368,28 @@ namespace Interactions
// Pickup from slot (empty hands) - LIFO removal
if (heldItemData == null)
{
int lastIndex = CurrentSlottedCount - 1;
var itemToPickup = slottedItemObjects[lastIndex];
var itemDataToPickup = slottedItemsData[lastIndex];
// Find last non-null item
int lastIndex = -1;
for (int i = slottedItemObjects.Count - 1; i >= 0; i--)
{
if (slottedItemObjects[i] != null)
{
lastIndex = i;
break;
}
}
// Try to give item to follower
FollowerController.TryPickupItem(itemToPickup, itemDataToPickup, dropItem: false);
if (lastIndex >= 0)
{
var itemToPickup = slottedItemObjects[lastIndex];
var itemDataToPickup = slottedItemsData[lastIndex];
// Remove from slot
RemoveItemAtIndex(lastIndex);
// Try to give item to follower
FollowerController.TryPickupItem(itemToPickup, itemDataToPickup, dropItem: false);
// Remove from slot
RemoveItemAtIndex(lastIndex);
}
// Just picked up from slot - not a success
return false;
@@ -343,7 +407,16 @@ namespace Interactions
/// </summary>
private void ClearSlot()
{
var previousData = slottedItemsData.Count > 0 ? slottedItemsData[0] : null;
// Find first non-null data for event
PickupItemData previousData = null;
foreach (var data in slottedItemsData)
{
if (data != null)
{
previousData = data;
break;
}
}
// Clear all pickup's OwningSlot references
foreach (var itemObj in slottedItemObjects)
@@ -375,7 +448,7 @@ namespace Interactions
/// </summary>
private void RemoveItemAtIndex(int index)
{
if (index < 0 || index >= CurrentSlottedCount)
if (index < 0 || index >= slottedItemObjects.Count)
return;
var itemObj = slottedItemObjects[index];
@@ -391,9 +464,10 @@ namespace Interactions
}
}
slottedItemObjects.RemoveAt(index);
slottedItemsData.RemoveAt(index);
slottedItemCorrectness.RemoveAt(index); // Also remove correctness tracking
// Set to null instead of removing to maintain sparse storage
slottedItemObjects[index] = null;
slottedItemsData[index] = null;
slottedItemCorrectness[index] = false;
if (CurrentSlottedCount == 0)
{
@@ -412,6 +486,74 @@ namespace Interactions
#region Visual Updates
/// <summary>
/// Determines the best slot index for an item based on assignments and availability.
/// Returns -1 if no mappings are configured (uses append behavior).
/// </summary>
private int GetTargetSlotIndexForItem(PickupItemData itemData)
{
if (slottedItemRenderers == null || slottedItemRenderers.Length == 0)
return -1;
// Check if any renderer has assignments configured
bool hasAnyAssignments = false;
for (int i = 0; i < slottedItemRenderers.Length; i++)
{
if (slottedItemRenderers[i]?.assignedItem != null)
{
hasAnyAssignments = true;
break;
}
}
// If no assignments configured, use append behavior (backward compatible)
if (!hasAnyAssignments)
return -1;
// Step 1: Check if this item has a dedicated assigned slot
for (int i = 0; i < slottedItemRenderers.Length; i++)
{
var mapping = slottedItemRenderers[i];
if (mapping?.assignedItem != null && PickupItemData.AreEquivalent(mapping.assignedItem, itemData))
{
// This is the assigned slot for this item
return i;
}
}
// Step 2: Item is not assigned to a specific slot - find best available slot
// Ensure lists are large enough to check
while (slottedItemObjects.Count < slottedItemRenderers.Length)
{
slottedItemObjects.Add(null);
slottedItemsData.Add(null);
slottedItemCorrectness.Add(false);
}
// Prefer unassigned empty slots first
for (int i = 0; i < slottedItemRenderers.Length; i++)
{
var mapping = slottedItemRenderers[i];
if (mapping?.assignedItem == null && slottedItemObjects[i] == null)
{
return i; // Empty unassigned slot
}
}
// Then try assigned but empty slots
for (int i = 0; i < slottedItemRenderers.Length; i++)
{
var mapping = slottedItemRenderers[i];
if (mapping?.assignedItem != null && slottedItemObjects[i] == null)
{
return i; // Empty assigned slot
}
}
// All slots full - return -1 to trigger swap logic
return -1;
}
/// <summary>
/// Updates the sprite and scale for all slotted items.
/// </summary>
@@ -423,10 +565,12 @@ namespace Interactions
// Update each renderer based on slotted items
for (int i = 0; i < slottedItemRenderers.Length; i++)
{
var slotRenderer = slottedItemRenderers[i];
if (slotRenderer == null)
var mapping = slottedItemRenderers[i];
if (mapping == null || mapping.renderer == null)
continue;
var slotRenderer = mapping.renderer;
// If we have an item at this index, show it
if (i < slottedItemsData.Count && slottedItemsData[i] != null)
{
@@ -645,7 +789,7 @@ namespace Interactions
/// <summary>
/// Public API for slotting items during gameplay.
/// Adds item to the slot (multi-slot support).
/// Adds item to the slot (multi-slot support with positional awareness).
/// Caller is responsible for managing follower's held item state.
/// </summary>
public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData)
@@ -661,10 +805,31 @@ namespace Interactions
var allowed = config?.allowedItems ?? new List<PickupItemData>();
bool isCorrectItem = PickupItemData.ListContainsEquivalent(allowed, itemToSlotData);
// Add to lists
slottedItemObjects.Add(itemToSlot);
slottedItemsData.Add(itemToSlotData);
slottedItemCorrectness.Add(isCorrectItem); // Track correctness
// Determine target slot index
int targetIndex = GetTargetSlotIndexForItem(itemToSlotData);
if (targetIndex >= 0)
{
// Positional slotting - ensure lists are large enough
while (slottedItemObjects.Count <= targetIndex)
{
slottedItemObjects.Add(null);
slottedItemsData.Add(null);
slottedItemCorrectness.Add(false);
}
// Place at specific index
slottedItemObjects[targetIndex] = itemToSlot;
slottedItemsData[targetIndex] = itemToSlotData;
slottedItemCorrectness[targetIndex] = isCorrectItem;
}
else
{
// Append behavior (backward compatible or when all slots full)
slottedItemObjects.Add(itemToSlot);
slottedItemsData.Add(itemToSlotData);
slottedItemCorrectness.Add(isCorrectItem);
}
// Deactivate item and set pickup state
itemToSlot.SetActive(false);

View File

@@ -26,6 +26,10 @@ namespace Minigames.StatueDressup.Controllers
[SerializeField] private GameObject statue;
[SerializeField] private DecorationDraggableInstance draggablePrefab; // Prefab for spawning decorations
[Header("Edit UI")]
[SerializeField] private UI.DecorationEditUI editUIPrefab; // Prefab for edit UI
private UI.DecorationEditUI _editUIInstance;
[Header("UI Pages")]
[SerializeField] private UI.PlayAreaPage playAreaPage;
[SerializeField] private UI.PhotoGalleryPage photoGalleryPage;
@@ -37,8 +41,8 @@ namespace Minigames.StatueDressup.Controllers
[Header("Photo Settings")]
[SerializeField] private RectTransform photoArea; // Area to capture
private List<DecorationDraggableInstance> placedDecorations = new List<DecorationDraggableInstance>();
private bool minigameCompleted;
private List<DecorationDraggableInstance> _placedDecorations = new List<DecorationDraggableInstance>();
private bool _minigameCompleted;
// Public properties
public Transform StatueParent => statueParent;
@@ -127,9 +131,9 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
public void RegisterDecoration(DecorationDraggableInstance decoration)
{
if (decoration != null && !placedDecorations.Contains(decoration))
if (decoration != null && !_placedDecorations.Contains(decoration))
{
placedDecorations.Add(decoration);
_placedDecorations.Add(decoration);
Logging.Debug($"[StatueDecorationController] Decoration placed: {decoration.Data?.DecorationName}");
// Auto-save state
@@ -142,9 +146,9 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
public void UnregisterDecoration(DecorationDraggableInstance decoration)
{
if (decoration != null && placedDecorations.Contains(decoration))
if (decoration != null && _placedDecorations.Contains(decoration))
{
placedDecorations.Remove(decoration);
_placedDecorations.Remove(decoration);
Logging.Debug($"[StatueDecorationController] Decoration removed: {decoration.Data?.DecorationName}");
// Auto-save state
@@ -157,7 +161,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private void OnTakePhoto()
{
if (minigameCompleted)
if (_minigameCompleted)
{
Logging.Debug("[StatueDecorationController] Minigame already completed");
return;
@@ -174,7 +178,7 @@ namespace Minigames.StatueDressup.Controllers
/// </summary>
private System.Collections.IEnumerator CapturePhotoCoroutine()
{
int decorationCount = placedDecorations.Count;
int decorationCount = _placedDecorations.Count;
bool captureSuccess = false;
string savedPhotoId = null;
@@ -222,7 +226,7 @@ namespace Minigames.StatueDressup.Controllers
// Show completion feedback
ShowCompletionFeedback();
minigameCompleted = true;
_minigameCompleted = true;
}
/// <summary>
@@ -290,7 +294,7 @@ namespace Minigames.StatueDressup.Controllers
};
// Collect all decoration placements
foreach (var decoration in placedDecorations)
foreach (var decoration in _placedDecorations)
{
if (decoration == null || decoration.Data == null) continue;
@@ -455,6 +459,38 @@ namespace Minigames.StatueDressup.Controllers
return true;
}
/// <summary>
/// Show edit UI for a placed decoration
/// </summary>
public void ShowEditUI(DecorationDraggableInstance decoration)
{
if (decoration == null)
{
Logging.Warning("[StatueDecorationController] Cannot show edit UI - decoration is null");
return;
}
// Create edit UI instance if needed
if (_editUIInstance == null)
{
if (editUIPrefab == null)
{
Logging.Error("[StatueDecorationController] Edit UI prefab is not assigned!");
return;
}
// Instantiate as child of canvas (find appropriate parent)
Transform canvasTransform = statueArea != null ? statueArea.root : transform.root;
_editUIInstance = Instantiate(editUIPrefab, canvasTransform);
_editUIInstance.transform.SetAsLastSibling(); // Ensure it's on top
Logging.Debug("[StatueDecorationController] Created edit UI instance");
}
// Show the UI
_editUIInstance.Show(decoration);
}
/// <summary>
/// Cleanup when controller is destroyed
/// </summary>

View File

@@ -21,36 +21,36 @@ namespace Minigames.StatueDressup.DragDrop
[SerializeField] private Image decorationImage;
[SerializeField] private CanvasGroup canvasGroup;
private DecorationData decorationData;
private RectTransform rectTransform;
private Canvas canvas;
private Transform canvasParent; // Parent transform for dragging (usually canvas or draggable container)
private RectTransform statueOutline;
private Transform statueParent;
private StatueDecorationController controller;
private AppleHills.Core.Settings.IStatueDressupSettings settings;
private System.Action onFinishedCallback;
private System.Action onShowOutlineCallback;
private System.Action onHideOutlineCallback;
private DecorationData _decorationData;
private RectTransform _rectTransform;
private Canvas _canvas;
private Transform _canvasParent; // Parent transform for dragging (usually canvas or draggable container)
private RectTransform _statueOutline;
private Transform _statueParent;
private StatueDecorationController _controller;
private AppleHills.Core.Settings.IStatueDressupSettings _settings;
private System.Action _onFinishedCallback;
private System.Action _onShowOutlineCallback;
private System.Action _onHideOutlineCallback;
private bool isDragging;
private bool isPlacedOnStatue;
private Vector3 dragOffset;
private bool dragStarted; // Track if drag actually started (vs just a click)
private bool _isDragging;
private bool _isPlacedOnStatue;
private Vector3 _dragOffset;
private bool _dragStarted; // Track if drag actually started (vs just a click)
// Properties
public DecorationData Data => decorationData;
public bool IsPlacedOnStatue => isPlacedOnStatue;
public DecorationData Data => _decorationData;
public bool IsPlacedOnStatue => _isPlacedOnStatue;
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
canvas = GetComponentInParent<Canvas>();
_rectTransform = GetComponent<RectTransform>();
_canvas = GetComponentInParent<Canvas>();
// Store initial parent for dragging context
if (transform.parent != null)
{
canvasParent = transform.parent;
_canvasParent = transform.parent;
}
if (canvasGroup == null)
@@ -76,24 +76,24 @@ namespace Minigames.StatueDressup.DragDrop
return;
}
decorationData = context.Data;
statueOutline = context.StatueOutline;
statueParent = context.StatueParent;
controller = context.Controller;
settings = context.Settings;
onFinishedCallback = context.OnFinished;
onShowOutlineCallback = context.OnShowOutline;
onHideOutlineCallback = context.OnHideOutline;
_decorationData = context.Data;
_statueOutline = context.StatueOutline;
_statueParent = context.StatueParent;
_controller = context.Controller;
_settings = context.Settings;
_onFinishedCallback = context.OnFinished;
_onShowOutlineCallback = context.OnShowOutline;
_onHideOutlineCallback = context.OnHideOutline;
// Handle placed vs new drag
if (context.IsPlaced)
{
isPlacedOnStatue = true;
isDragging = false;
statueParent = transform.parent; // Already parented to statue
_isPlacedOnStatue = true;
_isDragging = false;
_statueParent = transform.parent; // Already parented to statue
if (context.CanvasParent != null)
{
canvasParent = context.CanvasParent;
_canvasParent = context.CanvasParent;
}
}
@@ -104,9 +104,9 @@ namespace Minigames.StatueDressup.DragDrop
}
// Set authored size
if (rectTransform != null && context.Data != null)
if (_rectTransform != null && context.Data != null)
{
rectTransform.sizeDelta = context.Data.AuthoredSize;
_rectTransform.sizeDelta = context.Data.AuthoredSize;
}
// Make interactive if placed (so it can be picked up)
@@ -154,22 +154,22 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
public void StartDragFromIcon(PointerEventData eventData)
{
isDragging = true;
_isDragging = true;
// Broadcast started dragging event (from grid)
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false);
var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationStartedDragging(eventDataObj);
// Calculate offset from cursor to object center
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform,
_canvas.transform as RectTransform,
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
dragOffset = rectTransform.localPosition - (Vector3)localPoint;
_dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
Logging.Debug($"[DecorationDraggableInstance] Started drag from icon: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Started drag from icon: {_decorationData?.DecorationName}");
}
/// <summary>
@@ -177,16 +177,16 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
public void ContinueDrag(PointerEventData eventData)
{
if (!isDragging) return;
if (!_isDragging) return;
// Update position to follow cursor
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform,
_canvas.transform as RectTransform,
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
rectTransform.localPosition = localPoint + (Vector2)dragOffset;
_rectTransform.localPosition = localPoint + (Vector2)_dragOffset;
}
/// <summary>
@@ -194,12 +194,12 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
public void EndDrag(PointerEventData eventData)
{
isDragging = false;
_isDragging = false;
Logging.Debug($"[DecorationDraggableInstance] Drag ended: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Drag ended: {_decorationData?.DecorationName}");
// Broadcast finished dragging event
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: isPlacedOnStatue);
var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: _isPlacedOnStatue);
DecorationEventsManager.BroadcastDecorationFinishedDragging(eventDataObj);
// Check if overlapping with statue
@@ -218,20 +218,20 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
private bool IsOverlappingStatue()
{
if (statueOutline == null || rectTransform == null)
if (_statueOutline == null || _rectTransform == null)
{
Logging.Warning($"[DecorationDraggableInstance] Cannot check overlap - statueOutline or RectTransform is null");
return false;
}
// Get bounds of this item in world space
Rect itemRect = GetWorldRect(rectTransform);
Rect outlineRect = GetWorldRect(statueOutline);
Rect itemRect = GetWorldRect(_rectTransform);
Rect outlineRect = GetWorldRect(_statueOutline);
// Check for any overlap
bool overlaps = itemRect.Overlaps(outlineRect);
Logging.Debug($"[DecorationDraggableInstance] Overlap check: {decorationData?.DecorationName}, overlaps={overlaps}");
Logging.Debug($"[DecorationDraggableInstance] Overlap check: {_decorationData?.DecorationName}, overlaps={overlaps}");
return overlaps;
}
@@ -255,28 +255,28 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
private void PlaceOnStatue()
{
Logging.Debug($"[DecorationDraggableInstance] Placing on statue: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Placing on statue: {_decorationData?.DecorationName}");
isPlacedOnStatue = true;
_isPlacedOnStatue = true;
// Broadcast dropped on statue event
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false);
var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationDroppedOnStatue(eventDataObj);
// Move to statue parent if specified
if (statueParent != null && transform.parent != statueParent)
if (_statueParent != null && transform.parent != _statueParent)
{
transform.SetParent(statueParent, true); // Keep world position
transform.SetParent(_statueParent, true); // Keep world position
}
// Register with controller
if (controller != null)
if (_controller != null)
{
controller.RegisterDecoration(this);
_controller.RegisterDecoration(this);
}
// Notify menu controller to hide outline
onFinishedCallback?.Invoke();
_onFinishedCallback?.Invoke();
}
/// <summary>
@@ -284,22 +284,22 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
private void PlayPopOutAndDestroy()
{
Logging.Debug($"[DecorationDraggableInstance] Pop-out and destroy: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Pop-out and destroy: {_decorationData?.DecorationName}");
// Broadcast dropped out event (animation starting)
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false);
var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationDroppedOut(eventDataObj);
// Notify menu controller to hide outline immediately
onFinishedCallback?.Invoke();
_onFinishedCallback?.Invoke();
float duration = settings?.PlacementAnimationDuration ?? StatueDressupConstants.DefaultAnimationDuration;
float duration = _settings?.PlacementAnimationDuration ?? StatueDressupConstants.DefaultAnimationDuration;
// Play pop-out with fade animation
TweenAnimationUtility.PopOutWithFade(transform, canvasGroup, duration, () =>
{
// Broadcast finished dropping out event (animation complete)
var finalEventData = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: false);
var finalEventData = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: false);
DecorationEventsManager.BroadcastDecorationFinishedDroppingOut(finalEventData);
Destroy(gameObject);
@@ -311,26 +311,26 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
public void StartDragFromStatue(PointerEventData eventData)
{
Logging.Debug($"[DecorationDraggableInstance] StartDragFromStatue called for: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Show outline callback is null: {onShowOutlineCallback == null}");
Logging.Debug($"[DecorationDraggableInstance] StartDragFromStatue called for: {_decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Show outline callback is null: {_onShowOutlineCallback == null}");
if (controller != null)
if (_controller != null)
{
controller.UnregisterDecoration(this);
_controller.UnregisterDecoration(this);
}
isPlacedOnStatue = false;
isDragging = true;
_isPlacedOnStatue = false;
_isDragging = true;
// Broadcast started dragging event (from statue)
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: true);
var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: true);
DecorationEventsManager.BroadcastDecorationStartedDragging(eventDataObj);
// Show statue outline when picking up from statue
if (onShowOutlineCallback != null)
if (_onShowOutlineCallback != null)
{
Logging.Debug("[DecorationDraggableInstance] Invoking show outline callback");
onShowOutlineCallback.Invoke();
_onShowOutlineCallback.Invoke();
}
else
{
@@ -338,24 +338,24 @@ namespace Minigames.StatueDressup.DragDrop
}
// Reparent to canvas for dragging (so coordinates work correctly)
if (canvasParent != null && transform.parent != canvasParent)
if (_canvasParent != null && transform.parent != _canvasParent)
{
// Store world position before reparenting
Vector3 worldPos = transform.position;
transform.SetParent(canvasParent, false);
transform.SetParent(_canvasParent, false);
transform.position = worldPos; // Restore world position
}
// Calculate offset using proper camera
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform,
_canvas.transform as RectTransform,
eventData.position,
eventData.pressEventCamera,
out Vector2 localPoint);
dragOffset = rectTransform.localPosition - (Vector3)localPoint;
_dragOffset = _rectTransform.localPosition - (Vector3)localPoint;
Logging.Debug($"[DecorationDraggableInstance] Started drag from statue: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Started drag from statue: {_decorationData?.DecorationName}");
}
#region Pointer Event Handlers
@@ -366,15 +366,23 @@ namespace Minigames.StatueDressup.DragDrop
public void OnPointerClick(PointerEventData eventData)
{
// Only handle clicks when placed on statue and not currently dragging
if (!isPlacedOnStatue || dragStarted) return;
if (!_isPlacedOnStatue || _dragStarted) return;
Logging.Debug($"[DecorationDraggableInstance] Decoration tapped: {decorationData?.DecorationName}");
Logging.Debug($"[DecorationDraggableInstance] Decoration tapped: {_decorationData?.DecorationName}");
// Broadcast tap event
var eventDataObj = new DecorationEventData(decorationData, gameObject, transform.position, fromStatue: true);
var eventDataObj = new DecorationEventData(_decorationData, gameObject, transform.position, fromStatue: true);
DecorationEventsManager.BroadcastDecorationTappedOnStatue(eventDataObj);
// Future: Open detail view, play sound effect, show info popup, etc.
// Show edit UI
if (_controller != null)
{
_controller.ShowEditUI(this);
}
else
{
Logging.Warning("[DecorationDraggableInstance] Cannot show edit UI - controller reference is null");
}
}
/// <summary>
@@ -383,9 +391,9 @@ namespace Minigames.StatueDressup.DragDrop
public void OnBeginDrag(PointerEventData eventData)
{
// Only handle drag from statue if already placed
if (!isPlacedOnStatue) return;
if (!_isPlacedOnStatue) return;
dragStarted = true;
_dragStarted = true;
StartDragFromStatue(eventData);
}
@@ -394,7 +402,7 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
public void OnDrag(PointerEventData eventData)
{
if (!isDragging) return;
if (!_isDragging) return;
ContinueDrag(eventData);
}
@@ -404,9 +412,9 @@ namespace Minigames.StatueDressup.DragDrop
/// </summary>
public void OnEndDrag(PointerEventData eventData)
{
if (!isDragging) return;
if (!_isDragging) return;
dragStarted = false;
_dragStarted = false;
EndDrag(eventData);
}

View File

@@ -0,0 +1,288 @@
using Core;
using UnityEngine;
using UnityEngine.UI;
using Minigames.StatueDressup.DragDrop;
namespace Minigames.StatueDressup.UI
{
/// <summary>
/// UI panel for editing a placed decoration's scale and rotation.
/// Shows sliders for scale (0.1x - 2x) and rotation (-180° to 180°).
/// Changes are applied immediately to the decoration's transform.
/// </summary>
public class DecorationEditUI : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private Slider scaleSlider;
[SerializeField] private Slider rotationSlider;
[SerializeField] private Button confirmButton;
[SerializeField] private Button resetButton;
[SerializeField] private CanvasGroup canvasGroup;
[Header("Slider Ranges")]
[SerializeField] private float minScale = 0.1f;
[SerializeField] private float maxScale = 2.0f;
[SerializeField] private float minRotation = -180f;
[SerializeField] private float maxRotation = 180f;
private DecorationDraggableInstance _targetDecoration;
private RectTransform _rectTransform;
private float _originalRotation;
private bool _isInitialized;
private void Awake()
{
// Get RectTransform
_rectTransform = GetComponent<RectTransform>();
// Ensure canvas group exists
if (canvasGroup == null)
{
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null)
{
canvasGroup = gameObject.AddComponent<CanvasGroup>();
}
}
// Setup slider ranges
if (scaleSlider != null)
{
scaleSlider.minValue = minScale;
scaleSlider.maxValue = maxScale;
scaleSlider.onValueChanged.AddListener(OnScaleChanged);
}
if (rotationSlider != null)
{
rotationSlider.minValue = minRotation;
rotationSlider.maxValue = maxRotation;
rotationSlider.onValueChanged.AddListener(OnRotationChanged);
}
// Setup buttons
if (confirmButton != null)
{
confirmButton.onClick.AddListener(OnConfirm);
}
if (resetButton != null)
{
resetButton.onClick.AddListener(OnReset);
}
// Start hidden
gameObject.SetActive(false);
_isInitialized = true;
}
private void OnDestroy()
{
// Clean up listeners
if (scaleSlider != null)
{
scaleSlider.onValueChanged.RemoveListener(OnScaleChanged);
}
if (rotationSlider != null)
{
rotationSlider.onValueChanged.RemoveListener(OnRotationChanged);
}
if (confirmButton != null)
{
confirmButton.onClick.RemoveListener(OnConfirm);
}
if (resetButton != null)
{
resetButton.onClick.RemoveListener(OnReset);
}
}
/// <summary>
/// Show the edit UI for the given decoration
/// </summary>
public void Show(DecorationDraggableInstance decoration)
{
if (!_isInitialized)
{
Logging.Error("[DecorationEditUI] Attempted to show before initialization!");
return;
}
if (decoration == null)
{
Logging.Error("[DecorationEditUI] Cannot show edit UI - decoration is null!");
return;
}
_targetDecoration = decoration;
// Store original rotation for reference
_originalRotation = decoration.transform.localEulerAngles.z;
// Normalize rotation to -180 to 180 range
if (_originalRotation > 180f)
{
_originalRotation -= 360f;
}
// Initialize sliders from current transform values
if (scaleSlider != null)
{
// Use X component for uniform scale
float currentScale = decoration.transform.localScale.x;
scaleSlider.value = Mathf.Clamp(currentScale, minScale, maxScale);
}
if (rotationSlider != null)
{
rotationSlider.value = Mathf.Clamp(_originalRotation, minRotation, maxRotation);
}
// Disable decoration raycasts during editing
CanvasGroup decorationCanvasGroup = decoration.GetComponent<CanvasGroup>();
if (decorationCanvasGroup != null)
{
decorationCanvasGroup.blocksRaycasts = false;
}
// Position UI centered over the decoration (context menu style)
PositionOverDecoration(decoration);
// Show UI immediately
gameObject.SetActive(true);
if (canvasGroup != null)
{
canvasGroup.alpha = 1f;
}
Logging.Debug($"[DecorationEditUI] Showing edit UI for: {decoration.Data?.DecorationName}");
}
/// <summary>
/// Position the UI centered over the decoration (context menu style)
/// </summary>
private void PositionOverDecoration(DecorationDraggableInstance decoration)
{
if (_rectTransform == null || decoration == null) return;
// Get decoration's world position
Vector3 decorationWorldPos = decoration.transform.position;
// Convert to canvas space if using screen space overlay
Canvas canvas = GetComponentInParent<Canvas>();
if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay)
{
// For overlay canvas, world position is already correct
_rectTransform.position = decorationWorldPos;
}
else if (canvas != null)
{
// For other canvas modes, convert properly
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvas.transform as RectTransform,
RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, decorationWorldPos),
canvas.worldCamera,
out Vector2 localPoint
);
_rectTransform.localPosition = localPoint;
}
Logging.Debug($"[DecorationEditUI] Positioned at decoration location: {decorationWorldPos}");
}
/// <summary>
/// Hide the edit UI
/// </summary>
public void Hide()
{
if (_targetDecoration != null)
{
// Re-enable decoration raycasts
CanvasGroup decorationCanvasGroup = _targetDecoration.GetComponent<CanvasGroup>();
if (decorationCanvasGroup != null)
{
decorationCanvasGroup.blocksRaycasts = true;
}
}
_targetDecoration = null;
gameObject.SetActive(false);
Logging.Debug("[DecorationEditUI] Edit UI hidden");
}
/// <summary>
/// Handle scale slider change
/// </summary>
private void OnScaleChanged(float value)
{
if (_targetDecoration == null) return;
// Apply uniform scale (X, Y, Z all the same)
_targetDecoration.transform.localScale = Vector3.one * value;
}
/// <summary>
/// Handle rotation slider change
/// </summary>
private void OnRotationChanged(float value)
{
if (_targetDecoration == null) return;
// Apply Z rotation only
_targetDecoration.transform.localEulerAngles = new Vector3(0f, 0f, value);
}
/// <summary>
/// Handle confirm button - save changes and close
/// </summary>
private void OnConfirm()
{
if (_targetDecoration != null)
{
// Trigger auto-save through the controller
var controller = Controllers.StatueDecorationController.Instance;
if (controller != null)
{
// The controller's RegisterDecoration already triggers SaveStatueState
// Since the decoration is already registered, we just need to trigger a save
// This happens automatically on the next RegisterDecoration/UnregisterDecoration call
Logging.Debug("[DecorationEditUI] Changes confirmed - will be auto-saved");
}
}
Hide();
}
/// <summary>
/// Handle reset button - restore original values
/// </summary>
private void OnReset()
{
if (_targetDecoration == null) return;
// Reset to authored size (scale 1.0) and 0 rotation
float defaultScale = 1.0f;
float defaultRotation = 0f;
if (scaleSlider != null)
{
scaleSlider.value = defaultScale;
}
if (rotationSlider != null)
{
rotationSlider.value = defaultRotation;
}
// Values are applied through the slider callbacks
Logging.Debug("[DecorationEditUI] Reset to defaults");
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7f3e2a1b9c4d5e6f7a8b9c0d1e2f3a4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: