Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
MDPro3
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
6
Issues
6
List
Boards
Labels
Service Desk
Milestones
Merge Requests
4
Merge Requests
4
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
赤子奈落
MDPro3
Commits
2a369a96
Commit
2a369a96
authored
Mar 08, 2026
by
Senator John
💬
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'patch' into 'master'
Bugs Fixed See merge request
!27
parents
58147aea
fc945e10
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
1601 additions
and
209 deletions
+1601
-209
Assets/Prefabs/UI/PopupDuelSelection.prefab
Assets/Prefabs/UI/PopupDuelSelection.prefab
+1
-1
Assets/Scripts/MDPro3/CardRenderer.cs
Assets/Scripts/MDPro3/CardRenderer.cs
+1215
-118
Assets/Scripts/MDPro3/Duel/BG/DuelBGManager.cs
Assets/Scripts/MDPro3/Duel/BG/DuelBGManager.cs
+15
-9
Assets/Scripts/MDPro3/Duel/CardDescription.cs
Assets/Scripts/MDPro3/Duel/CardDescription.cs
+11
-7
Assets/Scripts/MDPro3/Duel/GameCard.cs
Assets/Scripts/MDPro3/Duel/GameCard.cs
+14
-4
Assets/Scripts/MDPro3/Duel/YGOSharp/Card.cs
Assets/Scripts/MDPro3/Duel/YGOSharp/Card.cs
+23
-4
Assets/Scripts/MDPro3/Duel/YGOSharp/Deck.cs
Assets/Scripts/MDPro3/Duel/YGOSharp/Deck.cs
+23
-1
Assets/Scripts/MDPro3/Net/OnlineService.cs
Assets/Scripts/MDPro3/Net/OnlineService.cs
+26
-12
Assets/Scripts/MDPro3/Servant/Appearance.cs
Assets/Scripts/MDPro3/Servant/Appearance.cs
+8
-8
Assets/Scripts/MDPro3/Servant/OcgCore.cs
Assets/Scripts/MDPro3/Servant/OcgCore.cs
+3
-3
Assets/Scripts/MDPro3/System/UserInput.cs
Assets/Scripts/MDPro3/System/UserInput.cs
+6
-5
Assets/Scripts/MDPro3/UI/Popup/Old/PopupDuelSelection.cs
Assets/Scripts/MDPro3/UI/Popup/Old/PopupDuelSelection.cs
+32
-2
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionButton_MainMenu.cs
...pts/MDPro3/UI/SelectionButton/SelectionButton_MainMenu.cs
+75
-3
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionToggle_Deck.cs
...Scripts/MDPro3/UI/SelectionButton/SelectionToggle_Deck.cs
+21
-7
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionToggle_DeckOnline.cs
...s/MDPro3/UI/SelectionButton/SelectionToggle_DeckOnline.cs
+23
-5
Assets/Scripts/MDPro3/UI/UIWidget/Deck/DeckView.cs
Assets/Scripts/MDPro3/UI/UIWidget/Deck/DeckView.cs
+60
-8
Assets/Scripts/MDPro3/UI/UIWidget/SidePanel/ChatPanel.cs
Assets/Scripts/MDPro3/UI/UIWidget/SidePanel/ChatPanel.cs
+11
-6
Assets/Scripts/MDPro3/Utility/CardImageLoader.cs
Assets/Scripts/MDPro3/Utility/CardImageLoader.cs
+34
-6
No files found.
Assets/Prefabs/UI/PopupDuelSelection.prefab
View file @
2a369a96
...
...
@@ -384,7 +384,7 @@ MonoBehaviour:
m_Elasticity
:
0.1
m_Inertia
:
1
m_DecelerationRate
:
0.135
m_ScrollSensitivity
:
10
00
m_ScrollSensitivity
:
10
m_Viewport
:
{
fileID
:
277287946404074816
}
m_HorizontalScrollbar
:
{
fileID
:
0
}
m_VerticalScrollbar
:
{
fileID
:
277287947542810653
}
...
...
Assets/Scripts/MDPro3/CardRenderer.cs
View file @
2a369a96
...
...
@@ -494,6 +494,17 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
private
RectMask2D
_overFrameSideClipL
;
private
RectMask2D
_overFrameSideClipR
;
// Pendulum-only extra continuation clips for the left/right scale parchments.
private
RectTransform
_overFramePendulumCenterClipRt
;
private
RectMask2D
_overFramePendulumCenterClip
;
private
RawImage
_overFramePendulumCenterArt
;
private
RectTransform
_overFramePendulumScaleClipL_Rt
;
private
RectTransform
_overFramePendulumScaleClipR_Rt
;
private
RectMask2D
_overFramePendulumScaleClipL
;
private
RectMask2D
_overFramePendulumScaleClipR
;
private
RawImage
_overFramePendulumScaleArtL
;
private
RawImage
_overFramePendulumScaleArtR
;
// How strong the base-art continuation is inside the parchment
// Tuned to match Konami proxy readability in the effect box (less muddy continuation).
private
const
float
OverFrameTextBgAlpha
=
0.00f
;
...
...
@@ -511,11 +522,19 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
// ────────────────────────────────────────────────
private
const
float
OverFrameFadeTopAlpha
=
0.05f
;
private
const
float
OverFrameFadeBottomAlpha
=
0.11f
;
private
const
float
OverFramePendulumFadeTopAlpha
=
0.025f
;
private
const
float
OverFramePendulumFadeBottomAlpha
=
0.055f
;
// When the parchment background Image exists, use it as the wash overlay (looks like the proxy, not a flat rectangle)
private
const
bool
OverFramePreferSpriteWash
=
true
;
private
const
bool
OverFrameEnableWashOverlay
=
true
;
private
const
float
OverFrameWashSpriteAlpha
=
0.12f
;
private
const
float
OverFramePendulumWashSpriteAlpha
=
0.07f
;
// Pendulum-only extra fade overlays for left/right scale parchment zones.
private
VerticalGradientGraphic
_overFrameEffectFadePendulumC
;
private
VerticalGradientGraphic
_overFrameEffectFadePendulumL
;
private
VerticalGradientGraphic
_overFrameEffectFadePendulumR
;
private
const
float
OverFrameFadePadTop
=
0f
;
private
const
float
OverFrameFadePadBottom
=
0f
;
...
...
@@ -538,13 +557,22 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
if
(
_overFrameTextClipRt
)
_overFrameTextClipRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameArtSideL
)
_overFrameArtSideL
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameArtSideR
)
_overFrameArtSideR
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumCenterArt
)
_overFramePendulumCenterArt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtL
)
_overFramePendulumScaleArtL
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtR
)
_overFramePendulumScaleArtR
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameSideClipL_Rt
)
_overFrameSideClipL_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameSideClipR_Rt
)
_overFrameSideClipR_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumCenterClipRt
)
_overFramePendulumCenterClipRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipL_Rt
)
_overFramePendulumScaleClipL_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipR_Rt
)
_overFramePendulumScaleClipR_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadeMaskRt
)
_overFrameEffectFadeMaskRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFade
)
_overFrameEffectFade
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumC
)
_overFrameEffectFadePendulumC
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumL
)
_overFrameEffectFadePendulumL
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumR
)
_overFrameEffectFadePendulumR
.
gameObject
.
SetActive
(
false
);
}
...
...
@@ -561,6 +589,14 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
return
cardArt
;
}
private
bool
IsPendulumArtImage
(
RawImage
artImage
)
{
return
artImage
!=
null
&&
(
artImage
==
cardArtPendulum
||
artImage
==
cardArtPendulumSquare
||
artImage
==
cardArtPendulumWidth
);
}
// ────────────────────────────────────────────────
// Helper: move overlay out of Mask / RectMask2D so it can extend past art window
...
...
@@ -667,20 +703,29 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
// ────────────────────────────────────────────────
private
Text
GetActiveOcgDescriptionText
()
{
// Pendulum cards keep both description texts active; prefer the pendulum zone when it has content.
if
(
cardDescriptionPendulum
!=
null
&&
cardDescriptionPendulum
.
gameObject
.
activeInHierarchy
&&
!
string
.
IsNullOrEmpty
(
cardDescriptionPendulum
.
text
))
return
cardDescriptionPendulum
;
if
(
cardDescription
!=
null
&&
cardDescription
.
gameObject
.
activeInHierarchy
)
return
cardDescription
;
if
(
cardDescriptionPendulum
!=
null
&&
cardDescriptionPendulum
.
gameObject
.
activeInHierarchy
)
return
cardDescriptionPendulum
;
return
cardDescription
;
}
private
RectTransform
GetEffectBoxRectTransform
()
private
RectTransform
GetEffectBoxRectTransform
(
Text
preferredDesc
=
null
,
bool
allowExplicitOverride
=
true
,
float
minAreaOverTextFactor
=
1.05f
)
{
// 1) Explicit override (preferred): the REAL parchment Image rect.
// This prevents accidentally masking with TMP text rects / padding containers.
if
(
overFrameEffectBoxImage
!=
null
&&
overFrameEffectBoxImage
.
gameObject
!=
null
&&
overFrameEffectBoxImage
.
gameObject
.
activeInHierarchy
)
if
(
allowExplicitOverride
&&
overFrameEffectBoxImage
!=
null
&&
overFrameEffectBoxImage
.
gameObject
!=
null
&&
overFrameEffectBoxImage
.
gameObject
.
activeInHierarchy
)
return
overFrameEffectBoxImage
.
rectTransform
;
var
desc
=
GetActiveOcgDescriptionText
();
var
desc
=
preferredDesc
??
GetActiveOcgDescriptionText
();
if
(
desc
==
null
)
return
null
;
var
descRt
=
desc
.
GetComponent
<
RectTransform
>();
...
...
@@ -758,7 +803,7 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
float
area
=
Mathf
.
Max
(
0.0001f
,
(
oMaxX
-
oMinX
)
*
(
oMaxY
-
oMinY
));
// Must be meaningfully larger than text rect, but not huge
if
(
area
<
dArea
*
1.05f
)
continue
;
if
(
area
<
dArea
*
minAreaOverTextFactor
)
continue
;
if
(
area
>
maxArea
)
continue
;
bool
bordered
=
img
.
type
==
Image
.
Type
.
Sliced
;
...
...
@@ -910,17 +955,545 @@ public OverFrameEffectBoxRectMode overFrameEffectBoxRectMode = OverFrameEffectBo
return
true
;
}
// Converts an arbitrary RectTransform world-rect into frame-normalized coordinates [0..1].
// Unlike TryGetEffectBoxNormalizedRect, this has no sanity filter and is used for pendulum-specific regions.
private
static
bool
TryGetRectNormalizedInFrame
(
RectTransform
frameRt
,
RectTransform
sourceRt
,
out
Rect
rectNrm
)
{
rectNrm
=
default
;
if
(
frameRt
==
null
||
sourceRt
==
null
)
return
false
;
var
fc
=
new
Vector3
[
4
];
frameRt
.
GetWorldCorners
(
fc
);
// 0=BL,1=TL,2=TR,3=BR
Vector3
bl
=
fc
[
0
];
Vector3
br
=
fc
[
3
];
Vector3
tl
=
fc
[
1
];
Vector3
w
=
br
-
bl
;
Vector3
h
=
tl
-
bl
;
float
wLen2
=
Vector3
.
Dot
(
w
,
w
);
float
hLen2
=
Vector3
.
Dot
(
h
,
h
);
if
(
wLen2
<
1e-6f
||
hLen2
<
1e-6f
)
return
false
;
var
sc
=
new
Vector3
[
4
];
sourceRt
.
GetWorldCorners
(
sc
);
float
minX
=
999f
,
minY
=
999f
;
float
maxX
=
-
999f
,
maxY
=
-
999f
;
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
Vector3
rel
=
sc
[
i
]
-
bl
;
float
nx
=
Vector3
.
Dot
(
rel
,
w
)
/
wLen2
;
float
ny
=
Vector3
.
Dot
(
rel
,
h
)
/
hLen2
;
minX
=
Mathf
.
Min
(
minX
,
nx
);
minY
=
Mathf
.
Min
(
minY
,
ny
);
maxX
=
Mathf
.
Max
(
maxX
,
nx
);
maxY
=
Mathf
.
Max
(
maxY
,
ny
);
}
minX
=
Mathf
.
Clamp01
(
minX
);
minY
=
Mathf
.
Clamp01
(
minY
);
maxX
=
Mathf
.
Clamp01
(
maxX
);
maxY
=
Mathf
.
Clamp01
(
maxY
);
float
width
=
Mathf
.
Max
(
0f
,
maxX
-
minX
);
float
height
=
Mathf
.
Max
(
0f
,
maxY
-
minY
);
if
(
width
<
0.001f
||
height
<
0.001f
)
return
false
;
rectNrm
=
new
Rect
(
minX
,
minY
,
width
,
height
);
return
true
;
}
private
static
Rect
ExpandNormalizedRect
(
Rect
rectNrm
,
float
padLeft
,
float
padRight
,
float
padTop
,
float
padBottom
)
{
float
xMin
=
Mathf
.
Clamp01
(
rectNrm
.
xMin
-
padLeft
);
float
xMax
=
Mathf
.
Clamp01
(
rectNrm
.
xMin
+
rectNrm
.
width
+
padRight
);
float
yMin
=
Mathf
.
Clamp01
(
rectNrm
.
yMin
-
padBottom
);
float
yMax
=
Mathf
.
Clamp01
(
rectNrm
.
yMin
+
rectNrm
.
height
+
padTop
);
float
w
=
Mathf
.
Max
(
0f
,
xMax
-
xMin
);
float
h
=
Mathf
.
Max
(
0f
,
yMax
-
yMin
);
return
new
Rect
(
xMin
,
yMin
,
w
,
h
);
}
private
bool
TryGetImageBackedZoneNormalized
(
RectTransform
frameRt
,
Text
zoneText
,
out
RectTransform
zoneBoxRt
,
out
Rect
zoneNrm
)
{
zoneBoxRt
=
null
;
zoneNrm
=
default
;
if
(
frameRt
==
null
||
zoneText
==
null
||
!
zoneText
.
gameObject
.
activeInHierarchy
)
return
false
;
float
minAreaFactor
=
(
zoneText
==
lScale
||
zoneText
==
rScale
)
?
0.92f
:
1.02f
;
var
candidateRt
=
GetEffectBoxRectTransform
(
zoneText
,
false
,
minAreaFactor
);
if
(
candidateRt
==
null
)
return
false
;
var
img
=
candidateRt
.
GetComponent
<
Image
>();
if
(
img
==
null
||
img
.
sprite
==
null
)
return
false
;
if
(!
TryGetRectNormalizedInFrame
(
frameRt
,
candidateRt
,
out
zoneNrm
))
return
false
;
if
(
zoneNrm
.
width
<=
0.001f
||
zoneNrm
.
height
<=
0.001f
)
return
false
;
zoneBoxRt
=
candidateRt
;
return
true
;
}
private
static
bool
IsLikelyMatchingPendulumZone
(
Rect
fallbackZoneNrm
,
Rect
imageZoneNrm
)
{
if
(
fallbackZoneNrm
.
width
<=
0.001f
||
fallbackZoneNrm
.
height
<=
0.001f
||
imageZoneNrm
.
width
<=
0.001f
||
imageZoneNrm
.
height
<=
0.001f
)
return
false
;
float
widthRatio
=
imageZoneNrm
.
width
/
Mathf
.
Max
(
0.0001f
,
fallbackZoneNrm
.
width
);
float
heightRatio
=
imageZoneNrm
.
height
/
Mathf
.
Max
(
0.0001f
,
fallbackZoneNrm
.
height
);
if
(
widthRatio
<
0.55f
||
widthRatio
>
1.12f
||
heightRatio
<
0.55f
||
heightRatio
>
1.20f
)
return
false
;
float
ixMin
=
Mathf
.
Max
(
fallbackZoneNrm
.
xMin
,
imageZoneNrm
.
xMin
);
float
iyMin
=
Mathf
.
Max
(
fallbackZoneNrm
.
yMin
,
imageZoneNrm
.
yMin
);
float
ixMax
=
Mathf
.
Min
(
fallbackZoneNrm
.
xMax
,
imageZoneNrm
.
xMax
);
float
iyMax
=
Mathf
.
Min
(
fallbackZoneNrm
.
yMax
,
imageZoneNrm
.
yMax
);
float
iArea
=
Mathf
.
Max
(
0f
,
ixMax
-
ixMin
)
*
Mathf
.
Max
(
0f
,
iyMax
-
iyMin
);
float
baseArea
=
Mathf
.
Max
(
0.0001f
,
Mathf
.
Min
(
fallbackZoneNrm
.
width
*
fallbackZoneNrm
.
height
,
imageZoneNrm
.
width
*
imageZoneNrm
.
height
));
float
overlapRatio
=
iArea
/
baseArea
;
float
fallbackCx
=
0.5f
*
(
fallbackZoneNrm
.
xMin
+
fallbackZoneNrm
.
xMax
);
float
imageCx
=
0.5f
*
(
imageZoneNrm
.
xMin
+
imageZoneNrm
.
xMax
);
float
fallbackCy
=
0.5f
*
(
fallbackZoneNrm
.
yMin
+
fallbackZoneNrm
.
yMax
);
float
imageCy
=
0.5f
*
(
imageZoneNrm
.
yMin
+
imageZoneNrm
.
yMax
);
return
overlapRatio
>=
0.40f
&&
Mathf
.
Abs
(
fallbackCx
-
imageCx
)
<=
0.14f
&&
Mathf
.
Abs
(
fallbackCy
-
imageCy
)
<=
0.07f
;
}
private
bool
TryGetPendulumParchmentZonesNormalized
(
RectTransform
frameRt
,
out
Rect
centerZoneNrm
,
out
Rect
leftScaleZoneNrm
,
out
Rect
rightScaleZoneNrm
,
out
Rect
fullBandNrm
,
out
bool
usedImageBoxRects
,
out
RectTransform
centerZoneBoxRt
,
out
RectTransform
leftScaleZoneBoxRt
,
out
RectTransform
rightScaleZoneBoxRt
,
float
separatorGapNrm
)
{
centerZoneNrm
=
default
;
leftScaleZoneNrm
=
default
;
rightScaleZoneNrm
=
default
;
fullBandNrm
=
default
;
usedImageBoxRects
=
false
;
centerZoneBoxRt
=
null
;
leftScaleZoneBoxRt
=
null
;
rightScaleZoneBoxRt
=
null
;
if
(
frameRt
==
null
||
cardDescriptionPendulum
==
null
||
lScale
==
null
||
rScale
==
null
||
!
cardDescriptionPendulum
.
gameObject
.
activeInHierarchy
||
!
lScale
.
gameObject
.
activeInHierarchy
||
!
rScale
.
gameObject
.
activeInHierarchy
||
string
.
IsNullOrEmpty
(
cardDescriptionPendulum
.
text
))
return
false
;
// 1) Prefer REAL parchment Image rects (same behavior as normal overframe seam clipping).
// We intentionally bypass explicit override here because pendulum has separate center/scale parchments.
RectTransform
centerBoxRt
=
null
;
RectTransform
leftBoxRt
=
null
;
RectTransform
rightBoxRt
=
null
;
bool
hasCenterBoxRt
=
TryGetImageBackedZoneNormalized
(
frameRt
,
cardDescriptionPendulum
,
out
centerBoxRt
,
out
var
centerBoxNrm
);
bool
hasLeftBoxRt
=
TryGetImageBackedZoneNormalized
(
frameRt
,
lScale
,
out
leftBoxRt
,
out
var
leftBoxNrm
);
bool
hasRightBoxRt
=
TryGetImageBackedZoneNormalized
(
frameRt
,
rScale
,
out
rightBoxRt
,
out
var
rightBoxNrm
);
if
(
hasCenterBoxRt
&&
hasLeftBoxRt
&&
hasRightBoxRt
)
{
float
imgLeftX0
=
leftBoxNrm
.
xMin
;
float
imgLeftX1
=
leftBoxNrm
.
xMin
+
leftBoxNrm
.
width
;
float
imgCenterX0
=
centerBoxNrm
.
xMin
;
float
imgCenterX1
=
centerBoxNrm
.
xMin
+
centerBoxNrm
.
width
;
float
imgRightX0
=
rightBoxNrm
.
xMin
;
float
imgRightX1
=
rightBoxNrm
.
xMin
+
rightBoxNrm
.
width
;
bool
ordered
=
imgLeftX0
<
imgLeftX1
&&
imgCenterX0
<
imgCenterX1
&&
imgRightX0
<
imgRightX1
&&
imgLeftX0
<
imgCenterX0
&&
imgCenterX1
<
imgRightX1
;
// Side scale boxes are expected to be narrower than the center pendulum text parchment.
bool
shapeLooksLikePendulumParchment
=
leftBoxNrm
.
width
<
centerBoxNrm
.
width
&&
rightBoxNrm
.
width
<
centerBoxNrm
.
width
;
if
(
ordered
&&
shapeLooksLikePendulumParchment
)
{
float
imgYMin
=
Mathf
.
Min
(
centerBoxNrm
.
yMin
,
Mathf
.
Min
(
leftBoxNrm
.
yMin
,
rightBoxNrm
.
yMin
));
float
imgYMax
=
Mathf
.
Max
(
centerBoxNrm
.
yMin
+
centerBoxNrm
.
height
,
Mathf
.
Max
(
leftBoxNrm
.
yMin
+
leftBoxNrm
.
height
,
rightBoxNrm
.
yMin
+
rightBoxNrm
.
height
));
if
(
imgYMax
>
imgYMin
)
{
// With real parchment Image rects, use their exact bounds (no midpoint approximation).
// The border-safe inner cut is handled later by clip/fade inset logic.
leftScaleZoneNrm
=
leftBoxNrm
;
centerZoneNrm
=
centerBoxNrm
;
rightScaleZoneNrm
=
rightBoxNrm
;
centerZoneBoxRt
=
centerBoxRt
;
leftScaleZoneBoxRt
=
leftBoxRt
;
rightScaleZoneBoxRt
=
rightBoxRt
;
float
imgBandXMin
=
Mathf
.
Min
(
leftScaleZoneNrm
.
xMin
,
Mathf
.
Min
(
centerZoneNrm
.
xMin
,
rightScaleZoneNrm
.
xMin
));
float
imgBandXMax
=
Mathf
.
Max
(
leftScaleZoneNrm
.
xMax
,
Mathf
.
Max
(
centerZoneNrm
.
xMax
,
rightScaleZoneNrm
.
xMax
));
fullBandNrm
=
new
Rect
(
imgBandXMin
,
imgYMin
,
imgBandXMax
-
imgBandXMin
,
imgYMax
-
imgYMin
);
usedImageBoxRects
=
true
;
return
fullBandNrm
.
width
>
0.001f
&&
fullBandNrm
.
height
>
0.001f
;
}
}
}
// 2) Fallback: infer parchments from text rects + tuned padding.
if
(!
TryGetRectNormalizedInFrame
(
frameRt
,
cardDescriptionPendulum
.
rectTransform
,
out
var
centerTextNrm
)
||
!
TryGetRectNormalizedInFrame
(
frameRt
,
lScale
.
rectTransform
,
out
var
leftScaleTextNrm
)
||
!
TryGetRectNormalizedInFrame
(
frameRt
,
rScale
.
rectTransform
,
out
var
rightScaleTextNrm
))
return
false
;
Rect
center
=
ExpandNormalizedRect
(
centerTextNrm
,
OverFramePendulumCenterPadSideNrm
,
OverFramePendulumCenterPadSideNrm
,
OverFramePendulumCenterPadTopNrm
,
OverFramePendulumCenterPadBottomNrm
);
float
yMin
=
center
.
yMin
;
float
yMax
=
center
.
yMin
+
center
.
height
;
if
(
yMax
<=
yMin
)
return
false
;
float
leftX0
=
Mathf
.
Clamp01
(
leftScaleTextNrm
.
xMin
-
OverFramePendulumScalePadOuterNrm
);
float
rightX1
=
Mathf
.
Clamp01
(
rightScaleTextNrm
.
xMin
+
rightScaleTextNrm
.
width
+
OverFramePendulumScalePadOuterNrm
);
// In fallback mode, center text rect is narrower than center parchment.
// Derive seam boundaries from the scale box inner edges so fade/clip reaches inner separator walls.
float
centerBoundaryLeft
=
Mathf
.
Clamp01
(
leftScaleTextNrm
.
xMin
+
leftScaleTextNrm
.
width
+
OverFramePendulumScalePadInnerNrm
);
float
centerBoundaryRight
=
Mathf
.
Clamp01
(
rightScaleTextNrm
.
xMin
-
OverFramePendulumScalePadInnerNrm
);
if
(
centerBoundaryRight
<=
centerBoundaryLeft
)
return
false
;
float
separatorHalfGap
=
Mathf
.
Max
(
0f
,
separatorGapNrm
*
0.5f
);
float
leftX1
=
Mathf
.
Clamp01
(
centerBoundaryLeft
-
separatorHalfGap
);
float
centerX0
=
Mathf
.
Clamp01
(
centerBoundaryLeft
+
separatorHalfGap
);
float
centerX1
=
Mathf
.
Clamp01
(
centerBoundaryRight
-
separatorHalfGap
);
float
rightX0
=
Mathf
.
Clamp01
(
centerBoundaryRight
+
separatorHalfGap
);
if
(
leftX1
<=
leftX0
||
centerX1
<=
centerX0
||
rightX1
<=
rightX0
)
return
false
;
leftScaleZoneNrm
=
new
Rect
(
leftX0
,
yMin
,
leftX1
-
leftX0
,
yMax
-
yMin
);
centerZoneNrm
=
new
Rect
(
centerX0
,
yMin
,
centerX1
-
centerX0
,
yMax
-
yMin
);
rightScaleZoneNrm
=
new
Rect
(
rightX0
,
yMin
,
rightX1
-
rightX0
,
yMax
-
yMin
);
// Use REAL parchment Image rects when they clearly match the intended zone role.
// This prevents accidental "full upper band" picks from collapsing all three zones.
float
roleTolX
=
10f
/
704f
;
bool
centerBoxRoleValid
=
hasCenterBoxRt
&&
centerBoxNrm
.
xMin
>=
centerBoundaryLeft
-
roleTolX
&&
centerBoxNrm
.
xMax
<=
centerBoundaryRight
+
roleTolX
&&
centerBoxNrm
.
width
>
0.04f
;
bool
leftBoxRoleValid
=
hasLeftBoxRt
&&
leftBoxNrm
.
xMax
<=
centerBoundaryLeft
+
roleTolX
&&
leftBoxNrm
.
xMin
<=
centerBoundaryLeft
-
roleTolX
&&
leftBoxNrm
.
width
>
0.02f
;
bool
rightBoxRoleValid
=
hasRightBoxRt
&&
rightBoxNrm
.
xMin
>=
centerBoundaryRight
-
roleTolX
&&
rightBoxNrm
.
xMax
>=
centerBoundaryRight
+
roleTolX
&&
rightBoxNrm
.
width
>
0.02f
;
if
(
centerBoxRoleValid
)
{
centerZoneNrm
=
centerBoxNrm
;
centerZoneBoxRt
=
centerBoxRt
;
}
if
(
leftBoxRoleValid
)
{
leftScaleZoneNrm
=
leftBoxNrm
;
leftScaleZoneBoxRt
=
leftBoxRt
;
}
if
(
rightBoxRoleValid
)
{
rightScaleZoneNrm
=
rightBoxNrm
;
rightScaleZoneBoxRt
=
rightBoxRt
;
}
// Lock fallback seam boundaries to adjacent real boxes (when present), otherwise to
// scale-inner fallback boundaries. This removes vertical dead strips at separators.
float
seamLeft
=
centerZoneBoxRt
!=
null
?
centerZoneNrm
.
xMin
:
(
leftScaleZoneBoxRt
!=
null
?
leftScaleZoneNrm
.
xMax
:
centerBoundaryLeft
);
float
seamRight
=
centerZoneBoxRt
!=
null
?
centerZoneNrm
.
xMax
:
(
rightScaleZoneBoxRt
!=
null
?
rightScaleZoneNrm
.
xMin
:
centerBoundaryRight
);
seamLeft
=
Mathf
.
Clamp01
(
seamLeft
);
seamRight
=
Mathf
.
Clamp01
(
seamRight
);
if
(
seamRight
<=
seamLeft
)
return
false
;
if
(
leftScaleZoneBoxRt
==
null
)
leftScaleZoneNrm
=
new
Rect
(
leftScaleZoneNrm
.
xMin
,
leftScaleZoneNrm
.
yMin
,
Mathf
.
Max
(
0f
,
seamLeft
-
leftScaleZoneNrm
.
xMin
),
leftScaleZoneNrm
.
height
);
if
(
centerZoneBoxRt
==
null
)
centerZoneNrm
=
new
Rect
(
seamLeft
,
centerZoneNrm
.
yMin
,
Mathf
.
Max
(
0f
,
seamRight
-
seamLeft
),
centerZoneNrm
.
height
);
if
(
rightScaleZoneBoxRt
==
null
)
rightScaleZoneNrm
=
new
Rect
(
seamRight
,
rightScaleZoneNrm
.
yMin
,
Mathf
.
Max
(
0f
,
rightScaleZoneNrm
.
xMax
-
seamRight
),
rightScaleZoneNrm
.
height
);
if
(
leftScaleZoneNrm
.
width
<=
0.001f
||
centerZoneNrm
.
width
<=
0.001f
||
rightScaleZoneNrm
.
width
<=
0.001f
)
return
false
;
usedImageBoxRects
=
centerZoneBoxRt
!=
null
||
leftScaleZoneBoxRt
!=
null
||
rightScaleZoneBoxRt
!=
null
;
float
bandXMin
=
Mathf
.
Min
(
leftScaleZoneNrm
.
xMin
,
Mathf
.
Min
(
centerZoneNrm
.
xMin
,
rightScaleZoneNrm
.
xMin
));
float
bandXMax
=
Mathf
.
Max
(
leftScaleZoneNrm
.
xMax
,
Mathf
.
Max
(
centerZoneNrm
.
xMax
,
rightScaleZoneNrm
.
xMax
));
float
bandYMin
=
Mathf
.
Min
(
leftScaleZoneNrm
.
yMin
,
Mathf
.
Min
(
centerZoneNrm
.
yMin
,
rightScaleZoneNrm
.
yMin
));
float
bandYMax
=
Mathf
.
Max
(
leftScaleZoneNrm
.
yMax
,
Mathf
.
Max
(
centerZoneNrm
.
yMax
,
rightScaleZoneNrm
.
yMax
));
fullBandNrm
=
new
Rect
(
bandXMin
,
bandYMin
,
bandXMax
-
bandXMin
,
bandYMax
-
bandYMin
);
return
fullBandNrm
.
width
>
0.001f
&&
fullBandNrm
.
height
>
0.001f
;
}
private
void
UpdateMaskedContinuationClip
(
ref
RectTransform
clipRt
,
ref
RectMask2D
clipMask
,
ref
RawImage
artCopy
,
string
clipName
,
string
artName
,
Rect
zoneNrm
,
RectTransform
frameRt
,
RectTransform
clipParentRt
,
RectTransform
artReferenceRt
,
Texture2D
tex
,
Rect
uvRect
,
OverFrameSpec
spec
,
float
alpha
,
float
insetLeft
=
0f
,
float
insetRight
=
0f
,
float
insetTop
=
0f
,
float
insetBottom
=
0f
,
RectTransform
parchmentBoxRt
=
null
,
float
borderCutFactor
=
OverFrameBorderCutFactor
,
float
borderSafetyInset
=
OverFrameBorderSafetyInset
)
{
bool
hasZoneNrm
=
zoneNrm
.
width
>
0.001f
&&
zoneNrm
.
height
>
0.001f
;
bool
hasParchmentBox
=
parchmentBoxRt
!=
null
;
bool
valid
=
tex
!=
null
&&
frameRt
!=
null
&&
clipParentRt
!=
null
&&
artReferenceRt
!=
null
&&
(
hasZoneNrm
||
hasParchmentBox
);
if
(!
valid
)
{
if
(
clipRt
!=
null
)
clipRt
.
gameObject
.
SetActive
(
false
);
if
(
artCopy
!=
null
)
artCopy
.
gameObject
.
SetActive
(
false
);
return
;
}
if
(
clipRt
==
null
)
{
var
go
=
new
GameObject
(
clipName
,
typeof
(
RectTransform
),
typeof
(
RectMask2D
));
go
.
transform
.
SetParent
(
clipParentRt
,
false
);
clipRt
=
go
.
GetComponent
<
RectTransform
>();
clipMask
=
go
.
GetComponent
<
RectMask2D
>();
}
else
if
(
clipRt
.
parent
!=
clipParentRt
)
{
clipRt
.
SetParent
(
clipParentRt
,
false
);
}
clipRt
.
gameObject
.
SetActive
(
true
);
if
(
hasParchmentBox
)
MatchRectByWorldCorners
(
clipRt
,
parchmentBoxRt
,
clipParentRt
);
else
MatchRectToFrameNormalized
(
clipRt
,
frameRt
,
clipParentRt
,
zoneNrm
);
float
borderInsetLeft
=
0f
;
float
borderInsetRight
=
0f
;
float
borderInsetTop
=
0f
;
float
borderInsetBottom
=
0f
;
if
(
hasParchmentBox
&&
TryGetEffectBoxBorderMidWorld
(
parchmentBoxRt
,
out
var
midLeftW
,
out
var
midRightW
,
out
var
midTopW
,
out
var
midBottomW
))
{
midLeftW
*=
borderCutFactor
;
midRightW
*=
borderCutFactor
;
midTopW
*=
borderCutFactor
;
midBottomW
*=
borderCutFactor
;
borderInsetLeft
=
WorldToLocalX
(
clipRt
,
midLeftW
)
+
borderSafetyInset
;
borderInsetRight
=
WorldToLocalX
(
clipRt
,
midRightW
)
+
borderSafetyInset
;
borderInsetTop
=
WorldToLocalY
(
clipRt
,
midTopW
)
+
borderSafetyInset
;
borderInsetBottom
=
WorldToLocalY
(
clipRt
,
midBottomW
)
+
borderSafetyInset
;
}
InsetRect
(
clipRt
,
Mathf
.
Max
(
0f
,
borderInsetLeft
+
insetLeft
),
Mathf
.
Max
(
0f
,
borderInsetRight
+
insetRight
),
Mathf
.
Max
(
0f
,
borderInsetTop
+
insetTop
),
Mathf
.
Max
(
0f
,
borderInsetBottom
+
insetBottom
));
if
(
artCopy
==
null
&&
_overFrameArt
!=
null
)
{
var
clone
=
Instantiate
(
_overFrameArt
,
clipRt
);
clone
.
name
=
artName
;
clone
.
raycastTarget
=
false
;
artCopy
=
clone
;
var
arf
=
artCopy
.
GetComponent
<
AspectRatioFitter
>();
if
(
arf
)
arf
.
enabled
=
false
;
}
else
if
(
artCopy
!=
null
&&
artCopy
.
transform
.
parent
!=
clipRt
)
{
artCopy
.
transform
.
SetParent
(
clipRt
,
false
);
}
if
(
artCopy
==
null
)
return
;
artCopy
.
gameObject
.
SetActive
(
true
);
artCopy
.
texture
=
tex
;
artCopy
.
uvRect
=
uvRect
;
artCopy
.
color
=
new
Color
(
1f
,
1f
,
1f
,
alpha
);
artCopy
.
maskable
=
true
;
MatchRectByWorldCorners
(
artCopy
.
rectTransform
,
artReferenceRt
,
clipRt
);
artCopy
.
rectTransform
.
sizeDelta
*=
spec
.
scale
;
artCopy
.
rectTransform
.
anchoredPosition
+=
spec
.
offset
;
}
private
void
UpdateGradientFadeZone
(
ref
VerticalGradientGraphic
fadeGraphic
,
string
objectName
,
Rect
zoneNrm
,
RectTransform
frameRt
,
RectTransform
fadeParentRt
,
Color
washRgb
,
float
topAlpha
,
float
bottomAlpha
,
float
insetLeft
,
float
insetRight
,
float
insetTop
,
float
insetBottom
,
RectTransform
parchmentBoxRt
=
null
,
float
borderCutFactor
=
OverFrameBorderCutFactor
,
float
borderSafetyInset
=
OverFrameBorderSafetyInset
)
{
bool
hasZoneNrm
=
zoneNrm
.
width
>
0.001f
&&
zoneNrm
.
height
>
0.001f
;
bool
hasParchmentBox
=
parchmentBoxRt
!=
null
;
bool
valid
=
fadeParentRt
!=
null
&&
(
hasParchmentBox
||
(
frameRt
!=
null
&&
hasZoneNrm
));
if
(!
valid
)
{
if
(
fadeGraphic
!=
null
)
fadeGraphic
.
gameObject
.
SetActive
(
false
);
return
;
}
if
(
fadeGraphic
==
null
)
{
var
go
=
new
GameObject
(
objectName
,
typeof
(
RectTransform
),
typeof
(
CanvasRenderer
),
typeof
(
VerticalGradientGraphic
));
go
.
transform
.
SetParent
(
fadeParentRt
,
false
);
fadeGraphic
=
go
.
GetComponent
<
VerticalGradientGraphic
>();
fadeGraphic
.
raycastTarget
=
false
;
}
else
if
(
fadeGraphic
.
transform
.
parent
!=
fadeParentRt
)
{
fadeGraphic
.
transform
.
SetParent
(
fadeParentRt
,
false
);
}
fadeGraphic
.
gameObject
.
SetActive
(
true
);
fadeGraphic
.
topColor
=
new
Color
(
washRgb
.
r
,
washRgb
.
g
,
washRgb
.
b
,
topAlpha
);
fadeGraphic
.
bottomColor
=
new
Color
(
washRgb
.
r
,
washRgb
.
g
,
washRgb
.
b
,
bottomAlpha
);
RectTransform
rt
=
fadeGraphic
.
rectTransform
;
if
(
hasParchmentBox
)
MatchRectByWorldCorners
(
rt
,
parchmentBoxRt
,
fadeParentRt
);
else
MatchRectToFrameNormalized
(
rt
,
frameRt
,
fadeParentRt
,
zoneNrm
);
float
borderInsetLeft
=
0f
;
float
borderInsetRight
=
0f
;
float
borderInsetTop
=
0f
;
float
borderInsetBottom
=
0f
;
if
(
hasParchmentBox
&&
TryGetEffectBoxBorderMidWorld
(
parchmentBoxRt
,
out
var
midLeftW
,
out
var
midRightW
,
out
var
midTopW
,
out
var
midBottomW
))
{
midLeftW
*=
borderCutFactor
;
midRightW
*=
borderCutFactor
;
midTopW
*=
borderCutFactor
;
midBottomW
*=
borderCutFactor
;
borderInsetLeft
=
WorldToLocalX
(
rt
,
midLeftW
)
+
borderSafetyInset
;
borderInsetRight
=
WorldToLocalX
(
rt
,
midRightW
)
+
borderSafetyInset
;
borderInsetTop
=
WorldToLocalY
(
rt
,
midTopW
)
+
borderSafetyInset
;
borderInsetBottom
=
WorldToLocalY
(
rt
,
midBottomW
)
+
borderSafetyInset
;
}
InsetRect
(
rt
,
Mathf
.
Max
(
0f
,
borderInsetLeft
+
insetLeft
),
Mathf
.
Max
(
0f
,
borderInsetRight
+
insetRight
),
Mathf
.
Max
(
0f
,
borderInsetTop
+
insetTop
),
Mathf
.
Max
(
0f
,
borderInsetBottom
+
insetBottom
));
rt
.
sizeDelta
+=
new
Vector2
(
OverFrameFadePadSide
*
2f
,
OverFrameFadePadTop
+
OverFrameFadePadBottom
);
rt
.
anchoredPosition
+=
new
Vector2
(
0f
,
(
OverFrameFadePadTop
-
OverFrameFadePadBottom
)
*
0.5f
);
}
// ────────────────────────────────────────────────
// Effect-box BORDER mid-thickness in WORLD units.
// We use the parchment background sprite's 9-slice border to compute where
// the proxy cuts the art: right in the MIDDLE of the border thickness.
// Returns mid-border distances (left/right/top/bottom) measured in world units.
// ────────────────────────────────────────────────
private
bool
TryGetEffectBoxBorderMidWorld
(
out
float
midLeftW
,
out
float
midRightW
,
out
float
midTopW
,
out
float
midBottomW
)
private
bool
TryGetEffectBoxBorderMidWorld
(
RectTransform
boxRt
,
out
float
midLeftW
,
out
float
midRightW
,
out
float
midTopW
,
out
float
midBottomW
)
{
midLeftW
=
midRightW
=
midTopW
=
midBottomW
=
0f
;
var
boxRt
=
GetEffectBoxRectTransform
();
if
(
boxRt
==
null
)
return
false
;
var
img
=
boxRt
.
GetComponent
<
Image
>();
...
...
@@ -956,6 +1529,11 @@ private bool TryGetEffectBoxBorderMidWorld(out float midLeftW, out float midRigh
return
true
;
}
private
bool
TryGetEffectBoxBorderMidWorld
(
out
float
midLeftW
,
out
float
midRightW
,
out
float
midTopW
,
out
float
midBottomW
)
{
return
TryGetEffectBoxBorderMidWorld
(
GetEffectBoxRectTransform
(),
out
midLeftW
,
out
midRightW
,
out
midTopW
,
out
midBottomW
);
}
private
static
float
WorldToLocalX
(
RectTransform
rt
,
float
worldDist
)
{
if
(
rt
==
null
)
return
0f
;
...
...
@@ -1050,27 +1628,24 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Must be: OverFrameArt → FadeOverlay → Text/UI
// ────────────────────────────────────────────────
private
void
ApplyOverFrameEffectBoxFade
()
private
void
ApplyOverFrameEffectBoxFade
(
bool
isPendulum
)
{
if
(!
OverFrameEnableWashOverlay
)
if
(!
OverFrameEnableWashOverlay
)
{
// Only fade the artwork layers (continuation), never tint the parchment/text.
if
(
_overFrameEffectFadeMaskRt
)
_overFrameEffectFadeMaskRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFade
)
_overFrameEffectFade
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumC
)
_overFrameEffectFadePendulumC
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumL
)
_overFrameEffectFadePendulumL
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumR
)
_overFrameEffectFadePendulumR
.
gameObject
.
SetActive
(
false
);
return
;
}
var
desc
=
GetActiveOcgDescriptionText
();
var
desc
=
(
isPendulum
&&
cardDescription
!=
null
&&
cardDescription
.
gameObject
.
activeInHierarchy
)
?
cardDescription
:
GetActiveOcgDescriptionText
();
if
(
desc
==
null
)
return
;
bool
hasExplicitEffectBox
=
overFrameEffectBoxImage
!=
null
&&
overFrameEffectBoxImage
.
gameObject
!=
null
&&
overFrameEffectBoxImage
.
gameObject
.
activeInHierarchy
;
var
boxRt
=
hasExplicitEffectBox
?
overFrameEffectBoxImage
.
rectTransform
:
GetEffectBoxRectTransform
();
if
(
boxRt
==
null
)
return
;
// IMPORTANT: parent fade in the SAME UI layer as the description text
// (this is the layer that is visible above the artwork).
var
fadeParentRt
=
desc
.
transform
.
parent
as
RectTransform
;
...
...
@@ -1081,6 +1656,136 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
?
new
Color
(
1f
,
0f
,
1f
,
1f
)
:
new
Color
(
0.93f
,
0.86f
,
0.74f
,
1f
);
float
topA
=
OverFrameFadeDebugMagenta
?
1f
:
(
isPendulum
?
OverFramePendulumFadeTopAlpha
:
OverFrameFadeTopAlpha
);
float
botA
=
OverFrameFadeDebugMagenta
?
1f
:
(
isPendulum
?
OverFramePendulumFadeBottomAlpha
:
OverFrameFadeBottomAlpha
);
if
(
isPendulum
&&
cardFrame
!=
null
&&
cardFrame
.
gameObject
.
activeInHierarchy
&&
TryGetPendulumParchmentZonesNormalized
(
cardFrame
.
rectTransform
,
out
var
pendCenterNrm
,
out
var
pendLeftNrm
,
out
var
pendRightNrm
,
out
_
,
out
_
,
out
var
pendCenterBoxRt
,
out
var
pendLeftBoxRt
,
out
var
pendRightBoxRt
,
OverFramePendulumSeparatorGapFadeNrm
))
{
if
(
_overFrameEffectFadeMaskRt
!=
null
)
_overFrameEffectFadeMaskRt
.
gameObject
.
SetActive
(
false
);
float
centerFadeInsetSide
=
OverFramePendulumCenterFadeInsetSide
;
float
centerFadeInsetTop
=
OverFramePendulumCenterFadeInsetTop
;
float
centerFadeInsetBottom
=
OverFramePendulumCenterFadeInsetBottom
;
float
scaleFadeInsetOuter
=
OverFramePendulumScaleFadeInsetOuter
;
float
scaleFadeInsetInner
=
OverFramePendulumScaleFadeInsetInner
;
float
scaleFadeInsetTop
=
OverFramePendulumScaleFadeInsetTop
;
float
scaleFadeInsetBottom
=
OverFramePendulumScaleFadeInsetBottom
;
float
fallbackFadeInsetSide
=
OverFramePendulumZoneFallbackFadeInsetSide
;
float
fallbackFadeInsetTop
=
OverFramePendulumZoneFallbackFadeInsetTop
;
float
fallbackFadeInsetBottom
=
OverFramePendulumZoneFallbackFadeInsetBottom
;
if
(
pendCenterBoxRt
==
null
)
{
centerFadeInsetSide
+=
fallbackFadeInsetSide
;
centerFadeInsetTop
+=
fallbackFadeInsetTop
;
centerFadeInsetBottom
+=
fallbackFadeInsetBottom
;
}
if
(
pendLeftBoxRt
==
null
)
{
scaleFadeInsetOuter
+=
fallbackFadeInsetSide
;
scaleFadeInsetInner
+=
fallbackFadeInsetSide
;
scaleFadeInsetTop
+=
fallbackFadeInsetTop
;
scaleFadeInsetBottom
+=
fallbackFadeInsetBottom
;
}
if
(
pendRightBoxRt
==
null
)
{
scaleFadeInsetOuter
+=
fallbackFadeInsetSide
;
scaleFadeInsetInner
+=
fallbackFadeInsetSide
;
scaleFadeInsetTop
+=
fallbackFadeInsetTop
;
scaleFadeInsetBottom
+=
fallbackFadeInsetBottom
;
}
UpdateGradientFadeZone
(
ref
_overFrameEffectFadePendulumC
,
"OverFrameEffectFadePendulumC"
,
pendCenterNrm
,
cardFrame
.
rectTransform
,
fadeParentRt
,
washRgb
,
topA
,
botA
,
centerFadeInsetSide
,
centerFadeInsetSide
,
centerFadeInsetTop
,
centerFadeInsetBottom
,
pendCenterBoxRt
,
OverFramePendulumUpperZoneBorderCutFactor
,
OverFramePendulumUpperZoneBorderSafetyInset
);
UpdateGradientFadeZone
(
ref
_overFrameEffectFadePendulumL
,
"OverFrameEffectFadePendulumL"
,
pendLeftNrm
,
cardFrame
.
rectTransform
,
fadeParentRt
,
washRgb
,
topA
,
botA
,
scaleFadeInsetOuter
,
scaleFadeInsetInner
,
scaleFadeInsetTop
,
scaleFadeInsetBottom
,
pendLeftBoxRt
,
OverFramePendulumUpperZoneBorderCutFactor
,
OverFramePendulumUpperZoneBorderSafetyInset
);
UpdateGradientFadeZone
(
ref
_overFrameEffectFadePendulumR
,
"OverFrameEffectFadePendulumR"
,
pendRightNrm
,
cardFrame
.
rectTransform
,
fadeParentRt
,
washRgb
,
topA
,
botA
,
scaleFadeInsetInner
,
scaleFadeInsetOuter
,
scaleFadeInsetTop
,
scaleFadeInsetBottom
,
pendRightBoxRt
,
OverFramePendulumUpperZoneBorderCutFactor
,
OverFramePendulumUpperZoneBorderSafetyInset
);
int
fadeIdx
=
Mathf
.
Clamp
(
desc
.
transform
.
GetSiblingIndex
(),
0
,
fadeParentRt
.
childCount
-
1
);
if
(
_overFrameEffectFadePendulumC
)
_overFrameEffectFadePendulumC
.
rectTransform
.
SetSiblingIndex
(
fadeIdx
);
if
(
_overFrameEffectFadePendulumL
)
_overFrameEffectFadePendulumL
.
rectTransform
.
SetSiblingIndex
(
fadeIdx
);
if
(
_overFrameEffectFadePendulumR
)
_overFrameEffectFadePendulumR
.
rectTransform
.
SetSiblingIndex
(
fadeIdx
);
}
else
{
if
(
_overFrameEffectFadePendulumC
)
_overFrameEffectFadePendulumC
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumL
)
_overFrameEffectFadePendulumL
.
gameObject
.
SetActive
(
false
);
if
(
_overFrameEffectFadePendulumR
)
_overFrameEffectFadePendulumR
.
gameObject
.
SetActive
(
false
);
}
bool
hasExplicitEffectBox
=
overFrameEffectBoxImage
!=
null
&&
overFrameEffectBoxImage
.
gameObject
!=
null
&&
overFrameEffectBoxImage
.
gameObject
.
activeInHierarchy
;
var
boxRt
=
hasExplicitEffectBox
?
overFrameEffectBoxImage
.
rectTransform
:
GetEffectBoxRectTransform
(
desc
);
if
(
boxRt
==
null
)
return
;
// If we can, use the parchment background sprite itself as the wash overlay.
// This looks like the proxy (corners/edges match) and avoids a flat gray rectangle.
Image
boxImg
=
hasExplicitEffectBox
?
overFrameEffectBoxImage
:
boxRt
.
GetComponent
<
Image
>();
...
...
@@ -1113,7 +1818,9 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
_overFrameEffectFadeMaskImg
.
type
=
boxImg
.
type
;
_overFrameEffectFadeMaskImg
.
preserveAspect
=
boxImg
.
preserveAspect
;
_overFrameEffectFadeMaskImg
.
fillCenter
=
boxImg
.
fillCenter
;
_overFrameEffectFadeMaskImg
.
color
=
new
Color
(
washRgb
.
r
,
washRgb
.
g
,
washRgb
.
b
,
OverFrameWashSpriteAlpha
);
float
washAlpha
=
isPendulum
?
OverFramePendulumWashSpriteAlpha
:
OverFrameWashSpriteAlpha
;
_overFrameEffectFadeMaskImg
.
color
=
new
Color
(
washRgb
.
r
,
washRgb
.
g
,
washRgb
.
b
,
washAlpha
);
// If an old gradient exists from earlier builds, disable it so ONLY the art is faded.
if
(
_overFrameEffectFade
!=
null
)
...
...
@@ -1147,9 +1854,6 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
rt
.
anchoredPosition
=
Vector2
.
zero
;
rt
.
sizeDelta
=
Vector2
.
zero
;
float
topA
=
OverFrameFadeDebugMagenta
?
1f
:
OverFrameFadeTopAlpha
;
float
botA
=
OverFrameFadeDebugMagenta
?
1f
:
OverFrameFadeBottomAlpha
;
_overFrameEffectFade
.
topColor
=
new
Color
(
washRgb
.
r
,
washRgb
.
g
,
washRgb
.
b
,
topA
);
_overFrameEffectFade
.
bottomColor
=
new
Color
(
washRgb
.
r
,
washRgb
.
g
,
washRgb
.
b
,
botA
);
}
...
...
@@ -1160,8 +1864,21 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
if
(
cardFrame
!=
null
&&
cardFrame
.
gameObject
.
activeInHierarchy
)
{
Rect
boxNrm
=
OverFrameEffectBoxNrm
;
if
(
hasExplicitEffectBox
&&
TryGetEffectBoxNormalizedRect
(
cardFrame
.
rectTransform
,
out
var
dynBoxNrm
))
if
(
isPendulum
&&
desc
==
cardDescriptionPendulum
&&
TryGetRectNormalizedInFrame
(
cardFrame
.
rectTransform
,
boxRt
,
out
var
pendBoxNrm
))
{
boxNrm
=
ExpandNormalizedRect
(
pendBoxNrm
,
OverFramePendulumCenterPadSideNrm
,
OverFramePendulumCenterPadSideNrm
,
OverFramePendulumCenterPadTopNrm
,
OverFramePendulumCenterPadBottomNrm
);
}
else
if
(
hasExplicitEffectBox
&&
TryGetEffectBoxNormalizedRect
(
cardFrame
.
rectTransform
,
out
var
dynBoxNrm
))
{
boxNrm
=
dynBoxNrm
;
}
MatchRectToFrameNormalized
(
targetRt
,
cardFrame
.
rectTransform
,
fadeParentRt
,
boxNrm
);
}
...
...
@@ -1171,7 +1888,10 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
}
// Inset so we don't tint the parchment border line
InsetRect
(
targetRt
,
OverFrameFadeInsetSide
,
OverFrameFadeInsetSide
,
OverFrameFadeInsetTop
,
OverFrameFadeInsetBottom
);
float
fadeInsetSide
=
isPendulum
?
OverFramePendulumFadeInsetSide
:
OverFrameFadeInsetSide
;
float
fadeInsetTop
=
isPendulum
?
OverFramePendulumFadeInsetTop
:
OverFrameFadeInsetTop
;
float
fadeInsetBottom
=
isPendulum
?
OverFramePendulumFadeInsetBottom
:
OverFrameFadeInsetBottom
;
InsetRect
(
targetRt
,
fadeInsetSide
,
fadeInsetSide
,
fadeInsetTop
,
fadeInsetBottom
);
// Expand to match proxy region feel
targetRt
.
sizeDelta
+=
new
Vector2
(
OverFrameFadePadSide
*
2f
,
OverFrameFadePadTop
+
OverFrameFadePadBottom
);
...
...
@@ -1200,10 +1920,10 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
);
private
static
readonly
Rect
OverFrameEffectBoxNrm
=
new
Rect
(
0.05
397727f
,
// xMin = 38 / 704 (expanded 14px each side)
0.0
625f
,
0.8
9204545f
,
// width = 628 / 704 (600 + 28)
0.1
8847656f
+
(
OverFrameParchmentTopRaisePx
/
OverFrameRefH
)
0.05
965909f
,
// xMin = 42 / 704
0.0
5859375f
,
// yMin = 60 / 1024
0.8
8068182f
,
// width = 620 / 704
0.1
9238281f
+
(
OverFrameParchmentTopRaisePx
/
OverFrameRefH
)
// height = 197 / 1024 (+ top raise
)
);
...
...
@@ -1241,11 +1961,16 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Faint continuation strength inside the parchment box
// (you already have OverFrameTextArtAlpha; tweak here if desired)
private
const
float
OverFrameTextArtAlpha
=
0.36f
;
private
const
float
OverFramePendulumTextArtAlpha
=
0.36f
;
private
const
float
OverFramePendulumUpperZoneArtAlpha
=
OverFramePendulumTextArtAlpha
;
private
const
bool
OverFramePendulumUpperUseBaseArtContinuation
=
false
;
// Because the overlay is rendered ABOVE the frame sprite, we must keep the entire
// border thickness clear (otherwise the artwork tints/overlaps the border line).
// 1.0 = cut in the middle of the border, 2.0 = cut at the inner edge (full border).
private
const
float
OverFrameBorderCutFactor
=
2.0f
;
private
const
float
OverFramePendulumUpperZoneBorderCutFactor
=
2.00f
;
private
const
float
OverFramePendulumUpperZoneBorderSafetyInset
=
0.25f
;
// If the parchment background sprite has no 9-slice border data,
// we fall back to a safe local-unit border inset so the artwork never touches the border line.
...
...
@@ -1254,9 +1979,26 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// When the chosen effect-box RectTransform already represents the INNER fill (border excluded),
// we only apply a tiny inset to avoid texture filtering/half-pixel bleed.
private
const
float
OverFrameInnerRectNoBorderInset
=
0.15f
;
private
const
float
OverFramePendulumInnerRectNoBorderInset
=
0.20f
;
// Inner-rect variant for the upper clip nudge (usually 0; the rect is already on the inner edge).
private
const
float
OverFrameUpperClipInsetBottomInner
=
0.0f
;
private
const
float
OverFramePendulumUpperClipInsetBottomInner
=
0.35f
;
// Pendulum center text rect expansion in normalized frame space (704x1024 reference).
// This maps the center parchment box boundaries from the text rect without touching separator lines.
private
const
float
OverFramePendulumCenterPadSideNrm
=
8.5f
/
704f
;
private
const
float
OverFramePendulumCenterPadTopNrm
=
8.5f
/
1024f
;
private
const
float
OverFramePendulumCenterPadBottomNrm
=
8f
/
1024f
;
// Pendulum scale parchment widths inferred from the scale text rects.
private
const
float
OverFramePendulumScalePadOuterNrm
=
9f
/
704f
;
private
const
float
OverFramePendulumScalePadInnerNrm
=
2f
/
704f
;
// Keep separate separator dead-zones for wash-fade vs hard-clip:
// - Fade gap: small, so wash reaches close to inner parchment walls.
// - Clip gap: larger, so art clips hard on delimiter seams.
private
const
float
OverFramePendulumSeparatorGapFadeNrm
=
0.00f
/
704f
;
private
const
float
OverFramePendulumSeparatorGapClipNrm
=
0.35f
/
704f
;
private
const
float
OverFramePendulumUpperFallbackSeamRaiseNrm
=
4.20f
/
1024f
;
// Auto-detection thresholds in normalized frame space (0..1).
// These only matter in Auto mode and are generous to handle minor prefab/layout differences.
...
...
@@ -1269,26 +2011,59 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// IMPORTANT:
// - The border thickness itself comes from the parchment sprite's 9-slice border (TryGetEffectBoxBorderMidWorld + OverFrameBorderCutFactor).
// - These values are EXTRA "safety" insets added on top, to avoid any texture filtering / half-pixel bleed on the border lines.
private
const
float
OverFrameUpperClipInsetBottom
=
1.
0f
;
// pushes the upper hard-stop slightly ABOVE the border line
private
const
float
OverFrameUpperClipInsetBottom
=
1.
20f
;
// pushes the upper hard-stop slightly ABOVE the border line
private
const
float
OverFrameBorderSafetyInset
=
0.75f
;
// extra shrink to keep border lines perfectly clean
// Extra insets for the continuation clip inside the parchment (usually keep at 0 and tune safety above).
private
const
float
OverFrameTextClipInsetLeft
=
0.0f
;
private
const
float
OverFrameTextClipInsetRight
=
0.0f
;
private
const
float
OverFrameTextClipInsetTop
=
0.0f
;
private
const
float
OverFrameTextClipInsetBottom
=
0.0f
;
private
const
float
OverFrameTextClipInsetBottom
=
-
1.35f
;
private
const
float
OverFramePendulumTextClipInsetBottom
=
-
2.10f
;
// Side continuation clip insets (preserve outer card border + parchment frame line)
private
const
float
OverFrameSideClipInsetOuter
=
0.0f
;
private
const
float
OverFrameSideClipInsetInner
=
0.0f
;
private
const
float
OverFrameSideClipInsetTop
=
0.0f
;
private
const
float
OverFrameSideClipInsetBottom
=
0.0f
;
private
const
float
OverFrameSideContinuationSafetyInset
=
0.00f
;
private
const
float
OverFramePendulumSideClipInsetOuter
=
0.00f
;
private
const
float
OverFramePendulumSideContinuationSafetyInset
=
0.00f
;
// Tiny overlap between split clips to hide 1px seam gaps at left/right.
private
const
float
OverFrameSplitSeamOverlap
=
0.90f
;
private
const
float
OverFramePendulumSplitSeamOverlap
=
0.90f
;
// Optional: also inset the parchment wash overlay so it doesn't tint the border
private
const
float
OverFrameFadeInsetSide
=
0.
8
0f
;
private
const
float
OverFrameFadeInsetSide
=
0.
0
0f
;
private
const
float
OverFrameFadeInsetTop
=
0.00f
;
private
const
float
OverFrameFadeInsetBottom
=
0.90f
;
private
const
float
OverFrameFadeInsetBottom
=
-
0.20f
;
private
const
float
OverFramePendulumFadeInsetSide
=
0.70f
;
private
const
float
OverFramePendulumFadeInsetTop
=
0.10f
;
private
const
float
OverFramePendulumFadeInsetBottom
=
0.75f
;
// Upper pendulum fade boxes (center + scales) are tuned separately from lower effect-box fade.
private
const
float
OverFramePendulumCenterFadeInsetSide
=
0.10f
;
private
const
float
OverFramePendulumCenterFadeInsetTop
=
0.25f
;
private
const
float
OverFramePendulumCenterFadeInsetBottom
=
0.00f
;
private
const
float
OverFramePendulumScaleFadeInsetOuter
=
0.60f
;
private
const
float
OverFramePendulumScaleFadeInsetInner
=
0.10f
;
private
const
float
OverFramePendulumScaleFadeInsetTop
=
0.25f
;
private
const
float
OverFramePendulumScaleFadeInsetBottom
=
0.00f
;
// Upper pendulum parchments (center/left-scale/right-scale) use separate clips.
// Keep center and scale seam trims separate so narrow scale boxes don't get over-masked.
private
const
float
OverFramePendulumCenterClipInsetSide
=
0.20f
;
private
const
float
OverFramePendulumCenterClipInsetTop
=
0.45f
;
private
const
float
OverFramePendulumCenterClipInsetBottom
=
0.00f
;
private
const
float
OverFramePendulumScaleClipInsetOuter
=
0.60f
;
private
const
float
OverFramePendulumScaleClipInsetInner
=
0.20f
;
private
const
float
OverFramePendulumScaleClipInsetTop
=
0.45f
;
private
const
float
OverFramePendulumScaleClipInsetBottom
=
0.00f
;
// Applied when a pendulum upper zone has no dedicated parchment Image rect.
// Split fade vs clip so fade can reach inner walls while clip stays hard/stable.
private
const
float
OverFramePendulumZoneFallbackFadeInsetSide
=
0.00f
;
private
const
float
OverFramePendulumZoneFallbackFadeInsetTop
=
0.00f
;
private
const
float
OverFramePendulumZoneFallbackFadeInsetBottom
=
0.00f
;
private
const
float
OverFramePendulumZoneFallbackClipInsetSide
=
0.00f
;
private
const
float
OverFramePendulumZoneFallbackClipInsetTop
=
0.00f
;
private
const
float
OverFramePendulumZoneFallbackClipInsetBottom
=
0.00f
;
private
static
void
InsetRect
(
RectTransform
rt
,
float
left
,
float
right
,
float
top
,
float
bottom
)
{
...
...
@@ -1323,10 +2098,15 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
SetRectByWorldBLTR
(
dst
,
worldBL
,
worldTR
,
dstParent
);
}
private
bool
ApplyOverFrameProxySplit
(
Texture2D
tex
,
RawImage
baseArt
,
OverFrameSpec
spec
,
RectTransform
frameRt
,
Transform
anchorParent
,
RectTransform
anchorParentRt
)
private
bool
ApplyOverFrameProxySplit
(
Texture2D
tex
,
RawImage
baseArt
,
OverFrameSpec
spec
,
RectTransform
frameRt
,
Transform
anchorParent
,
RectTransform
anchorParentRt
,
bool
isPendulum
)
{
if
(
tex
==
null
||
frameRt
==
null
||
anchorParent
==
null
||
anchorParentRt
==
null
)
return
false
;
// Full-card overframe art should track the card frame bounds (same behavior as normal overframe path).
RectTransform
splitArtReferenceRt
=
frameRt
;
float
splitSeamOverlap
=
isPendulum
?
OverFramePendulumSplitSeamOverlap
:
OverFrameSplitSeamOverlap
;
// Prefer stable reference bounds unless an explicit parchment Image is assigned.
// This avoids auto-detect picking text/padding rects that shrink the mask width.
bool
hasExplicitEffectBox
=
...
...
@@ -1334,23 +2114,130 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
overFrameEffectBoxImage
.
gameObject
!=
null
&&
overFrameEffectBoxImage
.
gameObject
.
activeInHierarchy
;
// Dynamically derive the parchment/effect-box bounds
only from explicit rects
.
// Dynamically derive the parchment/effect-box bounds.
Rect
effectBoxNrm
=
OverFrameEffectBoxNrm
;
Rect
effectBoxInnerNrm
=
OverFrameEffectBoxInnerNrm
;
Rect
dynEffectBoxNrm
=
default
;
bool
haveDynEffectBox
=
false
;
if
(
hasExplicitEffectBox
)
haveDynEffectBox
=
TryGetEffectBoxNormalizedRect
(
frameRt
,
out
dynEffectBoxNrm
);
if
(
haveDynEffectBox
)
effectBoxNrm
=
dynEffectBoxNrm
;
// IMPORTANT: clip using explicit parchment Image when available.
// Decide whether that rect is OUTER (includes border) or already INNER (border excluded).
var
realBoxRt
=
hasExplicitEffectBox
?
overFrameEffectBoxImage
.
rectTransform
:
null
;
bool
effectBoxIsInnerFill
=
IsEffectBoxRectInnerFill
(
realBoxRt
,
effectBoxNrm
);
if
(
realBoxRt
==
null
)
effectBoxIsInnerFill
=
true
;
float
clipSafetyInset
=
effectBoxIsInnerFill
?
OverFrameInnerRectNoBorderInset
:
OverFrameBorderSafetyInset
;
bool
effectBoxIsInnerFill
;
Rect
pendCenterNrm
=
default
;
Rect
pendLeftScaleNrm
=
default
;
Rect
pendRightScaleNrm
=
default
;
Rect
pendFullBandNrm
=
default
;
bool
pendUsedImageBoxRects
=
false
;
RectTransform
pendCenterBoxRt
=
null
;
RectTransform
pendLeftBoxRt
=
null
;
RectTransform
pendRightBoxRt
=
null
;
bool
hasPendulumParchmentZones
=
isPendulum
&&
TryGetPendulumParchmentZonesNormalized
(
frameRt
,
out
pendCenterNrm
,
out
pendLeftScaleNrm
,
out
pendRightScaleNrm
,
out
pendFullBandNrm
,
out
pendUsedImageBoxRects
,
out
pendCenterBoxRt
,
out
pendLeftBoxRt
,
out
pendRightBoxRt
,
OverFramePendulumSeparatorGapClipNrm
);
if
(
hasPendulumParchmentZones
)
{
effectBoxNrm
=
pendCenterNrm
;
effectBoxInnerNrm
=
effectBoxNrm
;
effectBoxIsInnerFill
=
true
;
realBoxRt
=
null
;
}
else
if
(
isPendulum
&&
cardDescriptionPendulum
!=
null
&&
cardDescriptionPendulum
.
gameObject
.
activeInHierarchy
&&
!
string
.
IsNullOrEmpty
(
cardDescriptionPendulum
.
text
)
&&
TryGetRectNormalizedInFrame
(
frameRt
,
cardDescriptionPendulum
.
rectTransform
,
out
var
pendCenterFallbackNrm
))
{
effectBoxNrm
=
ExpandNormalizedRect
(
pendCenterFallbackNrm
,
OverFramePendulumCenterPadSideNrm
,
OverFramePendulumCenterPadSideNrm
,
OverFramePendulumCenterPadTopNrm
,
OverFramePendulumCenterPadBottomNrm
);
effectBoxInnerNrm
=
effectBoxNrm
;
effectBoxIsInnerFill
=
true
;
realBoxRt
=
null
;
}
else
{
Rect
dynEffectBoxNrm
=
default
;
bool
haveDynEffectBox
=
false
;
if
(
hasExplicitEffectBox
)
haveDynEffectBox
=
TryGetEffectBoxNormalizedRect
(
frameRt
,
out
dynEffectBoxNrm
);
if
(
haveDynEffectBox
)
effectBoxNrm
=
dynEffectBoxNrm
;
// IMPORTANT: clip using explicit parchment Image when available.
// Decide whether that rect is OUTER (includes border) or already INNER (border excluded).
effectBoxIsInnerFill
=
IsEffectBoxRectInnerFill
(
realBoxRt
,
effectBoxNrm
);
if
(
realBoxRt
==
null
)
effectBoxIsInnerFill
=
true
;
}
float
yCut
=
Mathf
.
Clamp01
(
effectBoxNrm
.
yMin
+
effectBoxNrm
.
height
);
float
clipSafetyInset
=
effectBoxIsInnerFill
?
(
isPendulum
?
OverFramePendulumInnerRectNoBorderInset
:
OverFrameInnerRectNoBorderInset
)
:
OverFrameBorderSafetyInset
;
float
yCut
;
if
(
hasPendulumParchmentZones
)
{
float
pendBandTop
=
pendFullBandNrm
.
yMin
+
pendFullBandNrm
.
height
;
float
fallbackSeamRaise
=
!
pendUsedImageBoxRects
?
OverFramePendulumUpperFallbackSeamRaiseNrm
:
0f
;
yCut
=
Mathf
.
Clamp01
(
pendBandTop
+
fallbackSeamRaise
);
// Keep fallback pendulum clip zones touching the moved seam.
// Without this, raising yCut can leave a thin uncovered strip near upper separators.
if
(
fallbackSeamRaise
>
0f
)
{
float
raisedTop
=
yCut
;
if
(
raisedTop
>
(
pendCenterNrm
.
yMin
+
pendCenterNrm
.
height
))
pendCenterNrm
=
new
Rect
(
pendCenterNrm
.
xMin
,
pendCenterNrm
.
yMin
,
pendCenterNrm
.
width
,
Mathf
.
Max
(
0f
,
raisedTop
-
pendCenterNrm
.
yMin
));
if
(
raisedTop
>
(
pendLeftScaleNrm
.
yMin
+
pendLeftScaleNrm
.
height
))
pendLeftScaleNrm
=
new
Rect
(
pendLeftScaleNrm
.
xMin
,
pendLeftScaleNrm
.
yMin
,
pendLeftScaleNrm
.
width
,
Mathf
.
Max
(
0f
,
raisedTop
-
pendLeftScaleNrm
.
yMin
));
if
(
raisedTop
>
(
pendRightScaleNrm
.
yMin
+
pendRightScaleNrm
.
height
))
pendRightScaleNrm
=
new
Rect
(
pendRightScaleNrm
.
xMin
,
pendRightScaleNrm
.
yMin
,
pendRightScaleNrm
.
width
,
Mathf
.
Max
(
0f
,
raisedTop
-
pendRightScaleNrm
.
yMin
));
if
(
raisedTop
>
(
effectBoxNrm
.
yMin
+
effectBoxNrm
.
height
))
{
effectBoxNrm
=
new
Rect
(
effectBoxNrm
.
xMin
,
effectBoxNrm
.
yMin
,
effectBoxNrm
.
width
,
Mathf
.
Max
(
0f
,
raisedTop
-
effectBoxNrm
.
yMin
));
effectBoxInnerNrm
=
effectBoxNrm
;
}
if
(
raisedTop
>
(
pendFullBandNrm
.
yMin
+
pendFullBandNrm
.
height
))
pendFullBandNrm
=
new
Rect
(
pendFullBandNrm
.
xMin
,
pendFullBandNrm
.
yMin
,
pendFullBandNrm
.
width
,
Mathf
.
Max
(
0f
,
raisedTop
-
pendFullBandNrm
.
yMin
));
}
}
else
{
yCut
=
Mathf
.
Clamp01
(
effectBoxNrm
.
yMin
+
effectBoxNrm
.
height
);
}
Rect
upperAreaNrm
=
new
Rect
(
0f
,
yCut
,
1f
,
Mathf
.
Max
(
0f
,
1f
-
yCut
));
...
...
@@ -1387,9 +2274,10 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
MatchRectToFrameNormalized
(
_overFrameMainClipRt
,
frameRt
,
anchorParentRt
,
upperAreaNrm
);
// Inset so we don't tint the parchment border line
float
upperClipInsetBottomInner
=
isPendulum
?
OverFramePendulumUpperClipInsetBottomInner
:
OverFrameUpperClipInsetBottomInner
;
float
mainCutLocal
=
(
effectBoxIsInnerFill
?
0f
:
(
hasBorder
?
WorldToLocalY
(
_overFrameMainClipRt
,
midTopW
)
:
OverFrameFallbackBorderInset
))
+
(
effectBoxIsInnerFill
?
OverFrameU
pperClipInsetBottomInner
:
OverFrameUpperClipInsetBottom
)
+
(
effectBoxIsInnerFill
?
u
pperClipInsetBottomInner
:
OverFrameUpperClipInsetBottom
)
+
clipSafetyInset
;
// keep the top parchment border line perfectly clean // keep the top parchment border line perfectly clean
InsetRect
(
_overFrameMainClipRt
,
0f
,
0f
,
0f
,
mainCutLocal
);
...
...
@@ -1403,7 +2291,7 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Align main art to full frame (then it gets clipped)
if
(
_overFrameArt
!=
null
)
{
MatchRectByWorldCorners
(
_overFrameArt
.
rectTransform
,
fram
eRt
,
_overFrameMainClipRt
);
MatchRectByWorldCorners
(
_overFrameArt
.
rectTransform
,
splitArtReferenc
eRt
,
_overFrameMainClipRt
);
_overFrameArt
.
rectTransform
.
sizeDelta
*=
spec
.
scale
;
_overFrameArt
.
rectTransform
.
anchoredPosition
+=
spec
.
offset
;
}
...
...
@@ -1415,13 +2303,29 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
float
boxX0
=
effectBoxNrm
.
xMin
;
float
boxX1
=
effectBoxNrm
.
xMin
+
effectBoxNrm
.
width
;
float
boxY1
=
effectBoxNrm
.
yMin
+
effectBoxNrm
.
height
;
//
Side continuation should run from card bottom up to the top of the effect box,
//
so beams/light on the side are not cut halfway near the footer area
.
//
Continue side gutters from card bottom to the current split seam.
//
For pendulum cards this avoids empty left/right zones below the lower parchment box
.
float
sideY0
=
0f
;
float
sideH
=
Mathf
.
Max
(
0f
,
boxY1
-
sideY0
);
float
sideY1
=
boxY1
;
float
sideH
=
Mathf
.
Max
(
0f
,
sideY1
-
sideY0
);
Rect
leftSideNrm
;
Rect
rightSideNrm
;
if
(
hasPendulumParchmentZones
)
{
float
leftOuterW
=
Mathf
.
Max
(
0f
,
pendLeftScaleNrm
.
xMin
);
float
rightOuterX
=
pendRightScaleNrm
.
xMin
+
pendRightScaleNrm
.
width
;
leftSideNrm
=
new
Rect
(
0f
,
sideY0
,
leftOuterW
,
sideH
);
rightSideNrm
=
new
Rect
(
rightOuterX
,
sideY0
,
Mathf
.
Max
(
0f
,
1f
-
rightOuterX
),
sideH
);
}
else
{
leftSideNrm
=
new
Rect
(
0f
,
sideY0
,
Mathf
.
Max
(
0f
,
boxX0
),
sideH
);
rightSideNrm
=
new
Rect
(
boxX1
,
sideY0
,
Mathf
.
Max
(
0f
,
1f
-
boxX1
),
sideH
);
}
Rect
leftSideNrm
=
new
Rect
(
0f
,
sideY0
,
Mathf
.
Max
(
0f
,
boxX0
),
sideH
)
;
Rect
rightSideNrm
=
new
Rect
(
boxX1
,
sideY0
,
Mathf
.
Max
(
0f
,
1f
-
boxX1
),
sideH
);
Texture
sideTex
=
tex
;
Rect
sideUv
=
_overFrameArt
!=
null
?
_overFrameArt
.
uvRect
:
new
Rect
(
0f
,
0f
,
1f
,
1f
);
// Left clip
if
(
leftSideNrm
.
width
>
0.001f
&&
leftSideNrm
.
height
>
0.001f
)
...
...
@@ -1443,13 +2347,15 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Inset so the over-art never touches the parchment border lines (left side margin)
// NOTE: This clip is OUTSIDE the parchment box, so we MUST NOT apply the parchment border thickness here.
// Applying the fallback border inset (13px) creates visible "empty margins" near the corners.
float
sideL_Inner
=
clipSafetyInset
+
OverFrameSideClipInsetInner
;
float
sideL_Top
=
clipSafetyInset
+
OverFrameSideClipInsetTop
;
float
sideL_Bottom
=
clipSafetyInset
+
OverFrameSideClipInsetBottom
;
float
sideSafety
=
isPendulum
?
OverFramePendulumSideContinuationSafetyInset
:
OverFrameSideContinuationSafetyInset
;
float
sideOuterInset
=
isPendulum
?
OverFramePendulumSideClipInsetOuter
:
OverFrameSideClipInsetOuter
;
float
sideL_Inner
=
sideSafety
+
OverFrameSideClipInsetInner
;
float
sideL_Top
=
sideSafety
+
OverFrameSideClipInsetTop
;
float
sideL_Bottom
=
sideSafety
+
OverFrameSideClipInsetBottom
;
InsetRect
(
_overFrameSideClipL_Rt
,
OverFrameSideClipInsetOuter
,
Mathf
.
Max
(
0f
,
sideL_Inner
-
OverFrameS
plitSeamOverlap
),
sideOuterInset
,
Mathf
.
Max
(
0f
,
sideL_Inner
-
s
plitSeamOverlap
),
Mathf
.
Max
(
0f
,
sideL_Top
),
Mathf
.
Max
(
0f
,
sideL_Bottom
));
if
(
_overFrameArtSideL
==
null
)
...
...
@@ -1468,12 +2374,12 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
}
_overFrameArtSideL
.
gameObject
.
SetActive
(
true
);
_overFrameArtSideL
.
texture
=
t
ex
;
_overFrameArtSideL
.
uvRect
=
_overFrameArt
!=
null
?
_overFrameArt
.
uvRect
:
_overFrameArtSideL
.
uvRect
;
_overFrameArtSideL
.
texture
=
sideT
ex
;
_overFrameArtSideL
.
uvRect
=
sideUv
;
_overFrameArtSideL
.
color
=
Color
.
white
;
_overFrameArtSideL
.
maskable
=
true
;
MatchRectByWorldCorners
(
_overFrameArtSideL
.
rectTransform
,
fram
eRt
,
_overFrameSideClipL_Rt
);
MatchRectByWorldCorners
(
_overFrameArtSideL
.
rectTransform
,
splitArtReferenc
eRt
,
_overFrameSideClipL_Rt
);
_overFrameArtSideL
.
rectTransform
.
sizeDelta
*=
spec
.
scale
;
_overFrameArtSideL
.
rectTransform
.
anchoredPosition
+=
spec
.
offset
;
}
...
...
@@ -1503,13 +2409,15 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Inset so the over-art never touches the parchment border lines (right side margin)
// NOTE: This clip is OUTSIDE the parchment box, so we MUST NOT apply the parchment border thickness here.
// Applying the fallback border inset (13px) creates visible "empty margins" near the corners.
float
sideR_Inner
=
clipSafetyInset
+
OverFrameSideClipInsetInner
;
float
sideR_Top
=
clipSafetyInset
+
OverFrameSideClipInsetTop
;
float
sideR_Bottom
=
clipSafetyInset
+
OverFrameSideClipInsetBottom
;
float
sideSafety
=
isPendulum
?
OverFramePendulumSideContinuationSafetyInset
:
OverFrameSideContinuationSafetyInset
;
float
sideOuterInset
=
isPendulum
?
OverFramePendulumSideClipInsetOuter
:
OverFrameSideClipInsetOuter
;
float
sideR_Inner
=
sideSafety
+
OverFrameSideClipInsetInner
;
float
sideR_Top
=
sideSafety
+
OverFrameSideClipInsetTop
;
float
sideR_Bottom
=
sideSafety
+
OverFrameSideClipInsetBottom
;
InsetRect
(
_overFrameSideClipR_Rt
,
Mathf
.
Max
(
0f
,
sideR_Inner
-
OverFrameS
plitSeamOverlap
),
OverFrameSideClipInsetOuter
,
Mathf
.
Max
(
0f
,
sideR_Inner
-
s
plitSeamOverlap
),
sideOuterInset
,
Mathf
.
Max
(
0f
,
sideR_Top
),
Mathf
.
Max
(
0f
,
sideR_Bottom
));
if
(
_overFrameArtSideR
==
null
)
...
...
@@ -1528,12 +2436,12 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
}
_overFrameArtSideR
.
gameObject
.
SetActive
(
true
);
_overFrameArtSideR
.
texture
=
t
ex
;
_overFrameArtSideR
.
uvRect
=
_overFrameArt
!=
null
?
_overFrameArt
.
uvRect
:
_overFrameArtSideR
.
uvRect
;
_overFrameArtSideR
.
texture
=
sideT
ex
;
_overFrameArtSideR
.
uvRect
=
sideUv
;
_overFrameArtSideR
.
color
=
Color
.
white
;
_overFrameArtSideR
.
maskable
=
true
;
MatchRectByWorldCorners
(
_overFrameArtSideR
.
rectTransform
,
fram
eRt
,
_overFrameSideClipR_Rt
);
MatchRectByWorldCorners
(
_overFrameArtSideR
.
rectTransform
,
splitArtReferenc
eRt
,
_overFrameSideClipR_Rt
);
_overFrameArtSideR
.
rectTransform
.
sizeDelta
*=
spec
.
scale
;
_overFrameArtSideR
.
rectTransform
.
anchoredPosition
+=
spec
.
offset
;
}
...
...
@@ -1544,11 +2452,94 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
}
// ── B) TEXT CLIP (continuation inside parchment) ─────────────────
var
desc
=
GetActiveOcgDescriptionText
();
if
(
desc
==
null
)
return
true
;
var
desc
=
(
isPendulum
&&
cardDescription
!=
null
&&
cardDescription
.
gameObject
.
activeInHierarchy
)
?
cardDescription
:
GetActiveOcgDescriptionText
();
if
(
desc
==
null
)
{
if
(
_overFramePendulumCenterClipRt
)
_overFramePendulumCenterClipRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumCenterArt
)
_overFramePendulumCenterArt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipL_Rt
)
_overFramePendulumScaleClipL_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipR_Rt
)
_overFramePendulumScaleClipR_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtL
)
_overFramePendulumScaleArtL
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtR
)
_overFramePendulumScaleArtR
.
gameObject
.
SetActive
(
false
);
return
true
;
}
var
fadeParentRt
=
desc
.
transform
.
parent
as
RectTransform
;
if
(
fadeParentRt
==
null
)
return
true
;
if
(
fadeParentRt
==
null
)
{
if
(
_overFramePendulumCenterClipRt
)
_overFramePendulumCenterClipRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumCenterArt
)
_overFramePendulumCenterArt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipL_Rt
)
_overFramePendulumScaleClipL_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipR_Rt
)
_overFramePendulumScaleClipR_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtL
)
_overFramePendulumScaleArtL
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtR
)
_overFramePendulumScaleArtR
.
gameObject
.
SetActive
(
false
);
return
true
;
}
RectTransform
textClipBoxRt
=
realBoxRt
;
Rect
textClipBoxNrm
=
effectBoxInnerNrm
;
bool
textClipIsInnerFill
=
effectBoxIsInnerFill
;
float
textClipSafetyInset
=
clipSafetyInset
;
float
textMidLeftW
=
midLeftW
;
float
textMidRightW
=
midRightW
;
float
textMidTopW
=
midTopW
;
float
textMidBottomW
=
midBottomW
;
bool
textHasBorder
=
hasBorder
;
if
(
isPendulum
)
{
textClipBoxRt
=
hasExplicitEffectBox
?
overFrameEffectBoxImage
.
rectTransform
:
GetEffectBoxRectTransform
(
desc
);
bool
trustDetectedLowerBox
=
false
;
if
(
textClipBoxRt
!=
null
)
{
if
(
hasExplicitEffectBox
)
{
trustDetectedLowerBox
=
true
;
}
else
{
var
detectedImg
=
textClipBoxRt
.
GetComponent
<
Image
>();
trustDetectedLowerBox
=
detectedImg
!=
null
&&
detectedImg
.
sprite
!=
null
;
}
}
if
(
trustDetectedLowerBox
&&
textClipBoxRt
!=
null
&&
TryGetRectNormalizedInFrame
(
frameRt
,
textClipBoxRt
,
out
var
pendLowerBoxNrm
))
{
// Reject text-rect-like detections: pendulum lower box should be close to normal effect-box width.
bool
likelyTooNarrow
=
pendLowerBoxNrm
.
width
<
(
OverFrameEffectBoxNrm
.
width
-
(
8f
/
704f
));
textClipBoxNrm
=
pendLowerBoxNrm
;
textClipIsInnerFill
=
likelyTooNarrow
?
true
:
IsEffectBoxRectInnerFill
(
textClipBoxRt
,
textClipBoxNrm
);
if
(
likelyTooNarrow
&&
!
hasExplicitEffectBox
)
{
textClipBoxRt
=
null
;
textClipBoxNrm
=
OverFrameEffectBoxNrm
;
}
}
else
{
textClipBoxRt
=
null
;
textClipBoxNrm
=
OverFrameEffectBoxNrm
;
textClipIsInnerFill
=
true
;
}
textClipSafetyInset
=
textClipIsInnerFill
?
OverFramePendulumInnerRectNoBorderInset
:
OverFrameBorderSafetyInset
;
textHasBorder
=
TryGetEffectBoxBorderMidWorld
(
textClipBoxRt
,
out
textMidLeftW
,
out
textMidRightW
,
out
textMidTopW
,
out
textMidBottomW
);
if
(
textHasBorder
)
{
textMidLeftW
*=
OverFrameBorderCutFactor
;
textMidRightW
*=
OverFrameBorderCutFactor
;
textMidTopW
*=
OverFrameBorderCutFactor
;
textMidBottomW
*=
OverFrameBorderCutFactor
;
}
}
if
(
_overFrameTextClipRt
==
null
)
{
...
...
@@ -1566,27 +2557,28 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Clip area = parchment box (prefer the REAL parchment Image rect when available)
// realBoxRt already computed above (explicit parchment Image preferred).
if
(
real
BoxRt
!=
null
)
MatchRectByWorldCorners
(
_overFrameTextClipRt
,
real
BoxRt
,
fadeParentRt
);
if
(
textClip
BoxRt
!=
null
)
MatchRectByWorldCorners
(
_overFrameTextClipRt
,
textClip
BoxRt
,
fadeParentRt
);
else
MatchRectToFrameNormalized
(
_overFrameTextClipRt
,
frameRt
,
fadeParentRt
,
effectBoxInner
Nrm
);
MatchRectToFrameNormalized
(
_overFrameTextClipRt
,
frameRt
,
fadeParentRt
,
textClipBox
Nrm
);
// Inset so continuation doesn't tint the orange border line.
// We cut at the INNER edge of the parchment border (9-slice border * OverFrameBorderCutFactor),
// then add a small safety inset to avoid half-pixel filtering bleed.
float
textInsetL
=
(
effectBoxIsInnerFill
?
0f
:
(
hasBorder
?
WorldToLocalX
(
_overFrameTextClipRt
,
midLeftW
)
:
OverFrameFallbackBorderInset
))
+
clipSafetyInset
+
OverFrameTextClipInsetLeft
;
float
textInsetR
=
(
effectBoxIsInnerFill
?
0f
:
(
hasBorder
?
WorldToLocalX
(
_overFrameTextClipRt
,
midRightW
)
:
OverFrameFallbackBorderInset
))
+
clipSafetyInset
+
OverFrameTextClipInsetRight
;
float
textInsetT
=
(
effectBoxIsInnerFill
?
0f
:
(
hasBorder
?
WorldToLocalY
(
_overFrameTextClipRt
,
midTopW
)
:
OverFrameFallbackBorderInset
))
+
clipSafetyInset
+
OverFrameTextClipInsetTop
;
float
textInsetB
=
(
effectBoxIsInnerFill
?
0f
:
(
hasBorder
?
WorldToLocalY
(
_overFrameTextClipRt
,
midBottomW
)
:
OverFrameFallbackBorderInset
))
+
clipSafetyInset
+
OverFrameTextClipInsetBottom
;
float
textInsetL
=
(
textClipIsInnerFill
?
0f
:
(
textHasBorder
?
WorldToLocalX
(
_overFrameTextClipRt
,
textMidLeftW
)
:
OverFrameFallbackBorderInset
))
+
textClipSafetyInset
+
OverFrameTextClipInsetLeft
;
float
textInsetR
=
(
textClipIsInnerFill
?
0f
:
(
textHasBorder
?
WorldToLocalX
(
_overFrameTextClipRt
,
textMidRightW
)
:
OverFrameFallbackBorderInset
))
+
textClipSafetyInset
+
OverFrameTextClipInsetRight
;
float
textInsetT
=
(
textClipIsInnerFill
?
0f
:
(
textHasBorder
?
WorldToLocalY
(
_overFrameTextClipRt
,
textMidTopW
)
:
OverFrameFallbackBorderInset
))
+
textClipSafetyInset
+
OverFrameTextClipInsetTop
;
float
textClipInsetBottom
=
isPendulum
?
OverFramePendulumTextClipInsetBottom
:
OverFrameTextClipInsetBottom
;
float
textInsetB
=
(
textClipIsInnerFill
?
0f
:
(
textHasBorder
?
WorldToLocalY
(
_overFrameTextClipRt
,
textMidBottomW
)
:
OverFrameFallbackBorderInset
))
+
textClipSafetyInset
+
textClipInsetBottom
;
InsetRect
(
_overFrameTextClipRt
,
Mathf
.
Max
(
0f
,
textInsetL
-
OverFrameS
plitSeamOverlap
),
Mathf
.
Max
(
0f
,
textInsetR
-
OverFrameS
plitSeamOverlap
),
Mathf
.
Max
(
0f
,
textInsetL
-
s
plitSeamOverlap
),
Mathf
.
Max
(
0f
,
textInsetR
-
s
plitSeamOverlap
),
Mathf
.
Max
(
0f
,
textInsetT
),
Mathf
.
Max
(
0f
,
textInsetB
));
// --- BG continuation under the parchment (prevents "empty" transparent gaps) ---
...
...
@@ -1626,7 +2618,7 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
_overFrameArtTextBG
.
maskable
=
true
;
// Align BG continuation to full frame (then clipped by parchment mask)
MatchRectByWorldCorners
(
_overFrameArtTextBG
.
rectTransform
,
fram
eRt
,
_overFrameTextClipRt
);
MatchRectByWorldCorners
(
_overFrameArtTextBG
.
rectTransform
,
splitArtReferenc
eRt
,
_overFrameTextClipRt
);
_overFrameArtTextBG
.
rectTransform
.
sizeDelta
*=
spec
.
scale
;
_overFrameArtTextBG
.
rectTransform
.
anchoredPosition
+=
spec
.
offset
;
...
...
@@ -1651,11 +2643,12 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
_overFrameArtText
.
gameObject
.
SetActive
(
true
);
_overFrameArtText
.
texture
=
tex
;
_overFrameArtText
.
uvRect
=
_overFrameArt
!=
null
?
_overFrameArt
.
uvRect
:
_overFrameArtText
.
uvRect
;
_overFrameArtText
.
color
=
new
Color
(
1f
,
1f
,
1f
,
OverFrameTextArtAlpha
);
float
textArtAlpha
=
isPendulum
?
OverFramePendulumTextArtAlpha
:
OverFrameTextArtAlpha
;
_overFrameArtText
.
color
=
new
Color
(
1f
,
1f
,
1f
,
textArtAlpha
);
_overFrameArtText
.
maskable
=
true
;
// Align continuation to full frame (then clipped by parchment mask)
MatchRectByWorldCorners
(
_overFrameArtText
.
rectTransform
,
fram
eRt
,
_overFrameTextClipRt
);
MatchRectByWorldCorners
(
_overFrameArtText
.
rectTransform
,
splitArtReferenc
eRt
,
_overFrameTextClipRt
);
_overFrameArtText
.
rectTransform
.
sizeDelta
*=
spec
.
scale
;
_overFrameArtText
.
rectTransform
.
anchoredPosition
+=
spec
.
offset
;
}
...
...
@@ -1663,11 +2656,138 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
if
(
_overFrameArtTextBG
!=
null
)
_overFrameArtTextBG
.
transform
.
SetSiblingIndex
(
0
);
if
(
_overFrameArtText
!=
null
)
_overFrameArtText
.
transform
.
SetSiblingIndex
(
1
);
if
(
hasPendulumParchmentZones
)
{
Texture2D
pendTex
=
tex
;
Rect
pendUv
=
_overFrameArt
!=
null
?
_overFrameArt
.
uvRect
:
new
Rect
(
0f
,
0f
,
1f
,
1f
);
if
(
OverFramePendulumUpperUseBaseArtContinuation
&&
baseArt
!=
null
&&
baseArt
.
texture
is
Texture2D
baseTex
)
{
// Scale parchments should continue the underlying artwork, not transparent cutout regions.
pendTex
=
baseTex
;
pendUv
=
baseArt
.
uvRect
;
}
float
pendAlpha
=
OverFramePendulumUpperZoneArtAlpha
;
float
centerInsetSide
=
OverFramePendulumCenterClipInsetSide
;
float
centerInsetTop
=
OverFramePendulumCenterClipInsetTop
;
float
centerInsetBottom
=
OverFramePendulumCenterClipInsetBottom
;
float
scaleInsetOuter
=
OverFramePendulumScaleClipInsetOuter
;
float
scaleInsetInner
=
OverFramePendulumScaleClipInsetInner
;
float
scaleInsetTop
=
OverFramePendulumScaleClipInsetTop
;
float
scaleInsetBottom
=
OverFramePendulumScaleClipInsetBottom
;
float
fallbackClipInsetSide
=
OverFramePendulumZoneFallbackClipInsetSide
;
float
fallbackClipInsetTop
=
OverFramePendulumZoneFallbackClipInsetTop
;
float
fallbackClipInsetBottom
=
OverFramePendulumZoneFallbackClipInsetBottom
;
if
(
pendCenterBoxRt
==
null
)
{
centerInsetSide
+=
fallbackClipInsetSide
;
centerInsetTop
+=
fallbackClipInsetTop
;
centerInsetBottom
+=
fallbackClipInsetBottom
;
}
if
(
pendLeftBoxRt
==
null
)
{
scaleInsetOuter
+=
fallbackClipInsetSide
;
scaleInsetInner
+=
fallbackClipInsetSide
;
scaleInsetTop
+=
fallbackClipInsetTop
;
scaleInsetBottom
+=
fallbackClipInsetBottom
;
}
if
(
pendRightBoxRt
==
null
)
{
scaleInsetOuter
+=
fallbackClipInsetSide
;
scaleInsetInner
+=
fallbackClipInsetSide
;
scaleInsetTop
+=
fallbackClipInsetTop
;
scaleInsetBottom
+=
fallbackClipInsetBottom
;
}
UpdateMaskedContinuationClip
(
ref
_overFramePendulumCenterClipRt
,
ref
_overFramePendulumCenterClip
,
ref
_overFramePendulumCenterArt
,
"OverFramePendulumCenterClip"
,
"OverFramePendulumCenterArt"
,
pendCenterNrm
,
frameRt
,
fadeParentRt
,
splitArtReferenceRt
,
pendTex
,
pendUv
,
spec
,
pendAlpha
,
centerInsetSide
,
centerInsetSide
,
centerInsetTop
,
centerInsetBottom
,
pendCenterBoxRt
,
OverFramePendulumUpperZoneBorderCutFactor
,
OverFramePendulumUpperZoneBorderSafetyInset
);
UpdateMaskedContinuationClip
(
ref
_overFramePendulumScaleClipL_Rt
,
ref
_overFramePendulumScaleClipL
,
ref
_overFramePendulumScaleArtL
,
"OverFramePendulumScaleClipL"
,
"OverFramePendulumScaleArtL"
,
pendLeftScaleNrm
,
frameRt
,
fadeParentRt
,
splitArtReferenceRt
,
pendTex
,
pendUv
,
spec
,
pendAlpha
,
scaleInsetOuter
,
scaleInsetInner
,
scaleInsetTop
,
scaleInsetBottom
,
pendLeftBoxRt
,
OverFramePendulumUpperZoneBorderCutFactor
,
OverFramePendulumUpperZoneBorderSafetyInset
);
UpdateMaskedContinuationClip
(
ref
_overFramePendulumScaleClipR_Rt
,
ref
_overFramePendulumScaleClipR
,
ref
_overFramePendulumScaleArtR
,
"OverFramePendulumScaleClipR"
,
"OverFramePendulumScaleArtR"
,
pendRightScaleNrm
,
frameRt
,
fadeParentRt
,
splitArtReferenceRt
,
pendTex
,
pendUv
,
spec
,
pendAlpha
,
scaleInsetInner
,
scaleInsetOuter
,
scaleInsetTop
,
scaleInsetBottom
,
pendRightBoxRt
,
OverFramePendulumUpperZoneBorderCutFactor
,
OverFramePendulumUpperZoneBorderSafetyInset
);
}
else
{
if
(
_overFramePendulumCenterClipRt
)
_overFramePendulumCenterClipRt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumCenterArt
)
_overFramePendulumCenterArt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipL_Rt
)
_overFramePendulumScaleClipL_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleClipR_Rt
)
_overFramePendulumScaleClipR_Rt
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtL
)
_overFramePendulumScaleArtL
.
gameObject
.
SetActive
(
false
);
if
(
_overFramePendulumScaleArtR
)
_overFramePendulumScaleArtR
.
gameObject
.
SetActive
(
false
);
}
// IMPORTANT: the continuation must be BELOW the description text (so text stays crisp),
// but ABOVE the parchment background (so you can actually see the art "under" the parchment like the official proxy).
// We insert at desc index so Unity shifts desc (and anything above it) one slot up.
int
descIdx
=
desc
.
transform
.
GetSiblingIndex
();
_overFrameTextClipRt
.
SetSiblingIndex
(
Mathf
.
Clamp
(
descIdx
,
0
,
fadeParentRt
.
childCount
-
1
));
if
(
_overFramePendulumCenterClipRt
)
_overFramePendulumCenterClipRt
.
SetSiblingIndex
(
Mathf
.
Clamp
(
descIdx
,
0
,
fadeParentRt
.
childCount
-
1
));
if
(
_overFramePendulumScaleClipL_Rt
)
_overFramePendulumScaleClipL_Rt
.
SetSiblingIndex
(
Mathf
.
Clamp
(
descIdx
,
0
,
fadeParentRt
.
childCount
-
1
));
if
(
_overFramePendulumScaleClipR_Rt
)
_overFramePendulumScaleClipR_Rt
.
SetSiblingIndex
(
Mathf
.
Clamp
(
descIdx
,
0
,
fadeParentRt
.
childCount
-
1
));
return
true
;
}
...
...
@@ -1691,6 +2811,7 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
CleanupOverFrame
();
return
false
;
}
bool
isPendulum
=
IsPendulumArtImage
(
baseArt
);
// 2) Load overlay PNG from OverFrame/Overframe folder by card ID.
var
tex
=
LoadOverFrameTexture
(
code
);
...
...
@@ -1748,35 +2869,11 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// Decide UV cropping:
// - Full-card overlays must NOT inherit baseArt.uvRect (it would zoom/crop the overlay).
// -
If the overlay texture aspect differs from the frame, crop UV to "cover" the frame (no stretching)
.
// -
Keep full UV for full-card overlays to match the legacy overframe mod behavior
.
Rect
overUv
=
baseArt
.
uvRect
;
if
(
looksFullCard
&&
cardFrame
!=
null
)
{
var
fr
=
cardFrame
.
rectTransform
.
rect
;
float
targetAspect
=
fr
.
width
/
Mathf
.
Max
(
1f
,
fr
.
height
);
float
texAspect
=
(
float
)
tex
.
width
/
Mathf
.
Max
(
1f
,
tex
.
height
);
if
(
Mathf
.
Abs
(
texAspect
-
targetAspect
)
>
0.0005f
)
{
if
(
texAspect
>
targetAspect
)
{
// Texture is wider: crop left/right.
float
w
=
targetAspect
/
texAspect
;
float
x
=
(
1f
-
w
)
*
0.5f
;
overUv
=
new
Rect
(
x
,
0f
,
w
,
1f
);
}
else
{
// Texture is taller/narrower: crop top/bottom.
float
h
=
texAspect
/
targetAspect
;
float
y
=
(
1f
-
h
)
*
0.5f
;
overUv
=
new
Rect
(
0f
,
y
,
1f
,
h
);
}
}
else
{
overUv
=
new
Rect
(
0f
,
0f
,
1f
,
1f
);
}
overUv
=
new
Rect
(
0f
,
0f
,
1f
,
1f
);
}
_overFrameArt
.
uvRect
=
overUv
;
...
...
@@ -1817,7 +2914,7 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
if
(
looksFullCard
&&
cardFrame
!=
null
)
{
splitApplied
=
ApplyOverFrameProxySplit
(
tex
,
baseArt
,
spec
,
cardFrame
.
rectTransform
,
anchorParent
,
parentRt
);
splitApplied
=
ApplyOverFrameProxySplit
(
tex
,
baseArt
,
spec
,
cardFrame
.
rectTransform
,
anchorParent
,
parentRt
,
isPendulum
);
}
if
(!
splitApplied
)
...
...
@@ -1871,7 +2968,7 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
// 10) FINAL: Apply effect-box fade + enforce ordering
// OverFrameArt → FadeOverlay → Text/UI
ApplyOverFrameEffectBoxFade
();
ApplyOverFrameEffectBoxFade
(
isPendulum
);
// Keep important UI on top (don’t hide ATK/Level/etc)
if
(
cardName
)
cardName
.
transform
.
SetAsLastSibling
();
...
...
@@ -2123,8 +3220,8 @@ private bool IsEffectBoxRectInnerFill(RectTransform boxRt, Rect measuredNrm)
line
.
SetActive
(
true
);
textATK
.
SetActive
(
true
);
textDEF
.
SetActive
(
true
);
numATK
.
text
=
data
.
Attack
==
-
2
?
"?"
:
data
.
Attack
.
To
String
();
numDEF
.
text
=
data
.
Defense
==
-
2
?
"?"
:
data
.
Defense
.
To
String
();
numATK
.
text
=
data
.
GetAttack
String
();
numDEF
.
text
=
data
.
GetDefense
String
();
linkCount
.
gameObject
.
SetActive
(
false
);
spellType
.
text
=
string
.
Empty
;
cardDescription
.
GetComponent
<
RectTransform
>().
sizeDelta
=
new
Vector2
(
590f
,
160f
);
...
...
Assets/Scripts/MDPro3/Duel/BG/DuelBGManager.cs
View file @
2a369a96
...
...
@@ -240,24 +240,30 @@ namespace MDPro3.Duel
#
region
Mate
var
mateConfig
=
Config
.
Get
(
condition
.
ToString
()
+
"Mate0"
,
Program
.
items
.
mates
[
0
].
id
.
ToString
());
if
(
mateConfig
!=
Items
.
CODE_NONE
.
ToString
()
||
deck
!=
null
)
var
overrideDeckAppearance
=
Config
.
GetBool
(
"OverrideDeckAppearance"
,
false
);
var
mateConfigIsNone
=
mateConfig
==
Items
.
CODE_NONE
.
ToString
();
if
(!
mateConfigIsNone
||
(
deck
!=
null
&&
!
overrideDeckAppearance
))
{
int
mateCode
=
int
.
Parse
(
mateConfig
);
if
(
deck
!=
null
&&
!
Config
.
GetBool
(
"OverrideDeckAppearance"
,
false
)
)
if
(
deck
!=
null
&&
!
overrideDeckAppearance
&&
!
mateConfigIsNone
)
mateCode
=
deck
.
Mate
;
var
mate
=
await
ABLoader
.
LoadMateAsync
(
mateCode
);
if
(
mate
!=
null
)
if
(
mate
Code
!=
Items
.
CODE_NONE
)
{
mate0
=
mate
;
mate0
.
parent
=
pos_Avatar_near
;
mate0
.
gameObject
.
SetActive
(
false
);
var
mate
=
await
ABLoader
.
LoadMateAsync
(
mateCode
);
if
(
mate
!=
null
)
{
mate0
=
mate
;
mate0
.
parent
=
pos_Avatar_near
;
mate0
.
gameObject
.
SetActive
(
false
);
}
}
}
mateConfig
=
Config
.
Get
(
condition
.
ToString
()
+
"Mate1"
,
Program
.
items
.
mates
[
0
].
id
.
ToString
());
if
(
mateConfig
!=
Items
.
CODE_NONE
.
ToString
())
{
var
mate
=
await
ABLoader
.
LoadMateAsync
(
int
.
Parse
(
Config
.
Get
(
condition
.
ToString
()
+
"Mate1"
,
Program
.
items
.
mates
[
0
].
id
.
ToString
())
));
var
mate
=
await
ABLoader
.
LoadMateAsync
(
int
.
Parse
(
mateConfig
));
if
(
mate
!=
null
)
{
mate1
=
mate
;
...
...
@@ -3295,4 +3301,4 @@ namespace MDPro3.Duel
#
endregion
}
}
\ No newline at end of file
}
Assets/Scripts/MDPro3/Duel/CardDescription.cs
View file @
2a369a96
...
...
@@ -202,10 +202,12 @@ namespace MDPro3
manager
.
GetElement
(
"Defense"
).
SetActive
(
true
);
manager
.
GetElement
(
"TextDefense"
).
SetActive
(
true
);
manager
.
GetElement
<
Text
>(
"TextDefense"
).
text
=
data
.
Defense
==
-
2
?
"?"
:
data
.
Defense
.
ToString
();
if
(
data
.
Defense
>
(
origin
.
Defense
<
0
?
0
:
origin
.
Defense
))
var
defense
=
Card
.
NormalizeBattleValue
(
data
.
Defense
,
true
);
var
originDefense
=
Card
.
NormalizeBattleValue
(
origin
.
Defense
,
false
);
manager
.
GetElement
<
Text
>(
"TextDefense"
).
text
=
Card
.
FormatBattleValue
(
defense
);
if
(
Card
.
NormalizeBattleValue
(
defense
,
false
)
>
originDefense
)
manager
.
GetElement
<
Text
>(
"TextDefense"
).
color
=
upColor
;
else
if
(
data
.
Defense
<
origin
.
Defense
)
else
if
(
Card
.
NormalizeBattleValue
(
defense
,
false
)
<
origin
Defense
)
manager
.
GetElement
<
Text
>(
"TextDefense"
).
color
=
downColor
;
else
manager
.
GetElement
<
Text
>(
"TextDefense"
).
color
=
equalColor
;
...
...
@@ -238,10 +240,12 @@ namespace MDPro3
}
}
manager
.
GetElement
<
Text
>(
"TextAttack"
).
text
=
data
.
Attack
==
-
2
?
"?"
:
data
.
Attack
.
ToString
();
if
(
data
.
Attack
>
(
origin
.
Attack
<
0
?
0
:
origin
.
Attack
))
var
attack
=
Card
.
NormalizeBattleValue
(
data
.
Attack
,
true
);
var
originAttack
=
Card
.
NormalizeBattleValue
(
origin
.
Attack
,
false
);
manager
.
GetElement
<
Text
>(
"TextAttack"
).
text
=
Card
.
FormatBattleValue
(
attack
);
if
(
Card
.
NormalizeBattleValue
(
attack
,
false
)
>
originAttack
)
manager
.
GetElement
<
Text
>(
"TextAttack"
).
color
=
upColor
;
else
if
(
data
.
Attack
<
origin
.
Attack
)
else
if
(
Card
.
NormalizeBattleValue
(
attack
,
false
)
<
origin
Attack
)
manager
.
GetElement
<
Text
>(
"TextAttack"
).
color
=
downColor
;
else
manager
.
GetElement
<
Text
>(
"TextAttack"
).
color
=
equalColor
;
...
...
@@ -527,4 +531,4 @@ namespace MDPro3
}
}
}
\ No newline at end of file
}
Assets/Scripts/MDPro3/Duel/GameCard.cs
View file @
2a369a96
...
...
@@ -423,10 +423,10 @@ namespace MDPro3
else
if
(
data
.
Id
>
0
)
data
.
CloneTo
(
lastValidData
);
if
(
d
.
Attack
<
0
)
d
.
Attack
=
0
;
if
(
d
.
Defense
<
0
)
d
.
Defense
=
0
;
d
.
Attack
=
Card
.
NormalizeBattleValue
(
d
.
Attack
,
false
);
d
.
Defense
=
Card
.
NormalizeBattleValue
(
d
.
Defense
,
false
)
;
d
.
rAttack
=
Card
.
NormalizeBattleValue
(
d
.
rAttack
,
false
);
d
.
rDefense
=
Card
.
NormalizeBattleValue
(
d
.
rDefense
,
false
)
;
if
(
d
.
Id
!=
data
.
Id
)
{
...
...
@@ -441,6 +441,8 @@ namespace MDPro3
}
}
data
=
d
;
if
(
model
!=
null
&&
p
.
InLocation
(
CardLocation
.
Hand
)
&&
!
inAnimation
)
RefreshHandTurnByCode
();
RefreshLabel
();
UpdateExDeckTop
();
}
...
...
@@ -1547,6 +1549,7 @@ namespace MDPro3
Program
.
instance
.
ocgcore
.
SetExDeckTop
(
this
);
ShowFaceDownCardOrNot
(
NeedShowFaceDownCard
());
RefreshHandTurnByCode
();
if
(
p
.
InLocation
(
CardLocation
.
Deck
,
CardLocation
.
Extra
))
Program
.
instance
.
ocgcore
.
DuelBGManager
.
ResizeDecks
();
...
...
@@ -1554,6 +1557,13 @@ namespace MDPro3
Program
.
instance
.
ocgcore
.
DuelBGManager
.
RefreshGravesState
();
}
private
void
RefreshHandTurnByCode
()
{
if
(
model
==
null
||
!
p
.
InLocation
(
CardLocation
.
Hand
))
return
;
manager
.
GetElement
<
Transform
>(
"Turn"
).
localEulerAngles
=
new
Vector3
(
0
,
0
,
data
.
Id
==
0
?
180
:
0
);
}
public
Sequence
StartCardSequence
(
Vector3
fromPosition
,
Vector3
fromRotation
,
float
interval
=
0f
)
{
if
(
model
==
null
)
...
...
Assets/Scripts/MDPro3/Duel/YGOSharp/Card.cs
View file @
2a369a96
...
...
@@ -10,6 +10,9 @@ namespace MDPro3.Duel.YGOSharp
{
public
class
Card
{
public
const
int
MAX_BATTLE_POWER_DISPLAY
=
99
_999_999
;
public
const
int
UNKNOWN_BATTLE_VALUE
=
-
2
;
public
int
Id
;
public
int
Ot
;
public
int
Alias
;
...
...
@@ -229,15 +232,31 @@ namespace MDPro3.Duel.YGOSharp
public
string
GetAttackString
()
{
return
Attack
==
-
2
?
"?"
:
Attack
.
ToString
(
);
return
FormatBattleValue
(
Attack
);
}
public
string
GetDefenseString
()
{
return
Defense
==
-
2
?
"?"
:
Defense
.
ToString
();
return
FormatBattleValue
(
Defense
);
}
public
static
int
NormalizeBattleValue
(
int
value
,
bool
keepUnknown
=
true
)
{
if
(
value
==
UNKNOWN_BATTLE_VALUE
)
return
keepUnknown
?
UNKNOWN_BATTLE_VALUE
:
0
;
if
(
value
<
0
)
return
MAX_BATTLE_POWER_DISPLAY
;
if
(
value
>
MAX_BATTLE_POWER_DISPLAY
)
return
MAX_BATTLE_POWER_DISPLAY
;
return
value
;
}
public
static
string
FormatBattleValue
(
int
value
)
{
var
normalized
=
NormalizeBattleValue
(
value
,
true
);
return
normalized
==
UNKNOWN_BATTLE_VALUE
?
"?"
:
normalized
.
ToString
();
}
// Put this near other fields/helpers inside Card class:
private
static
readonly
string
PendulumSeparatorLine
=
new
string
(
'─'
,
14
);
public
string
GetDescription
(
bool
withSetName
=
false
)
...
...
@@ -614,4 +633,4 @@ namespace MDPro3.Duel.YGOSharp
#
endregion
}
}
\ No newline at end of file
}
Assets/Scripts/MDPro3/Duel/YGOSharp/Deck.cs
View file @
2a369a96
...
...
@@ -290,7 +290,10 @@ namespace MDPro3.Duel.YGOSharp
var
ydk
=
GetYDK
();
try
{
deckName
=
Path
.
GetFileNameWithoutExtension
(
deckName
);
deckName
=
NormalizeDeckFileName
(
deckName
);
if
(!
IsValidDeckFileName
(
deckName
))
return
false
;
var
path
=
Program
.
PATH_DECK
+
(
type
==
string
.
Empty
?
string
.
Empty
:
$"
{
type
}
/"
)
+
deckName
+
Program
.
EXPANSION_YDK
;
var
dir
=
Path
.
GetDirectoryName
(
path
);
if
(!
Directory
.
Exists
(
dir
))
...
...
@@ -308,6 +311,25 @@ namespace MDPro3.Duel.YGOSharp
return
true
;
}
public
static
string
NormalizeDeckFileName
(
string
deckName
)
{
deckName
=
deckName
?.
Trim
()
??
string
.
Empty
;
if
(
deckName
.
EndsWith
(
Program
.
EXPANSION_YDK
,
StringComparison
.
OrdinalIgnoreCase
))
deckName
=
deckName
[..^
Program
.
EXPANSION_YDK
.
Length
];
return
deckName
;
}
public
static
bool
IsValidDeckFileName
(
string
deckName
)
{
if
(
string
.
IsNullOrWhiteSpace
(
deckName
))
return
false
;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
if
(
deckName
.
EndsWith
(
" "
,
StringComparison
.
Ordinal
)
||
deckName
.
EndsWith
(
"."
,
StringComparison
.
Ordinal
))
return
false
;
#endif
return
deckName
.
IndexOfAny
(
Path
.
GetInvalidFileNameChars
())
<
0
;
}
public
string
GetYDK
()
{
var
value
=
deckHint
+
"\r\n#main"
;
...
...
Assets/Scripts/MDPro3/Net/OnlineService.cs
View file @
2a369a96
...
...
@@ -28,20 +28,22 @@ namespace MDPro3.Net
private
static
async
UniTask
InitializeGenesysLflist
()
{
// Load cached/local genesys data immediately so Deck Editor works offline.
ParseGenesysLflist
();
var
eTag
=
await
GetETagAsync
(
URL_GENESYS_LFLIST
);
if
(!
string
.
IsNullOrEmpty
(
eTag
))
if
(
string
.
IsNullOrEmpty
(
eTag
))
return
;
var
configTag
=
Config
.
Get
(
GetLocalETagKey
(
URL_GENESYS_LFLIST
),
Config
.
EMPTY_STRING
);
if
(!
string
.
Equals
(
eTag
,
configTag
,
StringComparison
.
Ordinal
))
{
var
configTag
=
Config
.
Get
(
GetLocalETagKey
(
URL_GENESYS_LFLIST
),
Config
.
EMPTY_STRING
);
if
(!
string
.
Equals
(
eTag
,
configTag
,
StringComparison
.
Ordinal
))
{
Program
.
Debug
(
"Update Genesys Lflist."
);
await
DownloadGenesysLflist
(
eTag
);
}
else
Program
.
Debug
(
"Genesys Lflist do not need update."
);
Program
.
Debug
(
"Update Genesys Lflist."
);
await
DownloadGenesysLflist
(
eTag
);
ParseGenesysLflist
();
}
ParseGenesysLflist
(
);
else
Program
.
Debug
(
"Genesys Lflist do not need update."
);
}
private
static
bool
GenesysRequiresDownload
()
...
...
@@ -74,6 +76,10 @@ namespace MDPro3.Net
private
static
void
ParseGenesysLflist
()
{
genesysBannedCards
.
Clear
();
genesysPoints
.
Clear
();
officialGenesysLimit
=
100
;
if
(!
File
.
Exists
(
PATH_GENESYS_LFLIST
))
return
;
...
...
@@ -88,6 +94,13 @@ namespace MDPro3.Net
if
(
string
.
IsNullOrEmpty
(
line
)
||
line
.
StartsWith
(
"#"
))
continue
;
if
(
line
.
StartsWith
(
"$"
))
{
// e.g. "$genesys 100"
if
(
line
.
StartsWith
(
"$genesys"
,
StringComparison
.
OrdinalIgnoreCase
))
{
var
limitParts
=
line
.
Split
(
new
[]
{
' '
},
StringSplitOptions
.
RemoveEmptyEntries
);
if
(
limitParts
.
Length
>=
2
&&
int
.
TryParse
(
limitParts
[
1
],
out
var
limit
))
officialGenesysLimit
=
limit
;
}
currentType
=
line
;
continue
;
}
...
...
@@ -242,6 +255,7 @@ namespace MDPro3.Net
public
static
async
UniTask
<
string
>
GetETagAsync
(
string
url
)
{
using
var
headRequest
=
UnityWebRequest
.
Head
(
url
);
headRequest
.
timeout
=
8
;
await
headRequest
.
SendWebRequest
();
if
(
headRequest
.
result
!=
UnityWebRequest
.
Result
.
Success
)
...
...
@@ -269,4 +283,4 @@ namespace MDPro3.Net
public
string
banType
;
public
int
point
;
}
}
\ No newline at end of file
}
Assets/Scripts/MDPro3/Servant/Appearance.cs
View file @
2a369a96
...
...
@@ -193,14 +193,14 @@ namespace MDPro3.Servant
duelFace1
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"DuelFace1"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
1
);
duelFace0Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"DuelFace0Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
2
);
duelFace1Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"DuelFace1Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
3
);
watchFace0
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace0"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
watchFace1
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace1"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
watchFace0Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace0Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
watchFace1Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace1Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
replayFace0
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace0"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
replayFace1
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace1"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
replayFace0Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace0Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
replayFace1Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace1Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
);
watchFace0
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace0"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
0
);
watchFace1
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace1"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
1
);
watchFace0Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace0Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
2
);
watchFace1Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"WatchFace1Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
3
);
replayFace0
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace0"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
0
);
replayFace1
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace1"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
1
);
replayFace0Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace0Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
2
);
replayFace1Tag
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
Config
.
Get
(
"ReplayFace1Tag"
,
Program
.
items
.
faces
[
0
].
id
.
ToString
()),
Items
.
ItemType
.
Face
,
3
);
defaultFace0
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
"1010039"
,
Items
.
ItemType
.
Face
);
defaultFace1
=
await
Program
.
items
.
LoadConcreteItemIconAsync
(
"1010001"
,
Items
.
ItemType
.
Face
);
...
...
Assets/Scripts/MDPro3/Servant/OcgCore.cs
View file @
2a369a96
...
...
@@ -1948,13 +1948,13 @@ namespace MDPro3.Servant
DuelBGManager
.
PlayGraveEffect
(
p
,
isIn
);
}
public
int
GetAllAtk
(
bool
mySide
)
public
long
GetAllAtk
(
bool
mySide
)
{
int
allAttack
=
0
;
long
allAttack
=
0
;
var
monsters
=
GCS_GetLocationCards
(
mySide
?
0
:
1
,
(
int
)
CardLocation
.
MonsterZone
);
foreach
(
var
card
in
monsters
)
if
((
card
.
p
.
position
&
(
uint
)
CardPosition
.
FaceUpAttack
)
>
0
)
allAttack
+=
card
.
GetData
().
Attack
;
allAttack
+=
Card
.
NormalizeBattleValue
(
card
.
GetData
().
Attack
,
false
)
;
return
allAttack
;
}
...
...
Assets/Scripts/MDPro3/System/UserInput.cs
View file @
2a369a96
...
...
@@ -165,7 +165,7 @@ namespace MDPro3
MouseMovedEvent
();
}
if
(
MoveInput
!=
Vector2
.
zero
)
if
(
MoveInput
!=
Vector2
.
zero
&&
!
InputFieldActivating
()
)
{
if
(
Cursor
.
lockState
==
CursorLockMode
.
None
)
{
...
...
@@ -456,10 +456,11 @@ namespace MDPro3
if
(
current
==
null
)
return
false
;
if
(!
current
.
TryGetComponent
<
Selectable
>(
out
var
selectable
))
return
false
;
if
(
selectable
is
TMP_InputField
inputField
)
return
inputField
.
isFocused
;
else
return
false
;
if
(
selectable
is
TMP_InputField
tmpInputField
)
return
tmpInputField
.
isFocused
;
if
(
selectable
is
InputField
legacyInputField
)
return
legacyInputField
.
isFocused
;
return
false
;
}
#
endregion
...
...
Assets/Scripts/MDPro3/UI/Popup/Old/PopupDuelSelection.cs
View file @
2a369a96
using
System.Collections
;
using
System.Collections.Generic
;
using
UnityEngine
;
using
UnityEngine.EventSystems
;
using
UnityEngine.UI
;
namespace
MDPro3.UI
...
...
@@ -17,13 +18,17 @@ namespace MDPro3.UI
{
base
.
InitializeSelections
();
Program
.
instance
.
currentServant
.
returnAction
=
null
;
var
selectionButtons
=
new
List
<
Button
>();
for
(
int
i
=
1
;
i
<
selections
.
Count
;
i
++)
{
GameObject
newSelection
=
Instantiate
(
item
);
newSelection
.
transform
.
SetParent
(
scrollRect
.
content
,
false
);
newSelection
.
transform
.
GetChild
(
0
).
GetChild
(
0
).
GetComponent
<
Text
>().
text
=
selections
[
i
];
newSelection
.
transform
.
GetChild
(
0
).
name
=
responses
[
i
-
1
].
ToString
();
newSelection
.
transform
.
GetChild
(
0
).
GetComponent
<
Button
>().
onClick
.
AddListener
(()
=>
var
buttonTransform
=
newSelection
.
transform
.
GetChild
(
0
);
buttonTransform
.
name
=
responses
[
i
-
1
].
ToString
();
var
button
=
buttonTransform
.
GetComponent
<
Button
>();
button
.
onClick
.
AddListener
(()
=>
{
string
selected
=
UnityEngine
.
EventSystems
.
EventSystem
.
current
.
currentSelectedGameObject
.
name
;
...
...
@@ -35,14 +40,39 @@ namespace MDPro3.UI
}
Hide
();
});
selectionButtons
.
Add
(
button
);
newSelection
.
GetComponent
<
RectTransform
>().
anchoredPosition
=
new
Vector2
(
0
,
-
20
-
90
*
(
i
-
1
));
}
for
(
int
i
=
0
;
i
<
selectionButtons
.
Count
;
i
++)
{
var
button
=
selectionButtons
[
i
];
var
navigation
=
button
.
navigation
;
navigation
.
mode
=
Navigation
.
Mode
.
Explicit
;
navigation
.
selectOnUp
=
selectionButtons
[
Mathf
.
Max
(
0
,
i
-
1
)];
navigation
.
selectOnDown
=
selectionButtons
[
Mathf
.
Min
(
selectionButtons
.
Count
-
1
,
i
+
1
)];
navigation
.
selectOnLeft
=
button
;
navigation
.
selectOnRight
=
button
;
button
.
navigation
=
navigation
;
}
if
(
scrollRect
.
verticalScrollbar
!=
null
)
{
var
scrollbarNavigation
=
scrollRect
.
verticalScrollbar
.
navigation
;
scrollbarNavigation
.
mode
=
Navigation
.
Mode
.
None
;
scrollRect
.
verticalScrollbar
.
navigation
=
scrollbarNavigation
;
}
scrollRect
.
content
.
sizeDelta
=
new
Vector2
(
scrollRect
.
content
.
sizeDelta
.
x
,
25
+
(
selections
.
Count
-
1
)
*
90
);
baseRect
.
sizeDelta
=
new
Vector2
(
baseRect
.
sizeDelta
.
x
,
scrollRect
.
content
.
sizeDelta
.
y
+
50
>
800
?
800
:
scrollRect
.
content
.
sizeDelta
.
y
+
50
);
scrollRect
.
verticalNormalizedPosition
=
1f
;
if
(
selectionButtons
.
Count
>
0
)
EventSystem
.
current
.
SetSelectedGameObject
(
selectionButtons
[
0
].
gameObject
);
}
}
...
...
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionButton_MainMenu.cs
View file @
2a369a96
...
...
@@ -2,12 +2,19 @@ using System.Collections.Generic;
using
DG.Tweening
;
using
UnityEngine
;
using
UnityEngine.EventSystems
;
using
TMPro
;
using
YgomSystem.UI
;
namespace
MDPro3.UI
{
public
class
SelectionButton_MainMenu
:
SelectionButton
{
private
const
float
ArrowRestX
=
-
5f
;
private
const
float
ArrowStartX
=
-
262f
;
private
const
float
ArrowTextGap
=
30f
;
private
static
float
s_BaseButtonWidth
=
-
1f
;
private
static
float
s_BaseTextWidth
=
-
1f
;
protected
override
void
Awake
()
{
ElementsReset
();
...
...
@@ -19,6 +26,8 @@ namespace MDPro3.UI
private
void
ElementsReset
()
{
EnsureMainMenuButtonWidth
();
// Out
Manager
.
GetElement
<
CanvasGroup
>(
"Out"
).
alpha
=
1f
;
Manager
.
GetElement
<
RectTransform
>(
"Line"
).
localScale
=
Vector3
.
one
;
...
...
@@ -27,7 +36,7 @@ namespace MDPro3.UI
Manager
.
GetElement
<
CanvasGroup
>(
"Hover"
).
alpha
=
0f
;
Manager
.
GetElement
<
RectTransform
>(
"PlateTween"
).
localScale
=
new
Vector3
(
0.5f
,
1f
,
1f
);
Manager
.
GetElement
<
RectTransform
>(
"HoverTextMask"
).
offsetMax
=
new
Vector2
(-
340f
,
0f
);
Manager
.
GetElement
<
RectTransform
>(
"Arrow"
).
localPosition
=
new
Vector2
(
-
5f
,
0f
);
Manager
.
GetElement
<
RectTransform
>(
"Arrow"
).
localPosition
=
new
Vector2
(
ArrowRestX
,
0f
);
// Cursor
Manager
.
GetElement
<
RectTransform
>(
"Corner"
).
offsetMin
=
new
Vector2
(
0f
,
0f
);
...
...
@@ -42,6 +51,7 @@ namespace MDPro3.UI
if
(
hoverd
)
return
;
base
.
HoverOn
();
EnsureMainMenuButtonWidth
();
Manager
.
GetElement
<
CanvasGroup
>(
"Out"
).
alpha
=
0f
;
...
...
@@ -54,11 +64,73 @@ namespace MDPro3.UI
Manager
.
GetElement
<
RectTransform
>(
"HoverTextMask"
).
offsetMax
=
new
Vector2
(-
340f
,
0f
);
Manager
.
GetElement
<
RectTransform
>(
"HoverTextMask"
).
DOSizeDelta
(
Vector2
.
zero
,
0.2f
);
Manager
.
GetElement
<
RectTransform
>(
"Arrow"
).
anchoredPosition
=
new
Vector2
(
-
262f
,
0f
);
var
tween2
=
Manager
.
GetElement
<
RectTransform
>(
"Arrow"
).
DOAnchorPosX
(
-
5f
,
0.33f
).
SetEase
(
Ease
.
OutQuart
);
Manager
.
GetElement
<
RectTransform
>(
"Arrow"
).
anchoredPosition
=
new
Vector2
(
ArrowStartX
,
0f
);
var
tween2
=
Manager
.
GetElement
<
RectTransform
>(
"Arrow"
).
DOAnchorPosX
(
ArrowRestX
,
0.33f
).
SetEase
(
Ease
.
OutQuart
);
hoverOnTweens
.
Add
(
tween2
);
}
private
void
EnsureMainMenuButtonWidth
()
{
var
selfRect
=
transform
as
RectTransform
;
if
(
selfRect
==
null
||
selfRect
.
parent
==
null
)
return
;
var
selfTextRect
=
Manager
.
GetElement
<
RectTransform
>(
"Text"
);
if
(
s_BaseButtonWidth
<
0f
)
s_BaseButtonWidth
=
selfRect
.
sizeDelta
.
x
;
if
(
s_BaseTextWidth
<
0f
&&
selfTextRect
!=
null
)
s_BaseTextWidth
=
selfTextRect
.
rect
.
width
;
if
(
s_BaseButtonWidth
<=
0f
||
s_BaseTextWidth
<=
0f
)
return
;
var
buttons
=
selfRect
.
parent
.
GetComponentsInChildren
<
SelectionButton_MainMenu
>(
true
);
var
maxTargetWidth
=
s_BaseButtonWidth
;
foreach
(
var
button
in
buttons
)
{
if
(
button
==
null
)
continue
;
var
buttonRect
=
button
.
transform
as
RectTransform
;
if
(
buttonRect
==
null
)
continue
;
var
text
=
button
.
Manager
.
GetElement
<
TextMeshProUGUI
>(
"Text"
);
var
textOver
=
button
.
Manager
.
GetElement
<
TextMeshProUGUI
>(
"TextOver"
);
var
textRect
=
button
.
Manager
.
GetElement
<
RectTransform
>(
"Text"
);
if
(
text
==
null
||
textRect
==
null
)
continue
;
text
.
ForceMeshUpdate
();
textOver
?.
ForceMeshUpdate
();
var
maxLabelWidth
=
text
.
preferredWidth
;
if
(
textOver
!=
null
&&
textOver
.
preferredWidth
>
maxLabelWidth
)
maxLabelWidth
=
textOver
.
preferredWidth
;
var
neededTextWidth
=
maxLabelWidth
+
ArrowTextGap
;
var
extraWidth
=
Mathf
.
Max
(
0f
,
neededTextWidth
-
s_BaseTextWidth
);
var
targetWidth
=
s_BaseButtonWidth
+
extraWidth
;
if
(
targetWidth
>
maxTargetWidth
)
maxTargetWidth
=
targetWidth
;
}
var
resolvedWidth
=
Mathf
.
Ceil
(
maxTargetWidth
);
foreach
(
var
button
in
buttons
)
{
if
(
button
==
null
)
continue
;
var
buttonRect
=
button
.
transform
as
RectTransform
;
if
(
buttonRect
==
null
)
continue
;
if
(
Mathf
.
Abs
(
buttonRect
.
sizeDelta
.
x
-
resolvedWidth
)
<
0.01f
)
continue
;
buttonRect
.
sizeDelta
=
new
Vector2
(
resolvedWidth
,
buttonRect
.
sizeDelta
.
y
);
}
}
protected
override
void
HoverOff
(
bool
force
=
false
)
{
base
.
HoverOff
();
...
...
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionToggle_Deck.cs
View file @
2a369a96
...
...
@@ -99,17 +99,31 @@ namespace MDPro3.UI
if
(
index
==
0
)
return
;
await
UniTask
.
WaitWhile
(()
=>
Program
.
instance
.
deckSelector
.
inTransition
);
try
{
await
UniTask
.
WaitWhile
(
()
=>
Program
.
instance
.
deckSelector
.
inTransition
,
cancellationToken
:
destroyCancellationToken
);
for
(
int
i
=
0
;
i
<
transform
.
GetSiblingIndex
();
i
++)
await
UniTask
.
Yield
(
cancellationToken
:
destroyCancellationToken
);
int
siblingIndex
=
transform
.
GetSiblingIndex
();
for
(
int
i
=
0
;
i
<
siblingIndex
;
i
++)
await
UniTask
.
Yield
(
cancellationToken
:
destroyCancellationToken
);
if
(
gameObject
==
null
)
return
;
if
(
this
==
null
||
gameObject
==
null
)
return
;
var
sprite
=
await
Program
.
items
.
LoadDeckCaseIconAsync
(
deckCase
,
"_L_SD"
);
if
(
this
==
null
||
gameObject
==
null
||
sprite
==
null
)
return
;
var
sprite
=
await
Program
.
items
.
LoadDeckCaseIconAsync
(
deckCase
,
"_L_SD"
);
if
(
sprite
!=
null
)
Manager
.
GetElement
<
Image
>(
"DeckImage"
).
sprite
=
sprite
;
}
catch
(
System
.
OperationCanceledException
)
{
}
catch
(
MissingReferenceException
)
{
}
}
protected
override
async
UniTask
RefreshAsync
()
...
...
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionToggle_DeckOnline.cs
View file @
2a369a96
...
...
@@ -87,13 +87,31 @@ namespace MDPro3.UI
protected
override
async
UniTask
RefreshDeckCaseAsync
()
{
await
UniTask
.
WaitWhile
(()
=>
Program
.
instance
.
deckSelector
.
inTransition
);
for
(
int
i
=
0
;
i
<
transform
.
GetSiblingIndex
();
i
++)
await
UniTask
.
Yield
();
try
{
await
UniTask
.
WaitWhile
(
()
=>
Program
.
instance
.
deckSelector
.
inTransition
,
cancellationToken
:
destroyCancellationToken
);
int
siblingIndex
=
transform
.
GetSiblingIndex
();
for
(
int
i
=
0
;
i
<
siblingIndex
;
i
++)
await
UniTask
.
Yield
(
cancellationToken
:
destroyCancellationToken
);
if
(
this
==
null
||
gameObject
==
null
)
return
;
var
sprite
=
await
Program
.
items
.
LoadDeckCaseIconAsync
(
deckCase
,
"_L_SD"
);
if
(
this
==
null
||
gameObject
==
null
||
sprite
==
null
)
return
;
var
sprite
=
await
Program
.
items
.
LoadDeckCaseIconAsync
(
deckCase
,
"_L_SD"
);
if
(
sprite
!=
null
)
Manager
.
GetElement
<
Image
>(
"DeckImage"
).
sprite
=
sprite
;
}
catch
(
System
.
OperationCanceledException
)
{
}
catch
(
MissingReferenceException
)
{
}
}
protected
override
void
OnClick
()
...
...
Assets/Scripts/MDPro3/UI/UIWidget/Deck/DeckView.cs
View file @
2a369a96
...
...
@@ -783,7 +783,8 @@ namespace MDPro3.UI
if
(
condition
==
Condition
.
Editable
&&
!
deckLoaded
)
return
false
;
Deck
=
FromObjectDeckToCodedDeck
();
DeckFileSave
();
if
(!
DeckFileSave
())
return
false
;
SetCondition
(
Condition
.
Editable
);
return
true
;
}
...
...
@@ -1293,29 +1294,80 @@ namespace MDPro3.UI
return
DeckLocation
.
All
;
}
protected
void
DeckFileSave
()
protected
bool
DeckFileSave
()
{
try
{
var
deckName
=
GetDeckName
();
// TODO: 检查违法字符。
var
deckName
=
MDPro3
.
Duel
.
YGOSharp
.
Deck
.
NormalizeDeckFileName
(
GetDeckName
());
if
(!
MDPro3
.
Duel
.
YGOSharp
.
Deck
.
IsValidDeckFileName
(
deckName
))
throw
new
InvalidOperationException
(
$"Invalid deck name: \"
{
GetDeckName
()}
\""
);
Deck
.
type
=
deckType
;
Deck
.
Save
(
deckName
,
DateTime
.
UtcNow
);
if
(
deckName
!=
deckFileName
)
File
.
Delete
(
Program
.
PATH_DECK
+
this
.
deckNameWithType
+
Program
.
EXPANSION_YDK
);
var
oldDeckPath
=
GetDeckFilePath
(
deckFileName
);
if
(!
Deck
.
Save
(
deckName
,
DateTime
.
UtcNow
))
throw
new
IOException
(
$"Failed to save deck \"
{
deckName
}
\"."
);
var
newDeckPath
=
GetDeckFilePath
(
deckName
);
if
(
IsSameDeckPath
(
oldDeckPath
,
newDeckPath
))
ApplyCaseOnlyRename
(
oldDeckPath
,
newDeckPath
);
else
if
(
File
.
Exists
(
oldDeckPath
))
File
.
Delete
(
oldDeckPath
);
deckFileName
=
deckName
;
deckNameWithType
=
deckType
==
string
.
Empty
?
string
.
Empty
:
$"/
{
deckType
}
"
+
deckName
;
deckNameWithType
=
deckType
==
string
.
Empty
?
deckName
:
$"
{
deckType
}
/
{
deckName
}
"
;
InputDeckName
.
text
=
deckFileName
;
TextDeckName
.
text
=
deckFileName
;
MessageManager
.
Toast
(
InterString
.
Get
(
"本地卡组「[?]」已保存。"
,
deckName
));
Config
.
SetConfigDeck
(
deckName
,
true
);
SetDirty
(
false
);
return
true
;
}
catch
(
Exception
e
)
{
MessageManager
.
Toast
(
InterString
.
Get
(
"保存失败!"
));
MessageManager
.
Cast
(
e
.
Message
);
return
false
;
}
}
private
string
GetDeckFilePath
(
string
deckName
)
{
return
Program
.
PATH_DECK
+
(
deckType
==
string
.
Empty
?
string
.
Empty
:
$"
{
deckType
}
/"
)
+
deckName
+
Program
.
EXPANSION_YDK
;
}
private
static
bool
IsSameDeckPath
(
string
leftPath
,
string
rightPath
)
{
var
fullLeftPath
=
Path
.
GetFullPath
(
leftPath
);
var
fullRightPath
=
Path
.
GetFullPath
(
rightPath
);
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
return
string
.
Equals
(
fullLeftPath
,
fullRightPath
,
StringComparison
.
OrdinalIgnoreCase
);
#else
return
string
.
Equals
(
fullLeftPath
,
fullRightPath
,
StringComparison
.
Ordinal
);
#endif
}
private
static
void
ApplyCaseOnlyRename
(
string
oldPath
,
string
newPath
)
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
var
fullOldPath
=
Path
.
GetFullPath
(
oldPath
);
var
fullNewPath
=
Path
.
GetFullPath
(
newPath
);
if
(
string
.
Equals
(
fullOldPath
,
fullNewPath
,
StringComparison
.
Ordinal
))
return
;
if
(!
string
.
Equals
(
fullOldPath
,
fullNewPath
,
StringComparison
.
OrdinalIgnoreCase
))
return
;
if
(!
File
.
Exists
(
fullOldPath
))
return
;
var
directory
=
Path
.
GetDirectoryName
(
fullOldPath
);
var
tempPath
=
Path
.
Combine
(
directory
??
Program
.
PATH_DECK
,
$"__mdpro3_casefix_
{
Guid
.
NewGuid
():
N
}
.tmp"
);
File
.
Move
(
fullOldPath
,
tempPath
);
File
.
Move
(
tempPath
,
fullNewPath
);
#endif
}
#
region
Pickup
private
void
PrePick
()
...
...
Assets/Scripts/MDPro3/UI/UIWidget/SidePanel/ChatPanel.cs
View file @
2a369a96
...
...
@@ -311,6 +311,11 @@ namespace MDPro3.UI
public
static
PlayerPosition
GetPlayerPosition
(
int
player
)
{
player
=
GetRoomPlayerIndex
(
player
);
bool
spectatorInRoomLobby
=
RoomServant
.
SelfType
==
7
&&
Program
.
instance
.
room
!=
null
&&
Program
.
instance
.
room
.
showing
&&
!
Program
.
instance
.
ocgcore
.
showing
;
PlayerPosition
position
;
if
(
player
<
4
)
{
...
...
@@ -326,9 +331,9 @@ namespace MDPro3.UI
else
{
if
(
player
==
0
)
position
=
PlayerPosition
.
WatchMe
;
position
=
spectatorInRoomLobby
?
PlayerPosition
.
Me
:
PlayerPosition
.
WatchMe
;
else
position
=
PlayerPosition
.
WatchOp
;
position
=
spectatorInRoomLobby
?
PlayerPosition
.
Op
:
PlayerPosition
.
WatchOp
;
}
}
else
...
...
@@ -350,13 +355,13 @@ namespace MDPro3.UI
else
{
if
(
player
==
0
)
position
=
PlayerPosition
.
WatchMe
;
position
=
spectatorInRoomLobby
?
PlayerPosition
.
Me
:
PlayerPosition
.
WatchMe
;
else
if
(
player
==
1
)
position
=
PlayerPosition
.
WatchMyTag
;
position
=
spectatorInRoomLobby
?
PlayerPosition
.
MyTag
:
PlayerPosition
.
WatchMyTag
;
else
if
(
player
==
2
)
position
=
PlayerPosition
.
WatchOp
;
position
=
spectatorInRoomLobby
?
PlayerPosition
.
Op
:
PlayerPosition
.
WatchOp
;
else
position
=
PlayerPosition
.
WatchOpTag
;
position
=
spectatorInRoomLobby
?
PlayerPosition
.
OpTag
:
PlayerPosition
.
WatchOpTag
;
}
}
}
...
...
Assets/Scripts/MDPro3/Utility/CardImageLoader.cs
View file @
2a369a96
...
...
@@ -21,6 +21,7 @@ namespace MDPro3.Utility
private
static
readonly
ConcurrentDictionary
<
int
,
CacheEntry
>
cachedCards
=
new
();
private
static
readonly
ConcurrentDictionary
<
int
,
Texture2D
>
cachedCardNames
=
new
();
private
static
readonly
ConcurrentDictionary
<
int
,
Texture
>
cachedVideoCards
=
new
();
private
static
readonly
ConcurrentDictionary
<
int
,
byte
>
knownOverFrameArts
=
new
();
private
static
readonly
ConcurrentDictionary
<
int
,
SemaphoreSlim
>
artLoadingLocks
=
new
();
private
static
readonly
ConcurrentDictionary
<
int
,
SemaphoreSlim
>
cardLoadingLocks
=
new
();
...
...
@@ -86,7 +87,7 @@ namespace MDPro3.Utility
Interlocked
.
Increment
(
ref
entry
.
ReferenceCount
);
entry
.
IsPersistent
|=
persistent
;
}
else
else
if
(!
HasOverFrameArtFile
(
code
))
Debug
.
LogError
(
$"Art texture is null for code
{
code
}
"
);
return
entry
.
Texture
;
...
...
@@ -111,7 +112,7 @@ namespace MDPro3.Utility
cachedArts
.
TryRemove
(
code
,
out
_
);
throw
ex
;
}
if
(
newEntry
.
Texture
==
null
)
if
(
newEntry
.
Texture
==
null
&&
!
HasOverFrameArtFile
(
code
)
)
Debug
.
LogError
(
$"
{
code
}
art is null"
);
newEntry
.
LoadingTask
=
null
;
...
...
@@ -255,6 +256,7 @@ namespace MDPro3.Utility
cachedCardNames
.
Clear
();
ClearArtVideos
();
knownOverFrameArts
.
Clear
();
}
#
endregion
...
...
@@ -290,7 +292,7 @@ namespace MDPro3.Utility
}
if
(
art
==
null
)
{
lastCardFoundArt
=
false
;
lastCardFoundArt
=
HasOverFrameArtFile
(
code
)
;
return
null
;
}
...
...
@@ -354,7 +356,8 @@ namespace MDPro3.Utility
if
(
art
==
null
)
{
Debug
.
LogError
(
$"Get null from ArtLoad for Card
{
data
.
Id
}
:"
);
if
(!
HasOverFrameArtFile
(
data
.
Id
))
Debug
.
LogError
(
$"Get null from ArtLoad for Card
{
data
.
Id
}
:"
);
art
=
TextureManager
.
container
.
unknownArt
.
texture
;
}
if
(!
Program
.
instance
.
cardRenderer
.
RenderCard
(
code
,
art
))
...
...
@@ -397,7 +400,8 @@ namespace MDPro3.Utility
if
(
art
==
null
)
{
Debug
.
LogError
(
$"Get null from ArtLoad for Card
{
data
.
Id
}
:"
);
if
(!
HasOverFrameArtFile
(
data
.
Id
))
Debug
.
LogError
(
$"Get null from ArtLoad for Card
{
data
.
Id
}
:"
);
art
=
TextureManager
.
container
.
unknownArt
.
texture
;
}
...
...
@@ -559,6 +563,30 @@ namespace MDPro3.Utility
#
region
Art
File
List
Cache
private
static
bool
HasOverFrameArtFile
(
int
code
)
{
if
(
knownOverFrameArts
.
ContainsKey
(
code
))
return
true
;
var
fileName
=
code
+
Program
.
EXPANSION_PNG
;
#if !UNITY_EDITOR && (UNITY_ANDROID || UNITY_IOS)
var
overFramePath
=
Path
.
Combine
(
Application
.
persistentDataPath
,
"Picture"
,
"OverFrame"
,
fileName
);
var
overframePath
=
Path
.
Combine
(
Application
.
persistentDataPath
,
"Picture"
,
"Overframe"
,
fileName
);
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_STANDALONE_LINUX
var
overFramePath
=
Path
.
Combine
(
Environment
.
CurrentDirectory
,
"Picture"
,
"OverFrame"
,
fileName
);
var
overframePath
=
Path
.
Combine
(
Environment
.
CurrentDirectory
,
"Picture"
,
"Overframe"
,
fileName
);
#else
var
overFramePath
=
Path
.
Combine
(
Environment
.
CurrentDirectory
,
"Picture"
,
"OverFrame"
,
fileName
);
var
overframePath
=
Path
.
Combine
(
Environment
.
CurrentDirectory
,
"Picture"
,
"Overframe"
,
fileName
);
#endif
var
exists
=
File
.
Exists
(
overFramePath
)
||
File
.
Exists
(
overframePath
);
if
(
exists
)
knownOverFrameArts
.
TryAdd
(
code
,
0
);
return
exists
;
}
private
static
readonly
List
<
int
>
artFileList
=
new
();
private
static
readonly
Dictionary
<
int
,
string
>
artAltFileList
=
new
();
private
static
bool
artFileListInitialized
;
...
...
@@ -688,4 +716,4 @@ namespace MDPro3.Utility
#
endregion
}
}
\ No newline at end of file
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment