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
d20b6d9e
Commit
d20b6d9e
authored
Mar 13, 2026
by
Senator John
💬
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'patch' into 'master'
Patch See merge request
!32
parents
b3287b74
eea6cdcb
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
2896 additions
and
246 deletions
+2896
-246
Assets/Prefabs/ScriptableObjects/Items.asset
Assets/Prefabs/ScriptableObjects/Items.asset
+9
-2
Assets/Scripts/MDPro3/Duel/BG/DuelBGManager.cs
Assets/Scripts/MDPro3/Duel/BG/DuelBGManager.cs
+1434
-182
Assets/Scripts/MDPro3/Duel/BG/PremiumMateRules.cs
Assets/Scripts/MDPro3/Duel/BG/PremiumMateRules.cs
+109
-0
Assets/Scripts/MDPro3/Duel/BG/PremiumMateRules.cs.meta
Assets/Scripts/MDPro3/Duel/BG/PremiumMateRules.cs.meta
+2
-0
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs
+240
-0
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs.meta
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs.meta
+2
-0
Assets/Scripts/MDPro3/Duel/Message/DuelMessage.cs
Assets/Scripts/MDPro3/Duel/Message/DuelMessage.cs
+26
-0
Assets/Scripts/MDPro3/Game/Mate.cs
Assets/Scripts/MDPro3/Game/Mate.cs
+782
-31
Assets/Scripts/MDPro3/Game/Tools.cs
Assets/Scripts/MDPro3/Game/Tools.cs
+19
-3
Assets/Scripts/MDPro3/Helper/ABLoader.cs
Assets/Scripts/MDPro3/Helper/ABLoader.cs
+24
-11
Assets/Scripts/MDPro3/ScriptableObjects/Items.cs
Assets/Scripts/MDPro3/ScriptableObjects/Items.cs
+25
-4
Assets/Scripts/MDPro3/UI/Function/EventSEPlayer.cs
Assets/Scripts/MDPro3/UI/Function/EventSEPlayer.cs
+23
-0
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionToggle_AppearanceItem.cs
...Pro3/UI/SelectionButton/SelectionToggle_AppearanceItem.cs
+163
-2
Assets/Scripts/MDPro3/UI/ServantUI/AppearanceUI.cs
Assets/Scripts/MDPro3/UI/ServantUI/AppearanceUI.cs
+38
-11
No files found.
Assets/Prefabs/ScriptableObjects/Items.asset
View file @
d20b6d9e
...
...
@@ -954,7 +954,7 @@ MonoBehaviour:
-
id
:
1003003
m_name
:
m_description
:
path
:
Mate/M1367
0
_Model
path
:
Mate/M1367
1
_Model
secondFace
:
1
diy
:
0
notReady
:
0
...
...
@@ -965,6 +965,13 @@ MonoBehaviour:
secondFace
:
1
diy
:
0
notReady
:
0
-
id
:
1003203
m_name
:
m_description
:
path
:
Mate/M13670_Model
secondFace
:
1
diy
:
0
notReady
:
0
-
id
:
1003004
m_name
:
m_description
:
...
...
@@ -8133,4 +8140,4 @@ MonoBehaviour:
path
:
Wallpaper/88177324
secondFace
:
0
diy
:
1
notReady
:
0
\ No newline at end of file
notReady
:
0
Assets/Scripts/MDPro3/Duel/BG/DuelBGManager.cs
View file @
d20b6d9e
...
...
@@ -10,6 +10,7 @@ using System.Collections.Generic;
using
System.Drawing.Text
;
using
System.IO
;
using
System.Linq
;
using
System.Reflection
;
using
System.Threading
;
using
UnityEngine
;
using
UnityEngine.AddressableAssets
;
...
...
@@ -26,7 +27,6 @@ namespace MDPro3.Duel
{
public
class
DuelBGManager
{
#
region
Parameters
private
OcgCore
Core
=>
Program
.
instance
.
ocgcore
;
...
...
@@ -34,6 +34,8 @@ namespace MDPro3.Duel
private
bool
mate0Random
=
true
;
private
bool
mate1Random
=
true
;
private
Tween
mate0RandomCooldownTween
;
private
Tween
mate1RandomCooldownTween
;
private
int
bgPhase0
=
0
;
private
int
bgPhase1
=
0
;
private
bool
backgroundFieldInitialize
=
false
;
...
...
@@ -70,6 +72,8 @@ namespace MDPro3.Duel
private
BgEffectManager
stand1Manager
;
private
Mate
mate0
;
private
Mate
mate1
;
private
PremiumMateState
premiumMate0
;
private
PremiumMateState
premiumMate1
;
private
GameObject
phaseButton
;
private
TimerHandler
timerHandler
;
...
...
@@ -83,11 +87,43 @@ namespace MDPro3.Duel
#
endregion
private
sealed
class
PremiumMateState
{
public
int
Side
;
public
PremiumMateRule
Rule
;
public
PremiumMateSwapEffect
SwapEffect
;
public
Transform
Anchor
;
public
readonly
Dictionary
<
int
,
Mate
>
Forms
=
new
();
public
int
ActiveMateId
;
public
bool
IsTransitioning
;
public
int
QueuedMateId
;
public
bool
IsPermanentSub
;
public
bool
InBattlePhaseSub
;
public
bool
PendingDirectAttack
;
public
IReadOnlyList
<
string
>
OverrideTriggerPriority
;
public
int
PendingTurnSwapToken
;
public
int
RayeEngageMateId
;
public
int
RayeBattleMateId
;
}
private
readonly
struct
SwapEffectLabelPlaybackResult
{
public
bool
Consumed
{
get
;
}
public
SwapEffectLabelPlaybackResult
(
bool
consumed
)
{
Consumed
=
consumed
;
}
public
static
SwapEffectLabelPlaybackResult
None
=>
new
(
false
);
}
#
region
Public
public
async
UniTask
LoadAssetsAsync
()
{
loaded
=
false
;
ResetPremiumMateStates
();
deck
=
null
;
var
deckName
=
Config
.
GetConfigDeckName
();
...
...
@@ -266,30 +302,12 @@ namespace MDPro3.Duel
int
mateCode
=
int
.
Parse
(
mateConfig
);
if
(
deck
!=
null
&&
!
overrideDeckAppearance
)
mateCode
=
deck
.
Mate
;
if
(
mateCode
!=
Items
.
CODE_NONE
)
{
var
mate
=
await
ABLoader
.
LoadMateAsync
(
mateCode
);
if
(
mate
!=
null
)
{
mate0
=
mate
;
mate0
.
parent
=
pos_Avatar_near
;
mate0
.
gameObject
.
SetActive
(
false
);
}
}
await
LoadMateForSideAsync
(
0
,
mateCode
,
pos_Avatar_near
);
mateConfig
=
Config
.
Get
(
condition
.
ToString
()
+
"Mate1"
,
Program
.
items
.
mates
[
0
].
id
.
ToString
());
if
(
hasSide1Appearance
)
mateConfig
=
side1Appearance
.
Mate
.
ToString
();
if
(
mateConfig
!=
Items
.
CODE_NONE
.
ToString
())
{
var
mate
=
await
ABLoader
.
LoadMateAsync
(
int
.
Parse
(
mateConfig
));
if
(
mate
!=
null
)
{
mate1
=
mate
;
mate1
.
parent
=
pos_Avatar_far
;
mate1
.
gameObject
.
SetActive
(
false
);
}
}
await
LoadMateForSideAsync
(
1
,
int
.
Parse
(
mateConfig
),
pos_Avatar_far
);
#
endregion
...
...
@@ -442,196 +460,1367 @@ namespace MDPro3.Duel
};
CreatePlaceSelector
(
gps
);
}
for
(
uint
s
=
0
;
s
<
6
;
s
++)
for
(
uint
s
=
0
;
s
<
6
;
s
++)
{
gps
=
new
()
{
controller
=
c
,
location
=
(
uint
)
CardLocation
.
SpellZone
,
sequence
=
s
};
CreatePlaceSelector
(
gps
);
}
}
#
endregion
#
region
Quit
Load
await
processor
.
PreloadPlayerNames
();
if
(
Core
.
NeedVoice
())
{
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
alpha
=
1
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
blocksRaycasts
=
true
;
}
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
false
);
UIManager
.
ShowFPSLeft
();
UIManager
.
HideBlackBack
(
0f
);
UIManager
.
UIBlackOut
(
Core
.
TransitionTime
);
await
UniTask
.
WaitForSeconds
(
Core
.
TransitionTime
);
backgroundFieldInitialize
=
false
;
BackgroundFieldInitialize
();
if
(
condition
==
Condition
.
Duel
&&
Config
.
GetBool
(
"DuelAutoAcc"
,
false
)
||
condition
==
Condition
.
Watch
&&
Config
.
GetBool
(
"WatchAutoAcc"
,
false
)
||
condition
==
Condition
.
Replay
&&
Config
.
GetBool
(
"ReplayAutoAcc"
,
false
))
Core
.
GetUI
<
OcgCoreUI
>().
OnAcc
();
#
endregion
loaded
=
true
;
}
public
async
UniTask
ExitDuelAsync
()
{
ClearResponse
();
CameraManager
.
BlackOut
(
0f
,
0.3f
);
UIManager
.
UIBlackIn
(
Core
.
TransitionTime
);
Core
.
GetUI
<
OcgCoreUI
>().
CloseHint
();
HideAttackLine
();
HideDuelFinalBlowText
();
await
UniTask
.
WaitForSeconds
(
Core
.
TransitionTime
);
Core
.
servantUI
.
ShutDown
();
NoMoreWait
=
true
;
packages
.
Clear
();
allPackages
.
Clear
();
AudioManager
.
ResetSESource
();
mycardDuel
=
false
;
Core
.
CloseCharaFace
();
Dispose
();
foreach
(
var
card
in
cards
)
card
.
Dispose
();
cards
.
Clear
();
pause
=
false
;
nextMoveAction
=
null
;
Core
.
cachedCharaFaces
.
Clear
();
CameraManager
.
ShiftTo2D
();
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
false
);
Core
.
GetUI
<
OcgCoreUI
>().
DuelLog
.
ClearLog
();
Program
.
instance
.
ui_
.
chatPanel
.
Hide
();
await
UniTask
.
WaitForSeconds
(
0.3f
);
UIManager
.
UIBlackOut
(
Core
.
TransitionTime
);
await
UniTask
.
WaitForSeconds
(
Core
.
TransitionTime
);
UIManager
.
ShowFPSRight
();
AudioManager
.
PlayBGM
(
AudioManager
.
BGM_MENU_MAIN
);
}
private
void
InitializeDeckModel
(
ElementObjectManager
deckManager
,
int
player
,
CardLocation
location
)
{
deckManager
.
transform
.
SetParent
(
player
==
0
?
field0Manager
.
transform
:
field1Manager
.
transform
,
false
);
deckManager
.
transform
.
localPosition
=
_positionMap
[(
player
,
location
)];
deckManager
.
transform
.
localEulerAngles
=
_angleMap
[(
player
,
location
)];
allGameObjects
.
Add
(
deckManager
.
gameObject
);
var
mat
=
player
==
0
?
myProtector
:
opProtector
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"DummyDeck/DummyCardModel_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel01_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel02_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel03_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel04_back"
).
material
=
mat
;
deckManager
.
gameObject
.
SetActive
(
false
);
}
private
static
readonly
Dictionary
<(
int
,
CardLocation
),
Vector3
>
_positionMap
=
new
()
{
[(
0
,
CardLocation
.
Deck
)]
=
new
(
26.6f
,
1.5f
,
-
23.5f
),
[(
0
,
CardLocation
.
Extra
)]
=
new
(-
26.6f
,
1.5f
,
-
23.5f
),
[(
1
,
CardLocation
.
Deck
)]
=
new
(-
26.6f
,
1.5f
,
23.5f
),
[(
1
,
CardLocation
.
Extra
)]
=
new
(
26.6f
,
1.5f
,
23.5f
)
};
private
static
readonly
Dictionary
<(
int
,
CardLocation
),
Vector3
>
_angleMap
=
new
()
{
[(
0
,
CardLocation
.
Deck
)]
=
new
(
0f
,
-
20f
,
0f
),
[(
0
,
CardLocation
.
Extra
)]
=
new
(
0f
,
20f
,
0f
),
[(
1
,
CardLocation
.
Deck
)]
=
new
(
0f
,
160f
,
0f
),
[(
1
,
CardLocation
.
Extra
)]
=
new
(
0f
,
-
160f
,
0f
)
};
public
async
UniTask
ShowDecksAsync
()
{
if
(
myDeck
==
null
||
myExtra
==
null
||
opDeck
==
null
||
opExtra
==
null
)
return
;
await
ShowAllDeckModelsAsync
();
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
alpha
=
1
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
blocksRaycasts
=
true
;
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
true
);
AudioManager
.
PlayBgmNormal
(
Config
.
GetBool
(
"BGMbyMySide"
,
true
)
?
field0Manager
.
name
:
field1Manager
.
name
);
}
public
async
UniTask
ShowDecksWithDuelStartTextAsync
()
{
if
(
myDeck
==
null
||
myExtra
==
null
||
opDeck
==
null
||
opExtra
==
null
)
return
;
await
ShowAllDeckModelsAsync
();
var
effect
=
ABLoader
.
LoadMasterDuelGameObject
(
"DuelTextStart"
);
var
director
=
effect
.
GetComponent
<
PlayableDirector
>();
await
director
.
WaitAsync
();
UnityEngine
.
Object
.
Destroy
(
effect
);
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
alpha
=
1
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
blocksRaycasts
=
true
;
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
true
);
AudioManager
.
PlayBgmNormal
(
Config
.
GetBool
(
"BGMbyMySide"
,
true
)
?
field0Manager
.
name
:
field1Manager
.
name
);
}
public
void
BackgroundFieldInitialize
()
{
if
(
field0Manager
==
null
||
field1Manager
==
null
)
return
;
if
(
bgPhase0
==
1
&&
bgPhase1
==
1
)
return
;
if
(
backgroundFieldInitialize
)
{
field0Manager
.
gameObject
.
SetActive
(
false
);
field1Manager
.
gameObject
.
SetActive
(
false
);
field0Manager
.
gameObject
.
SetActive
(
true
);
field1Manager
.
gameObject
.
SetActive
(
true
);
}
field0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
grave0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
bgPhase0
=
1
;
field1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
grave1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
bgPhase1
=
1
;
InitializeMateEntry
(
0
);
InitializeMateEntry
(
1
);
if
(
timerHandler
!=
null
)
timerHandler
.
DuelStart
();
RestartMateRandomCooldown
(
0
);
RestartMateRandomCooldown
(
1
);
backgroundFieldInitialize
=
true
;
}
public
void
OnNewTurn
(
bool
myTurn
,
int
turn
)
{
ApplyPremiumNewTurn
(
GetPremiumMateState
(
0
),
myTurn
,
turn
);
ApplyPremiumNewTurn
(
GetPremiumMateState
(
1
),
!
myTurn
,
turn
);
}
public
void
OnNewPhase
(
int
turnPlayer
,
DuelPhase
phase
)
{
ApplyPremiumPhase
(
GetPremiumMateState
(
0
),
turnPlayer
==
0
,
phase
);
ApplyPremiumPhase
(
GetPremiumMateState
(
1
),
turnPlayer
==
1
,
phase
);
}
public
void
OnSpecialSummonFromExtra
(
int
ownerPlayer
)
{
var
state
=
GetPremiumMateState
(
ownerPlayer
);
if
(
state
==
null
)
return
;
if
(
state
.
Rule
.
Behavior
==
PremiumMateBehavior
.
GaiaExtraDeckPermanent
||
state
.
Rule
.
Behavior
==
PremiumMateBehavior
.
FiendsmithExtraDeckOrEquipPermanent
)
{
if
(
state
.
IsPermanentSub
)
return
;
state
.
IsPermanentSub
=
true
;
RequestPremiumMateForm
(
state
,
state
.
Rule
.
SubId
);
}
}
public
void
OnEquipApplied
(
int
ownerPlayer
)
{
var
state
=
GetPremiumMateState
(
ownerPlayer
);
if
(
state
==
null
)
return
;
if
(
state
.
Rule
.
Behavior
!=
PremiumMateBehavior
.
FiendsmithExtraDeckOrEquipPermanent
)
return
;
if
(
state
.
IsPermanentSub
)
return
;
state
.
IsPermanentSub
=
true
;
RequestPremiumMateForm
(
state
,
state
.
Rule
.
SubId
);
}
public
void
OnLifePointsChanged
(
int
player
,
int
lifePoint
)
{
var
state
=
GetPremiumMateState
(
player
);
if
(
state
==
null
)
return
;
if
(
state
.
Rule
.
Behavior
!=
PremiumMateBehavior
.
ShuraigLpThreshold
)
return
;
if
(
state
.
IsPermanentSub
)
return
;
if
(
lifePoint
>
state
.
Rule
.
LpThreshold
)
return
;
state
.
IsPermanentSub
=
true
;
RequestPremiumMateForm
(
state
,
state
.
Rule
.
SubId
);
}
public
void
OnDirectAttack
(
int
attackerPlayer
)
{
var
state
=
GetPremiumMateState
(
attackerPlayer
);
if
(
state
==
null
)
return
;
if
(
state
.
Rule
.
Behavior
!=
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
)
return
;
var
battleMateId
=
GetRayeBattleMateId
(
state
);
if
(
state
.
ActiveMateId
!=
battleMateId
)
return
;
state
.
PendingDirectAttack
=
true
;
}
public
void
OnPlayerDamaged
(
int
defenderPlayer
,
int
amount
)
{
if
(
amount
<=
0
)
return
;
var
state0
=
GetPremiumMateState
(
0
);
var
state1
=
GetPremiumMateState
(
1
);
TryResolveRayeDirectAttack
(
state0
,
defenderPlayer
);
TryResolveRayeDirectAttack
(
state1
,
defenderPlayer
);
}
private
void
TryResolveRayeDirectAttack
(
PremiumMateState
state
,
int
defenderPlayer
)
{
if
(
state
==
null
)
return
;
if
(
state
.
Rule
.
Behavior
!=
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
)
return
;
if
(!
state
.
PendingDirectAttack
)
return
;
if
(
state
.
Side
==
defenderPlayer
)
return
;
state
.
PendingDirectAttack
=
false
;
state
.
InBattlePhaseSub
=
false
;
RequestPremiumMateForm
(
state
,
GetRayeEngageMateId
(
state
));
}
private
void
ApplyPremiumNewTurn
(
PremiumMateState
state
,
bool
ownerTurn
,
int
turn
)
{
if
(
state
==
null
)
return
;
_
=
ownerTurn
;
state
.
PendingDirectAttack
=
false
;
switch
(
state
.
Rule
.
Behavior
)
{
case
PremiumMateBehavior
.
LaundryBattlePhaseRoundTrip
:
state
.
InBattlePhaseSub
=
false
;
if
(!
state
.
IsPermanentSub
)
RequestPremiumMateForm
(
state
,
state
.
Rule
.
BaseId
);
break
;
case
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
:
state
.
InBattlePhaseSub
=
false
;
break
;
case
PremiumMateBehavior
.
IpSpTurnParity
:
var
target
=
(
turn
%
2
!=
0
)
?
state
.
Rule
.
BaseId
:
state
.
Rule
.
SubId
;
const
float
ipSpPreSwapDelaySeconds
=
0.80f
;
var
turnSwapToken
=
++
state
.
PendingTurnSwapToken
;
_
=
RequestPremiumMateFormDelayedAsync
(
state
,
target
,
ipSpPreSwapDelaySeconds
,
turnSwapToken
);
break
;
}
}
private
async
UniTask
RequestPremiumMateFormDelayedAsync
(
PremiumMateState
state
,
int
targetMateId
,
float
delaySeconds
,
int
token
)
{
if
(
state
==
null
)
return
;
if
(
delaySeconds
>
0f
)
await
UniTask
.
WaitForSeconds
(
delaySeconds
);
if
(
state
.
PendingTurnSwapToken
!=
token
)
return
;
if
(
GetPremiumMateState
(
state
.
Side
)
!=
state
)
return
;
RequestPremiumMateForm
(
state
,
targetMateId
);
}
private
void
ApplyPremiumPhase
(
PremiumMateState
state
,
bool
ownerTurn
,
DuelPhase
phase
)
{
if
(
state
==
null
)
return
;
switch
(
state
.
Rule
.
Behavior
)
{
case
PremiumMateBehavior
.
LaundryBattlePhaseRoundTrip
:
if
(
ownerTurn
&&
phase
==
DuelPhase
.
BattleStart
)
{
state
.
InBattlePhaseSub
=
true
;
RequestPremiumMateForm
(
state
,
state
.
Rule
.
SubId
);
}
else
if
((!
ownerTurn
||
phase
==
DuelPhase
.
Main2
||
phase
==
DuelPhase
.
End
)
&&
state
.
InBattlePhaseSub
)
{
state
.
InBattlePhaseSub
=
false
;
RequestPremiumMateForm
(
state
,
state
.
Rule
.
BaseId
);
}
break
;
case
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
:
var
battleMateId
=
GetRayeBattleMateId
(
state
);
if
(
ownerTurn
&&
phase
==
DuelPhase
.
BattleStart
)
{
state
.
PendingDirectAttack
=
false
;
state
.
InBattlePhaseSub
=
true
;
if
(
state
.
ActiveMateId
!=
battleMateId
)
RequestPremiumMateForm
(
state
,
battleMateId
);
}
else
if
((
phase
==
DuelPhase
.
Main2
||
phase
==
DuelPhase
.
End
)
&&
state
.
InBattlePhaseSub
)
{
state
.
PendingDirectAttack
=
false
;
state
.
InBattlePhaseSub
=
false
;
}
break
;
}
}
private
void
InitializeMateEntry
(
int
side
)
{
var
state
=
GetPremiumMateState
(
side
);
if
(
state
!=
null
)
{
foreach
(
var
pair
in
state
.
Forms
)
if
(
pair
.
Value
!=
null
)
pair
.
Value
.
gameObject
.
SetActive
(
false
);
if
(!
state
.
Forms
.
TryGetValue
(
state
.
ActiveMateId
,
out
var
activeMate
)
||
activeMate
==
null
)
activeMate
=
state
.
Forms
.
Values
.
FirstOrDefault
(
m
=>
m
!=
null
);
if
(
activeMate
==
null
)
{
SetMateForSide
(
side
,
null
);
return
;
}
state
.
ActiveMateId
=
activeMate
.
code
;
ResetMateTransform
(
activeMate
,
state
.
Anchor
);
SetExclusivePremiumMateActiveForm
(
state
,
activeMate
);
activeMate
.
PrepareForPremiumSwapActivation
();
activeMate
.
Play
(
Mate
.
MateAction
.
Entry
);
SetMateForSide
(
side
,
activeMate
);
return
;
}
var
mate
=
GetMateForSide
(
side
);
if
(
mate
!=
null
)
{
mate
.
gameObject
.
SetActive
(
true
);
mate
.
Play
(
Mate
.
MateAction
.
Entry
);
}
}
private
async
UniTask
LoadMateForSideAsync
(
int
side
,
int
mateCode
,
Transform
anchor
)
{
SetPremiumMateState
(
side
,
null
);
SetMateForSide
(
side
,
null
);
if
(
mateCode
==
Items
.
CODE_NONE
)
return
;
var
normalizedMateCode
=
PremiumMateRules
.
GetBaseMateId
(
mateCode
);
if
(
PremiumMateRules
.
TryGetRuleByBaseId
(
normalizedMateCode
,
out
var
rule
))
{
var
formIds
=
new
List
<
int
>
{
rule
.
BaseId
};
foreach
(
var
variantId
in
rule
.
VariantIds
)
if
(!
formIds
.
Contains
(
variantId
))
formIds
.
Add
(
variantId
);
var
loadedForms
=
new
Dictionary
<
int
,
Mate
>();
foreach
(
var
formId
in
formIds
)
{
var
formMate
=
await
ABLoader
.
LoadMateAsync
(
formId
);
if
(
formMate
==
null
)
continue
;
ConfigureLoadedMate
(
formMate
,
anchor
);
loadedForms
[
formId
]
=
formMate
;
}
if
(
loadedForms
.
TryGetValue
(
rule
.
BaseId
,
out
var
baseMate
)
&&
loadedForms
.
TryGetValue
(
rule
.
SubId
,
out
_
))
{
var
state
=
new
PremiumMateState
{
Side
=
side
,
Rule
=
rule
,
SwapEffect
=
PremiumMateSwapEffects
.
GetOrDefault
(
rule
.
BaseId
),
Anchor
=
anchor
,
ActiveMateId
=
rule
.
BaseId
};
foreach
(
var
pair
in
loadedForms
)
state
.
Forms
[
pair
.
Key
]
=
pair
.
Value
;
if
(
rule
.
Behavior
==
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
)
{
// The premium Raye mate's item IDs do not match the visual form order:
// 1003003 = Engage, 1003103 = Kagari, 1003203 = Raye.
// The opening Raye-only startup is disabled; start directly on Engage.
state
.
RayeEngageMateId
=
rule
.
BaseId
;
state
.
RayeBattleMateId
=
loadedForms
.
ContainsKey
(
1003103
)
?
1003103
:
rule
.
SubId
;
state
.
ActiveMateId
=
state
.
RayeEngageMateId
;
}
SetPremiumMateState
(
side
,
state
);
if
(!
loadedForms
.
TryGetValue
(
state
.
ActiveMateId
,
out
var
initialMate
)
||
initialMate
==
null
)
initialMate
=
baseMate
;
SetMateForSide
(
side
,
initialMate
);
return
;
}
var
fallbackMate
=
loadedForms
.
Values
.
FirstOrDefault
();
if
(
fallbackMate
!=
null
)
{
SetMateForSide
(
side
,
fallbackMate
);
return
;
}
}
var
mate
=
await
ABLoader
.
LoadMateAsync
(
normalizedMateCode
);
if
(
mate
==
null
)
return
;
ConfigureLoadedMate
(
mate
,
anchor
);
SetMateForSide
(
side
,
mate
);
}
private
static
void
ConfigureLoadedMate
(
Mate
mate
,
Transform
anchor
)
{
if
(
mate
==
null
)
return
;
ResetMateTransform
(
mate
,
anchor
);
mate
.
gameObject
.
SetActive
(
false
);
}
private
static
void
ResetMateTransform
(
Mate
mate
,
Transform
anchor
)
{
if
(
mate
==
null
||
anchor
==
null
)
return
;
mate
.
parent
=
anchor
;
mate
.
transform
.
SetParent
(
anchor
,
false
);
mate
.
transform
.
localPosition
=
Vector3
.
zero
;
mate
.
transform
.
localRotation
=
Quaternion
.
identity
;
}
private
static
int
GetRayeEngageMateId
(
PremiumMateState
state
)
{
if
(
state
==
null
)
return
0
;
if
(
state
.
RayeEngageMateId
>
0
)
return
state
.
RayeEngageMateId
;
return
state
.
Rule
.
BaseId
;
}
private
static
int
GetRayeBattleMateId
(
PremiumMateState
state
)
{
if
(
state
==
null
)
return
0
;
return
state
.
RayeBattleMateId
>
0
?
state
.
RayeBattleMateId
:
state
.
Rule
.
SubId
;
}
private
static
int
ResolvePremiumMateTargetId
(
PremiumMateState
state
,
int
targetMateId
)
{
if
(
state
==
null
)
return
targetMateId
;
if
(
state
.
Rule
.
Behavior
==
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
&&
targetMateId
==
GetRayeBattleMateId
(
state
)
&&
!
state
.
InBattlePhaseSub
)
{
return
GetRayeEngageMateId
(
state
);
}
return
targetMateId
;
}
private
void
ActivatePremiumMateFormImmediate
(
PremiumMateState
state
,
int
targetMateId
)
{
if
(
state
==
null
)
return
;
if
(!
state
.
Forms
.
TryGetValue
(
targetMateId
,
out
var
targetMate
)
||
targetMate
==
null
)
return
;
ResetMateTransform
(
targetMate
,
state
.
Anchor
);
SetExclusivePremiumMateActiveForm
(
state
,
targetMate
);
targetMate
.
PrepareForPremiumSwapActivation
();
state
.
ActiveMateId
=
targetMateId
;
SetMateForSide
(
state
.
Side
,
targetMate
);
RestartMateRandomCooldown
(
state
.
Side
);
}
private
static
void
SetExclusivePremiumMateActiveForm
(
PremiumMateState
state
,
Mate
activeMate
,
Mate
additionalActiveMate
=
null
)
{
if
(
state
==
null
)
return
;
foreach
(
var
pair
in
state
.
Forms
)
{
var
mate
=
pair
.
Value
;
if
(
mate
==
null
)
continue
;
mate
.
gameObject
.
SetActive
(
mate
==
activeMate
||
mate
==
additionalActiveMate
);
}
}
private
static
bool
ShouldSuppressAmbientPremiumMateActions
(
PremiumMateState
state
)
{
return
state
!=
null
&&
state
.
Rule
.
Behavior
==
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
;
}
private
static
bool
HasNonChangeTrigger
(
IReadOnlyList
<
string
>
triggerPriority
)
{
if
(
triggerPriority
==
null
||
triggerPriority
.
Count
==
0
)
return
false
;
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
trigger
=
triggerPriority
[
i
];
if
(
string
.
IsNullOrEmpty
(
trigger
))
continue
;
if
(!
trigger
.
StartsWith
(
"Change"
,
StringComparison
.
OrdinalIgnoreCase
))
return
true
;
}
return
false
;
}
private
static
bool
IsChangeTrigger
(
string
triggerName
)
{
return
!
string
.
IsNullOrEmpty
(
triggerName
)
&&
triggerName
.
StartsWith
(
"Change"
,
StringComparison
.
OrdinalIgnoreCase
);
}
private
static
bool
HasConfiguredChangeTrigger
(
IReadOnlyList
<
string
>
triggerPriority
)
{
if
(
triggerPriority
==
null
||
triggerPriority
.
Count
==
0
)
return
false
;
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
if
(
IsChangeTrigger
(
triggerPriority
[
i
]))
return
true
;
}
return
false
;
}
private
static
IReadOnlyList
<
string
>
BuildNonChangeFirstPriority
(
IReadOnlyList
<
string
>
triggerPriority
)
{
if
(
triggerPriority
==
null
||
triggerPriority
.
Count
<=
1
)
return
triggerPriority
;
var
hasLeadingChange
=
false
;
var
hasFollowingNonChange
=
false
;
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
trigger
=
triggerPriority
[
i
];
if
(
string
.
IsNullOrEmpty
(
trigger
))
continue
;
if
(
IsChangeTrigger
(
trigger
))
{
hasLeadingChange
=
true
;
continue
;
}
if
(
hasLeadingChange
)
{
hasFollowingNonChange
=
true
;
break
;
}
}
if
(!
hasFollowingNonChange
)
return
triggerPriority
;
var
reordered
=
new
List
<
string
>(
triggerPriority
.
Count
);
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
trigger
=
triggerPriority
[
i
];
if
(
string
.
IsNullOrEmpty
(
trigger
)
||
IsChangeTrigger
(
trigger
))
continue
;
reordered
.
Add
(
trigger
);
}
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
trigger
=
triggerPriority
[
i
];
if
(
string
.
IsNullOrEmpty
(
trigger
)
||
!
IsChangeTrigger
(
trigger
))
continue
;
reordered
.
Add
(
trigger
);
}
return
reordered
.
Count
>
0
?
reordered
:
triggerPriority
;
}
private
static
bool
MateHasAnyChangeTriggerParameter
(
Mate
mate
,
IReadOnlyList
<
string
>
triggerPriority
)
{
if
(
mate
==
null
||
triggerPriority
==
null
||
triggerPriority
.
Count
==
0
)
return
false
;
var
checkedSet
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
);
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
trigger
=
triggerPriority
[
i
];
if
(!
IsChangeTrigger
(
trigger
))
continue
;
if
(!
checkedSet
.
Add
(
trigger
))
continue
;
if
(
mate
.
HasMasterDuelTriggerParameter
(
trigger
))
return
true
;
}
return
false
;
}
private
static
bool
MateHasAnyTransitionCandidate
(
Mate
mate
,
IReadOnlyList
<
string
>
triggerPriority
)
{
if
(
mate
==
null
||
triggerPriority
==
null
||
triggerPriority
.
Count
==
0
)
return
false
;
return
mate
.
TryDescribeMasterDuelTransitionCandidates
(
triggerPriority
,
out
_
);
}
private
static
UniTask
WaitForSecondsUnscaledAsync
(
float
seconds
)
{
if
(
seconds
<=
0f
)
return
UniTask
.
CompletedTask
;
return
UniTask
.
Delay
(
TimeSpan
.
FromSeconds
(
seconds
),
DelayType
.
UnscaledDeltaTime
);
}
private
static
UniTask
WaitForSecondsSwapTimingAsync
(
float
seconds
,
bool
useUnscaledTiming
)
{
if
(
seconds
<=
0f
)
return
UniTask
.
CompletedTask
;
return
useUnscaledTiming
?
WaitForSecondsUnscaledAsync
(
seconds
)
:
UniTask
.
WaitForSeconds
(
seconds
);
}
private
static
async
UniTask
<
float
>
MeasureCurrentTransitionQueueDelayAsync
(
Mate
mate
,
int
beforeStateHash
,
bool
beforeInTransition
,
float
maxWaitSeconds
)
{
if
(
mate
==
null
||
maxWaitSeconds
<=
0f
)
return
0f
;
var
startRealtime
=
Time
.
realtimeSinceStartup
;
while
(
Time
.
realtimeSinceStartup
-
startRealtime
<
maxWaitSeconds
)
{
await
UniTask
.
Yield
(
PlayerLoopTiming
.
Update
);
if
(!
mate
.
TryGetMasterDuelPrimaryAnimatorSnapshot
(
out
var
stateHash
,
out
_
,
out
_
,
out
var
inTransition
,
out
var
activeInHierarchy
)
||
!
activeInHierarchy
)
{
return
0f
;
}
if
((!
beforeInTransition
&&
inTransition
)
||
stateHash
!=
beforeStateHash
)
return
Time
.
realtimeSinceStartup
-
startRealtime
;
}
return
0f
;
}
private
void
RequestPremiumMateForm
(
PremiumMateState
state
,
int
targetMateId
)
{
if
(
state
==
null
)
return
;
targetMateId
=
ResolvePremiumMateTargetId
(
state
,
targetMateId
);
if
(!
state
.
Forms
.
ContainsKey
(
targetMateId
))
return
;
if
(
state
.
IsTransitioning
&&
state
.
QueuedMateId
==
targetMateId
)
return
;
if
(!
state
.
IsTransitioning
&&
state
.
ActiveMateId
==
targetMateId
)
return
;
state
.
QueuedMateId
=
targetMateId
;
if
(
state
.
IsTransitioning
)
return
;
_
=
ProcessPremiumMateSwapQueueAsync
(
state
);
}
private
async
UniTask
ProcessPremiumMateSwapQueueAsync
(
PremiumMateState
state
)
{
if
(
state
==
null
)
return
;
state
.
IsTransitioning
=
true
;
try
{
while
(
state
.
QueuedMateId
!=
0
)
{
if
(
state
.
QueuedMateId
==
state
.
ActiveMateId
)
{
state
.
QueuedMateId
=
0
;
break
;
}
var
nextMateId
=
state
.
QueuedMateId
;
state
.
QueuedMateId
=
0
;
await
SwapPremiumMateAsync
(
state
,
nextMateId
);
}
}
finally
{
state
.
IsTransitioning
=
false
;
}
}
private
async
UniTask
SwapPremiumMateAsync
(
PremiumMateState
state
,
int
targetMateId
)
{
if
(
state
==
null
)
return
;
targetMateId
=
ResolvePremiumMateTargetId
(
state
,
targetMateId
);
if
(!
state
.
Forms
.
TryGetValue
(
targetMateId
,
out
var
nextMate
)
||
nextMate
==
null
)
return
;
var
currentMate
=
GetMateForSide
(
state
.
Side
);
if
(
currentMate
==
null
&&
state
.
Forms
.
TryGetValue
(
state
.
ActiveMateId
,
out
var
fromState
))
currentMate
=
fromState
;
if
(
currentMate
==
nextMate
)
{
SetExclusivePremiumMateActiveForm
(
state
,
nextMate
);
state
.
ActiveMateId
=
targetMateId
;
SetMateForSide
(
state
.
Side
,
nextMate
);
return
;
}
var
toSub
=
state
.
Rule
.
Behavior
==
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
?
targetMateId
==
GetRayeBattleMateId
(
state
)
:
targetMateId
==
state
.
Rule
.
SubId
;
var
swapDelay
=
toSub
?
state
.
SwapEffect
.
ToSubDelaySeconds
:
state
.
SwapEffect
.
ToBaseDelaySeconds
;
var
effectAssetPath
=
state
.
SwapEffect
.
GetEffectAssetPath
(
toSub
);
var
useUnscaledSwapTiming
=
state
.
SwapEffect
.
UseUnscaledSwapTiming
;
var
changeOnTargetMate
=
state
.
SwapEffect
.
PlayChangeOnTargetMate
;
var
changeOnBothMates
=
state
.
SwapEffect
.
PlayChangeOnBothMates
;
var
playChangeOnCurrentMate
=
!
changeOnTargetMate
||
changeOnBothMates
;
var
playChangeOnNextMate
=
changeOnTargetMate
||
changeOnBothMates
;
var
triggerPriorityOverride
=
state
.
OverrideTriggerPriority
;
var
currentTriggerPriority
=
triggerPriorityOverride
??
(
toSub
?
state
.
SwapEffect
.
ToSubCurrentTriggerPriority
:
state
.
SwapEffect
.
ToBaseCurrentTriggerPriority
);
var
nextTriggerPriority
=
triggerPriorityOverride
??
(
toSub
?
state
.
SwapEffect
.
ToSubNextTriggerPriority
:
state
.
SwapEffect
.
ToBaseNextTriggerPriority
);
IReadOnlyList
<
string
>
effectiveNextTriggerPriority
=
nextTriggerPriority
;
if
(
state
.
SwapEffect
.
UseChangeMotion
&&
currentMate
!=
null
&&
playChangeOnCurrentMate
&&
!
MateHasAnyTransitionCandidate
(
currentMate
,
currentTriggerPriority
))
playChangeOnCurrentMate
=
false
;
state
.
OverrideTriggerPriority
=
null
;
var
playedChangeOnCurrent
=
false
;
var
playedChangeOnNext
=
false
;
var
currentTransitionDelay
=
0f
;
var
nextTransitionDelay
=
0f
;
var
currentTriggerQueueDelay
=
0f
;
var
currentTriggerQueueProbeStarted
=
false
;
var
currentTriggerQueueDelayTask
=
UniTask
.
FromResult
(
0f
);
var
currentBeforeStateHash
=
0
;
var
currentBeforeInTransition
=
false
;
var
hasCurrentSnapshot
=
false
;
if
(
state
.
SwapEffect
.
CompensateCurrentTriggerQueueDelay
&&
currentMate
!=
null
)
{
hasCurrentSnapshot
=
currentMate
.
TryGetMasterDuelPrimaryAnimatorSnapshot
(
out
currentBeforeStateHash
,
out
_
,
out
_
,
out
currentBeforeInTransition
,
out
_
);
}
void
StartCurrentTriggerQueueProbeIfNeeded
()
{
if
(
currentTriggerQueueProbeStarted
||
!
playedChangeOnCurrent
||
!
hasCurrentSnapshot
)
return
;
if
(
state
.
SwapEffect
.
MaxCurrentTriggerQueueDelaySeconds
<=
0f
)
return
;
currentTriggerQueueProbeStarted
=
true
;
currentTriggerQueueDelayTask
=
MeasureCurrentTransitionQueueDelayAsync
(
currentMate
,
currentBeforeStateHash
,
currentBeforeInTransition
,
state
.
SwapEffect
.
MaxCurrentTriggerQueueDelaySeconds
);
}
var
effectPosition
=
currentMate
!=
null
?
currentMate
.
transform
.
position
:
state
.
Anchor
.
position
;
var
effectLabel
=
toSub
?
state
.
SwapEffect
.
ToSubEffectLabel
:
state
.
SwapEffect
.
ToBaseEffectLabel
;
var
preferEffectLabelPlayback
=
state
.
SwapEffect
.
PreferEffectLabelPlayback
&&
!
string
.
IsNullOrEmpty
(
effectLabel
);
if
(!
preferEffectLabelPlayback
&&
state
.
SwapEffect
.
UseChangeMotion
&&
currentMate
!=
null
&&
playChangeOnCurrentMate
)
{
playedChangeOnCurrent
=
currentMate
.
PlayChangeTransition
(
currentTriggerPriority
,
out
currentTransitionDelay
);
StartCurrentTriggerQueueProbeIfNeeded
();
}
if
(
state
.
SwapEffect
.
SourceToEffectDelaySeconds
>
0f
)
await
WaitForSecondsSwapTimingAsync
(
state
.
SwapEffect
.
SourceToEffectDelaySeconds
,
useUnscaledSwapTiming
);
var
effectPlayback
=
await
SpawnPremiumSwapEffectAsync
(
state
,
effectAssetPath
,
effectPosition
,
effectLabel
,
toSub
);
if
(
preferEffectLabelPlayback
&&
state
.
SwapEffect
.
UseChangeMotion
&&
currentMate
!=
null
&&
playChangeOnCurrentMate
)
{
if
(!
effectPlayback
.
Consumed
)
{
playedChangeOnCurrent
=
currentMate
.
PlayChangeTransition
(
currentTriggerPriority
,
out
currentTransitionDelay
);
StartCurrentTriggerQueueProbeIfNeeded
();
}
}
var
delay
=
swapDelay
;
if
(
currentTriggerQueueProbeStarted
)
{
currentTriggerQueueDelay
=
await
currentTriggerQueueDelayTask
;
if
(
currentTriggerQueueDelay
>
0f
)
delay
+=
currentTriggerQueueDelay
;
}
if
(
playedChangeOnCurrent
)
{
if
(
state
.
SwapEffect
.
UseTransitionDelayAsMinimum
)
delay
=
Mathf
.
Max
(
delay
,
currentTransitionDelay
);
else
if
(
delay
<=
0f
)
delay
=
currentTransitionDelay
;
}
var
keepCurrentAliveForOverlap
=
currentMate
!=
null
&&
playedChangeOnCurrent
&&
changeOnBothMates
&&
currentTransitionDelay
>
delay
;
if
(
delay
>
0f
)
await
WaitForSecondsSwapTimingAsync
(
delay
,
useUnscaledSwapTiming
);
targetMateId
=
ResolvePremiumMateTargetId
(
state
,
targetMateId
);
if
(!
state
.
Forms
.
TryGetValue
(
targetMateId
,
out
nextMate
)
||
nextMate
==
null
)
return
;
ResetMateTransform
(
nextMate
,
state
.
Anchor
);
if
(
currentMate
!=
null
&&
!
keepCurrentAliveForOverlap
)
currentMate
.
gameObject
.
SetActive
(
false
);
SetExclusivePremiumMateActiveForm
(
state
,
nextMate
,
keepCurrentAliveForOverlap
?
currentMate
:
null
);
nextMate
.
PrepareForPremiumSwapActivation
();
state
.
ActiveMateId
=
targetMateId
;
SetMateForSide
(
state
.
Side
,
nextMate
);
if
(
state
.
SwapEffect
.
PreferNonChangeNextWhenChangeTriggerMissing
&&
triggerPriorityOverride
==
null
&&
HasConfiguredChangeTrigger
(
nextTriggerPriority
)
&&
!
MateHasAnyChangeTriggerParameter
(
nextMate
,
nextTriggerPriority
))
{
var
reorderedPriority
=
BuildNonChangeFirstPriority
(
nextTriggerPriority
);
if
(!
ReferenceEquals
(
reorderedPriority
,
nextTriggerPriority
))
effectiveNextTriggerPriority
=
reorderedPriority
;
}
if
(
state
.
SwapEffect
.
NextMotionLeadInSeconds
>
0f
)
await
WaitForSecondsUnscaledAsync
(
state
.
SwapEffect
.
NextMotionLeadInSeconds
);
var
hasNextActivationTrigger
=
HasNonChangeTrigger
(
effectiveNextTriggerPriority
);
var
shouldPlayNextMotion
=
state
.
SwapEffect
.
UseChangeMotion
&&
(
playChangeOnNextMate
||
!
playedChangeOnCurrent
||
hasNextActivationTrigger
);
if
(
shouldPlayNextMotion
)
{
playedChangeOnNext
=
nextMate
.
PlayChangeTransition
(
effectiveNextTriggerPriority
,
out
nextTransitionDelay
);
var
nextStageWait
=
nextTransitionDelay
;
if
(
state
.
SwapEffect
.
NextMotionMinDurationSeconds
>
0f
)
nextStageWait
=
Mathf
.
Max
(
nextStageWait
,
state
.
SwapEffect
.
NextMotionMinDurationSeconds
);
if
(
nextStageWait
>
0f
)
await
WaitForSecondsUnscaledAsync
(
nextStageWait
);
if
(
state
.
SwapEffect
.
NextMotionDelaySeconds
>
0f
)
await
WaitForSecondsUnscaledAsync
(
state
.
SwapEffect
.
NextMotionDelaySeconds
);
}
if
(
keepCurrentAliveForOverlap
&&
currentMate
!=
null
)
currentMate
.
gameObject
.
SetActive
(
false
);
RestartMateRandomCooldown
(
state
.
Side
);
}
private
async
UniTask
<
SwapEffectLabelPlaybackResult
>
SpawnPremiumSwapEffectAsync
(
PremiumMateState
state
,
string
effectPath
,
Vector3
position
,
string
effectLabel
=
null
,
bool
toSub
=
true
)
{
if
(
state
==
null
||
state
.
SwapEffect
==
null
)
return
SwapEffectLabelPlaybackResult
.
None
;
if
(!
state
.
SwapEffect
.
HasChangeEffectAsset
(
toSub
))
return
SwapEffectLabelPlaybackResult
.
None
;
var
fullPath
=
Path
.
Combine
(
Program
.
root
,
effectPath
);
GameObject
effect
=
null
;
var
selectedScore
=
int
.
MinValue
;
void
ConsiderEffectCandidate
(
GameObject
candidate
)
{
if
(
candidate
==
null
)
return
;
var
score
=
GetSwapEffectCandidateScore
(
candidate
);
if
(
effect
==
null
||
score
>
selectedScore
)
{
if
(
effect
!=
null
&&
effect
!=
candidate
)
UnityEngine
.
Object
.
Destroy
(
effect
);
effect
=
candidate
;
selectedScore
=
score
;
}
else
{
UnityEngine
.
Object
.
Destroy
(
candidate
);
}
}
if
(
Directory
.
Exists
(
fullPath
))
{
var
leafName
=
Path
.
GetFileName
(
effectPath
);
var
directBundlePath
=
Path
.
Combine
(
fullPath
,
leafName
);
if
(!
string
.
IsNullOrEmpty
(
leafName
)
&&
File
.
Exists
(
directBundlePath
))
{
var
loadPath
=
effectPath
+
"/"
+
leafName
;
ConsiderEffectCandidate
(
await
ABLoader
.
LoadFromFileAsync
(
loadPath
,
false
,
true
));
}
ConsiderEffectCandidate
(
await
ABLoader
.
LoadFromFolderAsync
<
PlayableDirector
>(
effectPath
,
false
,
true
));
ConsiderEffectCandidate
(
await
ABLoader
.
LoadFromFolderAsync
<
ParticleSystem
>(
effectPath
,
false
,
true
));
}
else
{
ConsiderEffectCandidate
(
await
ABLoader
.
LoadFromFileAsync
(
effectPath
,
false
,
true
));
}
if
(
effect
==
null
)
return
SwapEffectLabelPlaybackResult
.
None
;
effect
.
transform
.
SetParent
(
Program
.
instance
.
container_3D
,
false
);
effect
.
transform
.
position
=
position
;
var
labelResult
=
await
PlayPremiumSwapEffectLabelAsync
(
effect
,
effectLabel
);
UnityEngine
.
Object
.
Destroy
(
effect
,
5f
);
return
labelResult
;
}
private
async
UniTask
<
SwapEffectLabelPlaybackResult
>
PlayPremiumSwapEffectLabelAsync
(
GameObject
effect
,
string
effectLabel
)
{
if
(
effect
==
null
)
return
SwapEffectLabelPlaybackResult
.
None
;
var
labelPlayed
=
false
;
var
fallbackPlayed
=
false
;
var
animators
=
effect
.
GetComponentsInChildren
<
Animator
>(
true
);
for
(
var
i
=
0
;
i
<
animators
.
Length
;
i
++)
{
var
animator
=
animators
[
i
];
if
(
animator
==
null
)
continue
;
if
(
TryPlayAnimatorEffectLabel
(
animator
,
effectLabel
))
labelPlayed
=
true
;
}
var
directors
=
effect
.
GetComponentsInChildren
<
PlayableDirector
>(
true
);
for
(
var
i
=
0
;
i
<
directors
.
Length
;
i
++)
{
var
director
=
directors
[
i
];
if
(
director
==
null
)
continue
;
if
(
TryPlayDirectorEffectLabel
(
director
,
effectLabel
,
out
var
startTime
))
{
director
.
time
=
startTime
;
director
.
Play
();
labelPlayed
=
true
;
}
}
var
changeEffects
=
effect
.
GetComponentsInChildren
<
BgAvatarChangeEffect
>(
true
);
if
(!
labelPlayed
&&
!
string
.
IsNullOrEmpty
(
effectLabel
))
{
for
(
var
i
=
0
;
i
<
changeEffects
.
Length
;
i
++)
{
if
(!
TryPlayBgAvatarChangeEffectLabel
(
changeEffects
[
i
],
effectLabel
))
continue
;
labelPlayed
=
true
;
break
;
}
}
if
(!
labelPlayed
&&
!
string
.
IsNullOrEmpty
(
effectLabel
))
{
if
(
TryPlayElementObjectLabel
(
effect
,
effectLabel
))
labelPlayed
=
true
;
else
if
(
TryPlayAnyComponentLabel
(
effect
,
effectLabel
))
labelPlayed
=
true
;
}
var
labeledControllers
=
effect
.
GetComponentsInChildren
<
LabeledPlayableController
>(
true
);
if
(!
labelPlayed
&&
!
string
.
IsNullOrEmpty
(
effectLabel
)
&&
labeledControllers
.
Length
>
0
)
{
for
(
var
i
=
0
;
i
<
directors
.
Length
;
i
++)
{
var
director
=
directors
[
i
];
if
(
director
==
null
||
director
.
state
==
PlayState
.
Playing
)
continue
;
director
.
Play
();
}
const
int
maxFramesToWait
=
3
;
for
(
var
frame
=
0
;
frame
<=
maxFramesToWait
&&
!
labelPlayed
;
frame
++)
{
gps
=
new
(
)
for
(
var
i
=
0
;
i
<
labeledControllers
.
Length
;
i
++
)
{
controller
=
c
,
location
=
(
uint
)
CardLocation
.
SpellZone
,
sequence
=
s
};
CreatePlaceSelector
(
gps
);
}
}
var
controller
=
labeledControllers
[
i
];
if
(
controller
==
null
||
controller
.
loopMixerBehaviour
==
null
)
continue
;
#
endregion
controller
.
PlayLabel
(
effectLabel
,
(
TimelineClip
)
null
);
labelPlayed
=
true
;
break
;
}
#
region
Quit
Load
if
(!
labelPlayed
&&
frame
<
maxFramesToWait
)
await
UniTask
.
Yield
();
}
}
await
processor
.
PreloadPlayerNames
();
if
(
Core
.
NeedVoice
())
if
(!
labelPlayed
&&
!
string
.
IsNullOrEmpty
(
effectLabel
)
&&
directors
.
Length
==
1
)
{
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
alpha
=
1
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
blocksRaycasts
=
true
;
var
director
=
directors
[
0
];
if
(
director
!=
null
)
{
director
.
time
=
0d
;
director
.
Play
();
fallbackPlayed
=
true
;
}
}
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
false
);
UIManager
.
ShowFPSLeft
();
UIManager
.
HideBlackBack
(
0f
);
UIManager
.
UIBlackOut
(
Core
.
TransitionTime
);
await
UniTask
.
WaitForSeconds
(
Core
.
TransitionTime
);
return
new
SwapEffectLabelPlaybackResult
(
labelPlayed
||
fallbackPlayed
);
}
backgroundFieldInitialize
=
false
;
BackgroundFieldInitialize
();
private
static
bool
TryPlayBgAvatarChangeEffectLabel
(
BgAvatarChangeEffect
changeEffect
,
string
effectLabel
)
{
if
(
changeEffect
==
null
||
string
.
IsNullOrEmpty
(
effectLabel
))
return
false
;
if
(
condition
==
Condition
.
Duel
&&
Config
.
GetBool
(
"DuelAutoAcc"
,
false
)
||
condition
==
Condition
.
Watch
&&
Config
.
GetBool
(
"WatchAutoAcc"
,
false
)
||
condition
==
Condition
.
Replay
&&
Config
.
GetBool
(
"ReplayAutoAcc"
,
false
)
)
Core
.
GetUI
<
OcgCoreUI
>().
OnAcc
()
;
var
toMain
=
LabelEquals
(
changeEffect
.
toMainLabel
,
effectLabel
);
var
toSub
=
LabelEquals
(
changeEffect
.
toSubLabel
,
effectLabel
);
if
(!
toMain
&&
!
toSub
)
return
false
;
#
endregion
var
fieldName
=
toMain
?
"toMainObj"
:
"toSubObj"
;
var
field
=
typeof
(
BgAvatarChangeEffect
).
GetField
(
fieldName
,
BindingFlags
.
Instance
|
BindingFlags
.
NonPublic
|
BindingFlags
.
Public
);
var
particle
=
field
!=
null
?
field
.
GetValue
(
changeEffect
)
as
ParticleSystem
:
null
;
if
(
particle
!=
null
)
{
PlayTargetObject
(
particle
.
gameObject
);
return
true
;
}
loaded
=
true
;
if
(
TryPlayElementObjectLabel
(
changeEffect
.
gameObject
,
effectLabel
))
return
true
;
return
TryPlayAnyComponentLabel
(
changeEffect
.
gameObject
,
effectLabel
);
}
p
ublic
async
UniTask
ExitDuelAsync
(
)
p
rivate
static
bool
TryPlayElementObjectLabel
(
GameObject
root
,
string
effectLabel
)
{
ClearResponse
();
CameraManager
.
BlackOut
(
0f
,
0.3f
)
;
if
(
root
==
null
||
string
.
IsNullOrEmpty
(
effectLabel
))
return
false
;
UIManager
.
UIBlackIn
(
Core
.
TransitionTime
);
Core
.
GetUI
<
OcgCoreUI
>().
CloseHint
();
HideAttackLine
();
HideDuelFinalBlowText
();
await
UniTask
.
WaitForSeconds
(
Core
.
TransitionTime
);
Core
.
servantUI
.
ShutDown
();
var
elements
=
root
.
GetComponentsInChildren
<
ElementObject
>(
true
);
var
played
=
false
;
for
(
var
i
=
0
;
i
<
elements
.
Length
;
i
++)
{
var
element
=
elements
[
i
];
if
(
element
==
null
||
!
LabelEquals
(
element
.
label
,
effectLabel
))
continue
;
NoMoreWait
=
true
;
packages
.
Clear
();
allPackages
.
Clear
();
AudioManager
.
ResetSESource
();
mycardDuel
=
false
;
Core
.
CloseCharaFace
();
Dispose
();
PlayTargetObject
(
element
.
gameObject
);
played
=
true
;
}
foreach
(
var
card
in
cards
)
card
.
Dispose
();
cards
.
Clear
();
pause
=
false
;
nextMoveAction
=
null
;
Core
.
cachedCharaFaces
.
Clear
();
CameraManager
.
ShiftTo2D
();
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
false
);
Core
.
GetUI
<
OcgCoreUI
>().
DuelLog
.
ClearLog
();
Program
.
instance
.
ui_
.
chatPanel
.
Hide
();
await
UniTask
.
WaitForSeconds
(
0.3f
);
UIManager
.
UIBlackOut
(
Core
.
TransitionTime
);
await
UniTask
.
WaitForSeconds
(
Core
.
TransitionTime
);
UIManager
.
ShowFPSRight
();
AudioManager
.
PlayBGM
(
AudioManager
.
BGM_MENU_MAIN
);
return
played
;
}
private
void
InitializeDeckModel
(
ElementObjectManager
deckManager
,
int
player
,
CardLocation
location
)
private
static
bool
TryPlayAnyComponentLabel
(
GameObject
root
,
string
effectLabel
)
{
deckManager
.
transform
.
SetParent
(
player
==
0
?
field0Manager
.
transform
:
field1Manager
.
transform
,
false
);
deckManager
.
transform
.
localPosition
=
_positionMap
[(
player
,
location
)];
deckManager
.
transform
.
localEulerAngles
=
_angleMap
[(
player
,
location
)];
allGameObjects
.
Add
(
deckManager
.
gameObject
);
if
(
root
==
null
||
string
.
IsNullOrEmpty
(
effectLabel
))
return
false
;
var
mat
=
player
==
0
?
myProtector
:
opProtector
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"DummyDeck/DummyCardModel_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel01_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel02_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel03_back"
).
material
=
mat
;
deckManager
.
GetNestedElement
<
MeshRenderer
>(
"CardShuffleTop/CardModel04_back"
).
material
=
mat
;
var
behaviours
=
root
.
GetComponentsInChildren
<
MonoBehaviour
>(
true
);
var
played
=
false
;
for
(
var
i
=
0
;
i
<
behaviours
.
Length
;
i
++)
{
var
behaviour
=
behaviours
[
i
];
if
(
behaviour
==
null
)
continue
;
deckManager
.
gameObject
.
SetActive
(
false
);
var
field
=
behaviour
.
GetType
().
GetField
(
"label"
,
BindingFlags
.
Instance
|
BindingFlags
.
Public
|
BindingFlags
.
NonPublic
);
if
(
field
==
null
||
field
.
FieldType
!=
typeof
(
string
))
continue
;
var
value
=
field
.
GetValue
(
behaviour
)
as
string
;
if
(!
LabelEquals
(
value
,
effectLabel
))
continue
;
PlayTargetObject
(
behaviour
.
gameObject
);
played
=
true
;
}
return
played
;
}
private
static
readonly
Dictionary
<(
int
,
CardLocation
),
Vector3
>
_positionMap
=
new
(
)
private
static
void
PlayTargetObject
(
GameObject
target
)
{
[(
0
,
CardLocation
.
Deck
)]
=
new
(
26.6f
,
1.5f
,
-
23.5f
),
[(
0
,
CardLocation
.
Extra
)]
=
new
(-
26.6f
,
1.5f
,
-
23.5f
),
[(
1
,
CardLocation
.
Deck
)]
=
new
(-
26.6f
,
1.5f
,
23.5f
),
[(
1
,
CardLocation
.
Extra
)]
=
new
(
26.6f
,
1.5f
,
23.5f
)
};
if
(
target
==
null
)
return
;
private
static
readonly
Dictionary
<(
int
,
CardLocation
),
Vector3
>
_angleMap
=
new
()
target
.
SetActive
(
true
);
var
particles
=
target
.
GetComponentsInChildren
<
ParticleSystem
>(
true
);
for
(
var
i
=
0
;
i
<
particles
.
Length
;
i
++)
{
var
particle
=
particles
[
i
];
if
(
particle
==
null
)
continue
;
particle
.
gameObject
.
SetActive
(
true
);
particle
.
Clear
(
true
);
particle
.
Play
(
true
);
}
var
animators
=
target
.
GetComponentsInChildren
<
Animator
>(
true
);
for
(
var
i
=
0
;
i
<
animators
.
Length
;
i
++)
{
var
animator
=
animators
[
i
];
if
(
animator
==
null
)
continue
;
animator
.
gameObject
.
SetActive
(
true
);
animator
.
Rebind
();
animator
.
Update
(
0f
);
animator
.
Play
(
0
,
0
,
0f
);
}
var
directors
=
target
.
GetComponentsInChildren
<
PlayableDirector
>(
true
);
for
(
var
i
=
0
;
i
<
directors
.
Length
;
i
++)
{
var
director
=
directors
[
i
];
if
(
director
==
null
)
continue
;
director
.
gameObject
.
SetActive
(
true
);
director
.
time
=
0d
;
director
.
Play
();
}
}
private
static
bool
LabelEquals
(
string
a
,
string
b
)
{
[(
0
,
CardLocation
.
Deck
)]
=
new
(
0f
,
-
20f
,
0f
),
[(
0
,
CardLocation
.
Extra
)]
=
new
(
0f
,
20f
,
0f
),
[(
1
,
CardLocation
.
Deck
)]
=
new
(
0f
,
160f
,
0f
),
[(
1
,
CardLocation
.
Extra
)]
=
new
(
0f
,
-
160f
,
0f
)
};
return
!
string
.
IsNullOrEmpty
(
a
)
&&
!
string
.
IsNullOrEmpty
(
b
)
&&
a
.
Equals
(
b
,
StringComparison
.
OrdinalIgnoreCase
);
}
p
ublic
async
UniTask
ShowDecksAsync
(
)
p
rivate
static
bool
TryPlayDirectorEffectLabel
(
PlayableDirector
director
,
string
effectLabel
,
out
double
startTime
)
{
if
(
myDeck
==
null
||
myExtra
==
null
||
opDeck
==
null
||
opExtra
==
null
)
return
;
startTime
=
0d
;
if
(
director
==
null
||
string
.
IsNullOrEmpty
(
effectLabel
)
)
return
false
;
await
ShowAllDeckModelsAsync
();
var
playableName
=
director
.
playableAsset
!=
null
?
director
.
playableAsset
.
name
:
string
.
Empty
;
var
nameMatch
=
(!
string
.
IsNullOrEmpty
(
playableName
)
&&
playableName
.
IndexOf
(
effectLabel
,
StringComparison
.
OrdinalIgnoreCase
)
>=
0
)
||
director
.
name
.
IndexOf
(
effectLabel
,
StringComparison
.
OrdinalIgnoreCase
)
>=
0
||
director
.
gameObject
.
name
.
IndexOf
(
effectLabel
,
StringComparison
.
OrdinalIgnoreCase
)
>=
0
;
if
(
nameMatch
)
return
true
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
alpha
=
1
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
blocksRaycasts
=
true
;
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
true
);
AudioManager
.
PlayBgmNormal
(
Config
.
GetBool
(
"BGMbyMySide"
,
true
)
?
field0Manager
.
name
:
field1Manager
.
name
);
return
TryGetDirectorLabelStartTime
(
director
,
effectLabel
,
out
startTime
);
}
p
ublic
async
UniTask
ShowDecksWithDuelStartTextAsync
(
)
p
rivate
static
bool
TryGetDirectorLabelStartTime
(
PlayableDirector
director
,
string
effectLabel
,
out
double
startTime
)
{
if
(
myDeck
==
null
||
myExtra
==
null
||
opDeck
==
null
||
opExtra
==
null
)
return
;
startTime
=
0d
;
if
(
director
==
null
||
string
.
IsNullOrEmpty
(
effectLabel
)
||
director
.
playableAsset
==
null
)
return
false
;
await
ShowAllDeckModelsAsync
();
var
outputs
=
director
.
playableAsset
.
outputs
;
foreach
(
var
output
in
outputs
)
{
var
track
=
output
.
sourceObject
as
TrackAsset
;
if
(
track
==
null
)
continue
;
var
effect
=
ABLoader
.
LoadMasterDuelGameObject
(
"DuelTextStart"
);
var
director
=
effect
.
GetComponent
<
PlayableDirector
>();
await
director
.
WaitAsync
();
UnityEngine
.
Object
.
Destroy
(
effect
)
;
foreach
(
var
clip
in
track
.
GetClips
())
{
if
(
clip
==
null
)
continue
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
alpha
=
1
;
Core
.
GetUI
<
OcgCoreUI
>().
CG
.
blocksRaycasts
=
true
;
Core
.
GetUI
<
OcgCoreUI
>().
Buttons
.
SetActive
(
true
);
AudioManager
.
PlayBgmNormal
(
Config
.
GetBool
(
"BGMbyMySide"
,
true
)
?
field0Manager
.
name
:
field1Manager
.
name
);
var
displayName
=
clip
.
displayName
;
if
(!
string
.
IsNullOrEmpty
(
displayName
)
&&
displayName
.
Equals
(
effectLabel
,
StringComparison
.
OrdinalIgnoreCase
))
{
startTime
=
clip
.
start
;
return
true
;
}
}
}
return
false
;
}
p
ublic
void
BackgroundFieldInitialize
(
)
p
rivate
static
int
GetSwapEffectCandidateScore
(
GameObject
candidate
)
{
if
(
field0Manager
==
null
||
field1Manager
==
null
)
return
;
if
(
bgPhase0
==
1
&&
bgPhase1
==
1
)
return
;
if
(
candidate
==
null
)
return
int
.
MinValue
;
var
labeledControllers
=
candidate
.
GetComponentsInChildren
<
LabeledPlayableController
>(
true
).
Length
;
var
directors
=
candidate
.
GetComponentsInChildren
<
PlayableDirector
>(
true
).
Length
;
var
animators
=
candidate
.
GetComponentsInChildren
<
Animator
>(
true
).
Length
;
var
particles
=
candidate
.
GetComponentsInChildren
<
ParticleSystem
>(
true
).
Length
;
var
changeEffects
=
candidate
.
GetComponentsInChildren
<
BgAvatarChangeEffect
>(
true
).
Length
;
var
elementLabels
=
candidate
.
GetComponentsInChildren
<
ElementObject
>(
true
).
Length
;
return
labeledControllers
*
100
+
changeEffects
*
60
+
directors
*
20
+
animators
*
8
+
elementLabels
*
4
+
particles
*
2
;
}
if
(
backgroundFieldInitialize
)
private
static
bool
TryPlayAnimatorEffectLabel
(
Animator
animator
,
string
effectLabel
)
{
if
(
animator
==
null
||
string
.
IsNullOrEmpty
(
effectLabel
))
return
false
;
var
played
=
false
;
var
parameters
=
animator
.
parameters
;
for
(
var
i
=
0
;
i
<
parameters
.
Length
;
i
++)
{
field0Manager
.
gameObject
.
SetActive
(
false
);
field1Manager
.
gameObject
.
SetActive
(
false
);
field0Manager
.
gameObject
.
SetActive
(
true
);
field1Manager
.
gameObject
.
SetActive
(
true
);
var
parameter
=
parameters
[
i
];
if
(
parameter
.
type
!=
AnimatorControllerParameterType
.
Trigger
)
continue
;
if
(!
parameter
.
name
.
Equals
(
effectLabel
,
StringComparison
.
OrdinalIgnoreCase
))
continue
;
animator
.
SetTrigger
(
parameter
.
name
);
played
=
true
;
}
if
(
played
)
return
true
;
field0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
grave0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
bgPhase0
=
1
;
field1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
grave1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
StartToPhase1
);
bgPhase1
=
1
;
if
(
mate0
!=
null
)
for
(
var
layer
=
0
;
layer
<
animator
.
layerCount
;
layer
++)
{
mate0
.
gameObject
.
SetActive
(
true
);
mate0
.
Play
(
Mate
.
MateAction
.
Entry
);
var
layerName
=
animator
.
GetLayerName
(
layer
);
var
shortHash
=
Animator
.
StringToHash
(
effectLabel
);
var
fullHash
=
Animator
.
StringToHash
(
$"
{
layerName
}
.
{
effectLabel
}
"
);
if
(
animator
.
HasState
(
layer
,
fullHash
))
{
animator
.
Play
(
fullHash
,
layer
,
0f
);
played
=
true
;
continue
;
}
if
(
animator
.
HasState
(
layer
,
shortHash
))
{
animator
.
Play
(
shortHash
,
layer
,
0f
);
played
=
true
;
}
}
if
(
mate1
!=
null
)
if
(
played
)
return
true
;
var
controller
=
animator
.
runtimeAnimatorController
;
if
(
controller
==
null
)
return
false
;
var
clips
=
controller
.
animationClips
;
for
(
var
i
=
0
;
i
<
clips
.
Length
;
i
++)
{
mate1
.
gameObject
.
SetActive
(
true
);
mate1
.
Play
(
Mate
.
MateAction
.
Entry
);
var
clip
=
clips
[
i
];
if
(
clip
==
null
)
continue
;
if
(!
clip
.
name
.
Equals
(
effectLabel
,
StringComparison
.
OrdinalIgnoreCase
))
continue
;
animator
.
Play
(
clip
.
name
,
0
,
0f
);
return
true
;
}
if
(
timerHandler
!=
null
)
timerHandler
.
DuelStart
();
mate0Random
=
false
;
mate1Random
=
false
;
DOTween
.
To
(
v
=>
{
},
0
,
0
,
UnityEngine
.
Random
.
Range
(
8
,
16
)).
OnComplete
(()
=>
{
mate0Random
=
true
;
});
DOTween
.
To
(
v
=>
{
},
0
,
0
,
UnityEngine
.
Random
.
Range
(
8
,
16
)).
OnComplete
(()
=>
{
mate1Random
=
true
;
});
return
false
;
}
backgroundFieldInitialize
=
true
;
private
Mate
GetMateForSide
(
int
side
)
{
return
side
==
0
?
mate0
:
mate1
;
}
private
void
SetMateForSide
(
int
side
,
Mate
mate
)
{
if
(
side
==
0
)
mate0
=
mate
;
else
mate1
=
mate
;
}
private
PremiumMateState
GetPremiumMateState
(
int
side
)
{
return
side
==
0
?
premiumMate0
:
premiumMate1
;
}
private
void
SetPremiumMateState
(
int
side
,
PremiumMateState
state
)
{
if
(
side
==
0
)
premiumMate0
=
state
;
else
premiumMate1
=
state
;
}
private
void
ResetPremiumMateStates
()
{
premiumMate0
=
null
;
premiumMate1
=
null
;
mate0RandomCooldownTween
?.
Kill
();
mate0RandomCooldownTween
=
null
;
mate1RandomCooldownTween
?.
Kill
();
mate1RandomCooldownTween
=
null
;
}
public
void
RefreshBgState
()
...
...
@@ -741,7 +1930,7 @@ namespace MDPro3.Duel
{
field0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
PhaseToDamagePhaseAll
);
//field0Manager.PlayAnimatorTrigger(TriggerLabelDefine.DamagePhaseToNextPhaseAll);
if
(
mate0
!=
null
&&
!
first
)
if
(
!
first
&&
CanPlayMateAction
(
0
,
mate0
)
)
mate0
.
Play
(
Mate
.
MateAction
.
GetDamage
);
if
(
bgPhase0
==
1
&&
life0
<
(
lpLimit
*
0.75f
))
{
...
...
@@ -790,7 +1979,7 @@ namespace MDPro3.Duel
{
field1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
PhaseToDamagePhaseAll
);
//field1Manager.PlayAnimatorTrigger(TriggerLabelDefine.DamagePhaseToNextPhaseAll);
if
(
mate1
!=
null
&&
!
first
)
if
(
!
first
&&
CanPlayMateAction
(
1
,
mate1
)
)
mate1
.
Play
(
Mate
.
MateAction
.
GetDamage
);
if
(
bgPhase1
==
1
&&
life1
<
(
lpLimit
*
0.75f
))
{
...
...
@@ -1085,6 +2274,9 @@ namespace MDPro3.Duel
foreach
(
var
go
in
allGameObjects
)
UnityEngine
.
Object
.
Destroy
(
go
);
allGameObjects
.
Clear
();
ResetPremiumMateStates
();
mate0
=
null
;
mate1
=
null
;
}
public
async
UniTask
ShowDuelResultText
(
string
text
)
...
...
@@ -1148,7 +2340,7 @@ namespace MDPro3.Duel
void
HeroWin
()
{
field0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
EndWin
);
if
(
mate0
!=
null
)
if
(
CanPlayMateAction
(
0
,
mate0
)
)
mate0
.
Play
(
Mate
.
MateAction
.
Victory
);
}
...
...
@@ -1164,14 +2356,14 @@ namespace MDPro3.Duel
if
(
stand0Manager
!=
null
)
stand0Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
DamagePhase4ToEnd
);
if
(
mate0
!=
null
)
if
(
CanPlayMateAction
(
0
,
mate0
)
)
mate0
.
Play
(
Mate
.
MateAction
.
Defeat
);
}
void
RivalWin
()
{
field1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
EndWin
);
if
(
mate1
!=
null
)
if
(
CanPlayMateAction
(
1
,
mate1
)
)
mate1
.
Play
(
Mate
.
MateAction
.
Victory
);
}
...
...
@@ -1187,7 +2379,7 @@ namespace MDPro3.Duel
if
(
stand1Manager
!=
null
)
stand1Manager
.
PlayAnimatorTrigger
(
TriggerLabelDefine
.
DamagePhase4ToEnd
);
if
(
mate1
!=
null
)
if
(
CanPlayMateAction
(
1
,
mate1
)
)
mate1
.
Play
(
Mate
.
MateAction
.
Defeat
);
}
...
...
@@ -2470,10 +3662,12 @@ namespace MDPro3.Duel
public
bool
HoveringMate0
()
{
if
(
mate0
==
null
)
if
(
mate0
==
null
)
return
false
;
if
(
UserInput
.
HoverObject
==
mate0
.
gameObject
)
return
true
;
if
(
UserInput
.
HoverObject
!=
null
&&
UserInput
.
HoverObject
.
transform
.
IsChildOf
(
mate0
.
transform
))
return
true
;
return
false
;
}
...
...
@@ -2483,12 +3677,14 @@ namespace MDPro3.Duel
return
false
;
if
(
UserInput
.
HoverObject
==
mate1
.
gameObject
)
return
true
;
if
(
UserInput
.
HoverObject
!=
null
&&
UserInput
.
HoverObject
.
transform
.
IsChildOf
(
mate1
.
transform
))
return
true
;
return
false
;
}
public
void
TapMate0
()
{
if
(
mate0
==
null
)
if
(
!
CanPlayMateAction
(
0
,
mate0
,
false
)
)
return
;
if
(
Time
.
time
-
mate0TapTime
<
1f
)
return
;
...
...
@@ -2498,7 +3694,7 @@ namespace MDPro3.Duel
public
void
TapMate1
()
{
if
(
mate1
==
null
)
if
(
!
CanPlayMateAction
(
1
,
mate1
,
false
)
)
return
;
if
(
Time
.
time
-
mate1TapTime
<
1f
)
return
;
...
...
@@ -2508,28 +3704,84 @@ namespace MDPro3.Duel
public
void
PlayMate0Random
()
{
if
(
mate0
==
null
)
if
(!
CanPlayMateAction
(
0
,
mate0
))
return
;
if
(
ShouldSuppressAmbientPremiumMateActions
(
GetPremiumMateState
(
0
)))
return
;
if
(!
mate0Random
)
return
;
mate0
.
Play
(
Mate
.
MateAction
.
Random
);
RestartMateRandomCooldown
(
0
);
}
public
void
PlayMate1Random
()
{
if
(!
CanPlayMateAction
(
1
,
mate1
))
return
;
if
(
mate0Random
)
if
(
ShouldSuppressAmbientPremiumMateActions
(
GetPremiumMateState
(
1
)))
return
;
if
(!
mate1Random
)
return
;
mate1
.
Play
(
Mate
.
MateAction
.
Random
);
RestartMateRandomCooldown
(
1
);
}
private
bool
CanPlayMateAction
(
int
side
,
Mate
mate
,
bool
skipWhileTransitioning
=
true
)
{
if
(
mate
==
null
)
return
false
;
if
(
skipWhileTransitioning
&&
IsPremiumMateTransitioning
(
side
))
return
false
;
return
true
;
}
private
bool
IsPremiumMateTransitioning
(
int
side
)
{
var
state
=
GetPremiumMateState
(
side
);
return
state
!=
null
&&
state
.
IsTransitioning
;
}
private
void
RestartMateRandomCooldown
(
int
side
)
{
var
premiumState
=
GetPremiumMateState
(
side
);
if
(
ShouldSuppressAmbientPremiumMateActions
(
premiumState
))
{
if
(
side
==
0
)
{
mate0Random
=
false
;
mate0RandomCooldownTween
?.
Kill
();
mate0RandomCooldownTween
=
null
;
}
else
{
mate1Random
=
false
;
mate1RandomCooldownTween
?.
Kill
();
mate1RandomCooldownTween
=
null
;
}
return
;
}
var
mate
=
side
==
0
?
mate0
:
mate1
;
var
delay
=
mate
!=
null
&&
mate
.
type
==
Mate
.
MateType
.
MasterDuel
?
UnityEngine
.
Random
.
Range
(
20f
,
70f
)
:
UnityEngine
.
Random
.
Range
(
8f
,
16f
);
if
(
side
==
0
)
{
mate0Random
=
false
;
mate0
.
Play
(
Mate
.
MateAction
.
Random
);
DOTween
.
To
(
v
=>
{
},
0
,
0
,
UnityEngine
.
Random
.
Range
(
8
,
16
)
).
OnComplete
(()
=>
mate0
RandomCooldownTween
?.
Kill
(
);
mate0RandomCooldownTween
=
DOTween
.
To
(
v
=>
{
},
0
,
0
,
delay
).
OnComplete
(()
=>
{
mate0Random
=
true
;
});
}
}
public
void
PlayMate1Random
()
{
if
(
mate1
==
null
)
return
;
if
(
mate1Random
)
else
{
mate1Random
=
false
;
mate1
.
Play
(
Mate
.
MateAction
.
Random
);
DOTween
.
To
(
v
=>
{
},
0
,
0
,
UnityEngine
.
Random
.
Range
(
8
,
16
)
).
OnComplete
(()
=>
mate1
RandomCooldownTween
?.
Kill
(
);
mate1RandomCooldownTween
=
DOTween
.
To
(
v
=>
{
},
0
,
0
,
delay
).
OnComplete
(()
=>
{
mate1Random
=
true
;
});
...
...
Assets/Scripts/MDPro3/Duel/BG/PremiumMateRules.cs
0 → 100644
View file @
d20b6d9e
using
System.Collections.Generic
;
using
System.Linq
;
namespace
MDPro3.Duel
{
public
enum
PremiumMateBehavior
{
LaundryBattlePhaseRoundTrip
,
GaiaExtraDeckPermanent
,
ShuraigLpThreshold
,
RayeBattlePhaseAndDirectAttack
,
FiendsmithExtraDeckOrEquipPermanent
,
IpSpTurnParity
}
public
sealed
class
PremiumMateRule
{
public
int
BaseId
{
get
;
}
public
int
SubId
{
get
;
}
public
IReadOnlyList
<
int
>
VariantIds
{
get
;
}
public
PremiumMateBehavior
Behavior
{
get
;
}
public
int
LpThreshold
{
get
;
}
public
PremiumMateRule
(
int
baseId
,
int
subId
,
PremiumMateBehavior
behavior
,
int
lpThreshold
=
0
,
IReadOnlyList
<
int
>
extraVariantIds
=
null
)
{
BaseId
=
baseId
;
SubId
=
subId
;
Behavior
=
behavior
;
LpThreshold
=
lpThreshold
;
var
variants
=
new
List
<
int
>
{
subId
};
if
(
extraVariantIds
!=
null
)
foreach
(
var
variantId
in
extraVariantIds
)
if
(
variantId
>
0
&&
variantId
!=
baseId
&&
!
variants
.
Contains
(
variantId
))
variants
.
Add
(
variantId
);
VariantIds
=
variants
;
}
}
public
static
class
PremiumMateRules
{
private
static
readonly
List
<
PremiumMateRule
>
_rules
=
new
()
{
new
PremiumMateRule
(
1000020
,
1000021
,
PremiumMateBehavior
.
LaundryBattlePhaseRoundTrip
),
new
PremiumMateRule
(
1003001
,
1003101
,
PremiumMateBehavior
.
GaiaExtraDeckPermanent
),
new
PremiumMateRule
(
1003002
,
1003102
,
PremiumMateBehavior
.
ShuraigLpThreshold
,
3000
),
new
PremiumMateRule
(
1003003
,
1003203
,
PremiumMateBehavior
.
RayeBattlePhaseAndDirectAttack
,
0
,
new
[]
{
1003103
}),
new
PremiumMateRule
(
1003004
,
1003104
,
PremiumMateBehavior
.
FiendsmithExtraDeckOrEquipPermanent
),
new
PremiumMateRule
(
1003005
,
1003105
,
PremiumMateBehavior
.
IpSpTurnParity
),
};
private
static
readonly
Dictionary
<
int
,
PremiumMateRule
>
_ruleByAnyId
=
_rules
.
SelectMany
(
rule
=>
rule
.
VariantIds
.
Select
(
id
=>
new
KeyValuePair
<
int
,
PremiumMateRule
>(
id
,
rule
))
.
Prepend
(
new
KeyValuePair
<
int
,
PremiumMateRule
>(
rule
.
BaseId
,
rule
)))
.
ToDictionary
(
pair
=>
pair
.
Key
,
pair
=>
pair
.
Value
);
private
static
readonly
Dictionary
<
int
,
PremiumMateRule
>
_ruleByBaseId
=
_rules
.
ToDictionary
(
rule
=>
rule
.
BaseId
,
rule
=>
rule
);
public
static
IReadOnlyList
<
PremiumMateRule
>
All
=>
_rules
;
public
static
bool
TryGetRule
(
int
mateId
,
out
PremiumMateRule
rule
)
{
return
_ruleByAnyId
.
TryGetValue
(
mateId
,
out
rule
);
}
public
static
bool
TryGetRuleByBaseId
(
int
mateId
,
out
PremiumMateRule
rule
)
{
return
_ruleByBaseId
.
TryGetValue
(
mateId
,
out
rule
);
}
public
static
bool
IsPremiumMateId
(
int
mateId
)
{
return
_ruleByAnyId
.
ContainsKey
(
mateId
);
}
public
static
bool
IsPremiumBaseId
(
int
mateId
)
{
return
_ruleByBaseId
.
ContainsKey
(
mateId
);
}
public
static
bool
IsPremiumVariantId
(
int
mateId
)
{
return
TryGetRule
(
mateId
,
out
var
rule
)
&&
rule
.
VariantIds
.
Contains
(
mateId
);
}
public
static
int
GetBaseMateId
(
int
mateId
)
{
return
TryGetRule
(
mateId
,
out
var
rule
)
?
rule
.
BaseId
:
mateId
;
}
public
static
int
GetSubMateId
(
int
mateId
)
{
return
TryGetRule
(
mateId
,
out
var
rule
)
?
rule
.
SubId
:
mateId
;
}
public
static
List
<
Items
.
Item
>
FilterAppearanceMateItems
(
IEnumerable
<
Items
.
Item
>
source
)
{
return
source
.
Where
(
item
=>
!
IsPremiumVariantId
(
item
.
id
)).
ToList
();
}
}
}
Assets/Scripts/MDPro3/Duel/BG/PremiumMateRules.cs.meta
0 → 100644
View file @
d20b6d9e
fileFormatVersion: 2
guid: 555a55c03fd6e3d45bc341e9808a721c
\ No newline at end of file
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs
0 → 100644
View file @
d20b6d9e
using
System.Collections.Generic
;
using
System.IO
;
using
System.Linq
;
using
UnityEngine
;
namespace
MDPro3.Duel
{
public
sealed
class
PremiumMateSwapEffect
{
public
int
BaseId
{
get
;
}
public
float
DelaySeconds
{
get
;
}
public
float
ToSubDelaySeconds
{
get
;
}
public
float
ToBaseDelaySeconds
{
get
;
}
public
bool
UseChangeMotion
{
get
;
}
public
bool
UseTransitionDelayAsMinimum
{
get
;
}
public
string
EffectAssetPath
{
get
;
}
public
string
ToSubEffectAssetPath
{
get
;
}
public
string
ToBaseEffectAssetPath
{
get
;
}
public
string
ToSubEffectLabel
{
get
;
}
public
string
ToBaseEffectLabel
{
get
;
}
public
bool
PreferEffectLabelPlayback
{
get
;
}
public
bool
PlayChangeOnTargetMate
{
get
;
}
public
bool
PlayChangeOnBothMates
{
get
;
}
public
bool
PreferNonChangeNextWhenChangeTriggerMissing
{
get
;
}
public
bool
CompensateCurrentTriggerQueueDelay
{
get
;
}
public
float
MaxCurrentTriggerQueueDelaySeconds
{
get
;
}
public
bool
UseUnscaledSwapTiming
{
get
;
}
public
float
NextMotionDelaySeconds
{
get
;
}
public
float
SourceToEffectDelaySeconds
{
get
;
}
public
float
NextMotionLeadInSeconds
{
get
;
}
public
float
NextMotionMinDurationSeconds
{
get
;
}
public
IReadOnlyList
<
string
>
ToSubTriggerPriority
{
get
;
}
public
IReadOnlyList
<
string
>
ToBaseTriggerPriority
{
get
;
}
public
IReadOnlyList
<
string
>
ToSubCurrentTriggerPriority
{
get
;
}
public
IReadOnlyList
<
string
>
ToBaseCurrentTriggerPriority
{
get
;
}
public
IReadOnlyList
<
string
>
ToSubNextTriggerPriority
{
get
;
}
public
IReadOnlyList
<
string
>
ToBaseNextTriggerPriority
{
get
;
}
public
IReadOnlyList
<
string
>
DuelStartTriggerPriority
{
get
;
}
private
static
readonly
string
[]
DefaultToSubTriggers
=
{
"Change"
,
"Change1"
,
"ChangePreHide"
,
"Change2"
};
private
static
readonly
string
[]
DefaultToBaseTriggers
=
{
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
};
public
PremiumMateSwapEffect
(
int
baseId
,
float
delaySeconds
,
bool
useChangeMotion
,
string
effectAssetPath
,
bool
useTransitionDelayAsMinimum
=
true
,
float
toSubDelaySeconds
=
-
1f
,
float
toBaseDelaySeconds
=
-
1f
,
IReadOnlyList
<
string
>
toSubTriggerPriority
=
null
,
IReadOnlyList
<
string
>
toBaseTriggerPriority
=
null
,
IReadOnlyList
<
string
>
toSubCurrentTriggerPriority
=
null
,
IReadOnlyList
<
string
>
toBaseCurrentTriggerPriority
=
null
,
IReadOnlyList
<
string
>
toSubNextTriggerPriority
=
null
,
IReadOnlyList
<
string
>
toBaseNextTriggerPriority
=
null
,
IReadOnlyList
<
string
>
duelStartTriggerPriority
=
null
,
string
toSubEffectAssetPath
=
null
,
string
toBaseEffectAssetPath
=
null
,
string
toSubEffectLabel
=
null
,
string
toBaseEffectLabel
=
null
,
bool
preferEffectLabelPlayback
=
false
,
bool
playChangeOnTargetMate
=
false
,
bool
playChangeOnBothMates
=
false
,
bool
preferNonChangeNextWhenChangeTriggerMissing
=
false
,
bool
compensateCurrentTriggerQueueDelay
=
false
,
float
maxCurrentTriggerQueueDelaySeconds
=
0f
,
bool
useUnscaledSwapTiming
=
false
,
float
nextMotionDelaySeconds
=
0f
,
float
sourceToEffectDelaySeconds
=
0f
,
float
nextMotionLeadInSeconds
=
0f
,
float
nextMotionMinDurationSeconds
=
0f
)
{
BaseId
=
baseId
;
DelaySeconds
=
Mathf
.
Max
(
0f
,
delaySeconds
);
ToSubDelaySeconds
=
toSubDelaySeconds
>=
0f
?
toSubDelaySeconds
:
DelaySeconds
;
ToBaseDelaySeconds
=
toBaseDelaySeconds
>=
0f
?
toBaseDelaySeconds
:
DelaySeconds
;
UseChangeMotion
=
useChangeMotion
;
UseTransitionDelayAsMinimum
=
useTransitionDelayAsMinimum
;
EffectAssetPath
=
effectAssetPath
;
ToSubEffectAssetPath
=
toSubEffectAssetPath
??
effectAssetPath
;
ToBaseEffectAssetPath
=
toBaseEffectAssetPath
??
effectAssetPath
;
ToSubEffectLabel
=
toSubEffectLabel
;
ToBaseEffectLabel
=
toBaseEffectLabel
;
PreferEffectLabelPlayback
=
preferEffectLabelPlayback
;
PlayChangeOnTargetMate
=
playChangeOnTargetMate
;
PlayChangeOnBothMates
=
playChangeOnBothMates
;
PreferNonChangeNextWhenChangeTriggerMissing
=
preferNonChangeNextWhenChangeTriggerMissing
;
CompensateCurrentTriggerQueueDelay
=
compensateCurrentTriggerQueueDelay
;
MaxCurrentTriggerQueueDelaySeconds
=
Mathf
.
Max
(
0f
,
maxCurrentTriggerQueueDelaySeconds
);
UseUnscaledSwapTiming
=
useUnscaledSwapTiming
;
NextMotionDelaySeconds
=
Mathf
.
Max
(
0f
,
nextMotionDelaySeconds
);
SourceToEffectDelaySeconds
=
Mathf
.
Max
(
0f
,
sourceToEffectDelaySeconds
);
NextMotionLeadInSeconds
=
Mathf
.
Max
(
0f
,
nextMotionLeadInSeconds
);
NextMotionMinDurationSeconds
=
Mathf
.
Max
(
0f
,
nextMotionMinDurationSeconds
);
ToSubTriggerPriority
=
toSubTriggerPriority
!=
null
&&
toSubTriggerPriority
.
Count
>
0
?
toSubTriggerPriority
.
ToArray
()
:
DefaultToSubTriggers
;
ToBaseTriggerPriority
=
toBaseTriggerPriority
!=
null
&&
toBaseTriggerPriority
.
Count
>
0
?
toBaseTriggerPriority
.
ToArray
()
:
DefaultToBaseTriggers
;
ToSubCurrentTriggerPriority
=
toSubCurrentTriggerPriority
!=
null
&&
toSubCurrentTriggerPriority
.
Count
>
0
?
toSubCurrentTriggerPriority
.
ToArray
()
:
ToSubTriggerPriority
;
ToBaseCurrentTriggerPriority
=
toBaseCurrentTriggerPriority
!=
null
&&
toBaseCurrentTriggerPriority
.
Count
>
0
?
toBaseCurrentTriggerPriority
.
ToArray
()
:
ToBaseTriggerPriority
;
ToSubNextTriggerPriority
=
toSubNextTriggerPriority
!=
null
&&
toSubNextTriggerPriority
.
Count
>
0
?
toSubNextTriggerPriority
.
ToArray
()
:
ToSubTriggerPriority
;
ToBaseNextTriggerPriority
=
toBaseNextTriggerPriority
!=
null
&&
toBaseNextTriggerPriority
.
Count
>
0
?
toBaseNextTriggerPriority
.
ToArray
()
:
ToBaseTriggerPriority
;
DuelStartTriggerPriority
=
duelStartTriggerPriority
!=
null
&&
duelStartTriggerPriority
.
Count
>
0
?
duelStartTriggerPriority
.
ToArray
()
:
ToSubTriggerPriority
;
}
public
string
GetEffectAssetPath
(
bool
toSub
)
{
return
toSub
?
ToSubEffectAssetPath
:
ToBaseEffectAssetPath
;
}
public
bool
HasChangeEffectAsset
(
bool
toSub
)
{
var
effectAssetPath
=
GetEffectAssetPath
(
toSub
);
if
(
string
.
IsNullOrEmpty
(
effectAssetPath
))
return
false
;
var
fullPath
=
Path
.
Combine
(
Program
.
root
,
effectAssetPath
);
return
File
.
Exists
(
fullPath
)
||
Directory
.
Exists
(
fullPath
);
}
}
public
static
class
PremiumMateSwapEffects
{
private
static
readonly
PremiumMateSwapEffect
_default
=
new
(
0
,
0.35f
,
true
,
string
.
Empty
);
private
static
readonly
Dictionary
<
int
,
PremiumMateSwapEffect
>
_effectsByBaseId
=
new
()
{
{
1000020
,
new
PremiumMateSwapEffect
(
1000020
,
0.45f
,
true
,
"MasterDuel/Mate/fxp_14759_change_001"
,
toSubEffectLabel
:
"HumanToDragon"
,
toBaseEffectLabel
:
"DragonToHuman"
,
useUnscaledSwapTiming
:
true
)
},
{
1003001
,
new
PremiumMateSwapEffect
(
1003001
,
1.30f
,
true
,
"MasterDuel/Mate/fxp_04044_change_001"
,
useTransitionDelayAsMinimum
:
false
,
toSubTriggerPriority
:
new
[]
{
"HorseToDragon"
,
"Change"
,
"Change2"
,
"Change1"
,
"ChangePreHide"
},
toBaseTriggerPriority
:
new
[]
{
"DragonToHorse"
,
"Change"
,
"ChangeBack"
,
"Change2"
,
"ChangePreHide"
},
toSubEffectLabel
:
"HorseToDragon"
,
toBaseEffectLabel
:
"DragonToHorse"
,
preferEffectLabelPlayback
:
true
)
},
{
1003002
,
new
PremiumMateSwapEffect
(
1003002
,
0.75f
,
true
,
string
.
Empty
,
useTransitionDelayAsMinimum
:
false
,
toSubTriggerPriority
:
new
[]
{
"Change"
,
"Change2"
,
"Change1"
,
"ChangePreHide"
},
toBaseTriggerPriority
:
new
[]
{
"Change"
,
"ChangeBack"
,
"Change2"
,
"ChangePreHide"
},
toSubCurrentTriggerPriority
:
new
[]
{
"ChangePreHide"
,
"Change1"
,
"Change2"
,
"ChangeBack"
,
"Change"
},
toSubNextTriggerPriority
:
new
[]
{
"Change"
,
"Change2"
,
"Change1"
,
"ChangePreHide"
},
playChangeOnTargetMate
:
true
,
playChangeOnBothMates
:
true
)
},
{
1003003
,
new
PremiumMateSwapEffect
(
1003003
,
1.60f
,
true
,
"MasterDuel/Mate/fxp_M13679_gate_02"
,
toSubDelaySeconds
:
0.90f
,
toBaseDelaySeconds
:
1.95f
,
toBaseEffectAssetPath
:
string
.
Empty
,
toSubEffectLabel
:
"EngageToKagari"
,
toBaseEffectLabel
:
"KagariToEngage"
,
useUnscaledSwapTiming
:
true
,
toSubTriggerPriority
:
new
[]
{
"Change2"
,
"Change1"
,
"Change"
,
"ChangePreHide"
},
toBaseTriggerPriority
:
new
[]
{
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
},
toBaseCurrentTriggerPriority
:
new
[]
{
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
},
toSubNextTriggerPriority
:
new
[]
{
"Change2"
,
"Change1"
,
"Change"
,
"Entry"
,
"Normal"
,
"ChangePreHide"
},
toBaseNextTriggerPriority
:
new
[]
{
"Entry"
,
"Normal"
,
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
},
duelStartTriggerPriority
:
new
[]
{
"Change"
,
"Change2"
,
"Change1"
,
"ChangePreHide"
},
playChangeOnTargetMate
:
false
,
preferNonChangeNextWhenChangeTriggerMissing
:
true
)
},
{
1003004
,
new
PremiumMateSwapEffect
(
1003004
,
2.80f
,
true
,
"MasterDuel/Mate/fxp_M20196_flash"
,
useTransitionDelayAsMinimum
:
false
,
toSubEffectLabel
:
"M20196ToM20215"
,
toBaseEffectLabel
:
"M20215ToM20196"
,
preferEffectLabelPlayback
:
false
,
playChangeOnTargetMate
:
true
,
playChangeOnBothMates
:
true
,
preferNonChangeNextWhenChangeTriggerMissing
:
false
,
compensateCurrentTriggerQueueDelay
:
true
,
maxCurrentTriggerQueueDelaySeconds
:
1.50f
,
useUnscaledSwapTiming
:
true
,
nextMotionDelaySeconds
:
0.45f
,
sourceToEffectDelaySeconds
:
0.18f
,
nextMotionLeadInSeconds
:
0.22f
,
nextMotionMinDurationSeconds
:
1.85f
,
toSubNextTriggerPriority
:
new
[]
{
"Change"
,
"Entry"
,
"Normal"
,
"Change1"
,
"ChangePreHide"
,
"Change2"
},
toBaseNextTriggerPriority
:
new
[]
{
"Change2"
,
"Change"
,
"Entry"
,
"Normal"
,
"ChangeBack"
,
"ChangePreHide"
})
},
{
1003005
,
new
PremiumMateSwapEffect
(
1003005
,
1.35f
,
true
,
string
.
Empty
,
useTransitionDelayAsMinimum
:
false
,
toSubTriggerPriority
:
new
[]
{
"Change"
,
"Change1"
,
"ChangePreHide"
,
"Change2"
},
toBaseTriggerPriority
:
new
[]
{
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
},
toSubCurrentTriggerPriority
:
new
[]
{
"Change"
,
"Change1"
,
"ChangePreHide"
,
"Change2"
},
toBaseCurrentTriggerPriority
:
new
[]
{
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
},
toSubNextTriggerPriority
:
new
[]
{
"Entry"
,
"Normal"
,
"Change"
,
"Change1"
,
"ChangePreHide"
,
"Change2"
},
toBaseNextTriggerPriority
:
new
[]
{
"Entry"
,
"Normal"
,
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
},
playChangeOnTargetMate
:
false
,
playChangeOnBothMates
:
false
)
},
};
public
static
PremiumMateSwapEffect
GetOrDefault
(
int
mateId
)
{
var
baseId
=
PremiumMateRules
.
GetBaseMateId
(
mateId
);
return
_effectsByBaseId
.
TryGetValue
(
baseId
,
out
var
effect
)
?
effect
:
_default
;
}
public
static
bool
TryGet
(
int
mateId
,
out
PremiumMateSwapEffect
effect
)
{
var
baseId
=
PremiumMateRules
.
GetBaseMateId
(
mateId
);
return
_effectsByBaseId
.
TryGetValue
(
baseId
,
out
effect
);
}
}
}
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs.meta
0 → 100644
View file @
d20b6d9e
fileFormatVersion: 2
guid: fd603fb2afd594547a10c88eda27f8dd
\ No newline at end of file
Assets/Scripts/MDPro3/Duel/Message/DuelMessage.cs
View file @
d20b6d9e
...
...
@@ -920,6 +920,13 @@ namespace MDPro3.Duel
card
.
AnimationPositon
();
ES_hint
=
InterString
.
Get
(
"「[?]」特殊召唤宣言时"
,
card
.
GetData
().
Name
);
var
isExtraDeckMonster
=
card
.
GetData
().
HasType
(
CardType
.
Fusion
)
||
card
.
GetData
().
HasType
(
CardType
.
Synchro
)
||
card
.
GetData
().
HasType
(
CardType
.
Xyz
)
||
card
.
GetData
().
HasType
(
CardType
.
Link
);
if
(
isExtraDeckMonster
)
duelBGManager
.
OnSpecialSummonFromExtra
(
gps
.
InMyControl
()
?
0
:
1
);
if
(
materialCards
.
Count
>
0
)
{
if
(
card
.
GetData
().
HasType
(
CardType
.
Link
))
...
...
@@ -1315,6 +1322,9 @@ namespace MDPro3.Duel
else
attackedPosition
=
attackedCard
.
model
.
transform
.
position
;
if
(
directAttack
!=
0
)
duelBGManager
.
OnDirectAttack
(
from
.
InMyControl
()
?
0
:
1
);
var
isFinalAttack
=
duelBGManager
.
IsFinalBlow
();
duelBGManager
.
HideAttackLine
();
duelBGManager
.
HideDuelFinalBlowText
();
...
...
@@ -1516,7 +1526,12 @@ namespace MDPro3.Duel
if
(
life0
<=
0
||
life1
<=
0
)
duelBGManager
.
FinishDamageEffect
();
if
(
currentMessage
==
GameMessage
.
Damage
)
duelBGManager
.
OnPlayerDamaged
(
player
,
Mathf
.
Max
(
value
,
0
));
duelBGManager
.
UpdateBgEffects
(
player
);
duelBGManager
.
OnLifePointsChanged
(
0
,
life0
);
duelBGManager
.
OnLifePointsChanged
(
1
,
life1
);
AudioManager
.
PlaySE
(
"SE_COST_DAMAGE"
);
Core
.
SetLP
(
player
,
-
value
);
await
UniTask
.
WaitForSeconds
(
0.5f
);
...
...
@@ -1543,6 +1558,8 @@ namespace MDPro3.Duel
ES_hint
=
InterString
.
Get
(
"对方生命值回复时"
);
}
duelBGManager
.
OnLifePointsChanged
(
0
,
life0
);
duelBGManager
.
OnLifePointsChanged
(
1
,
life1
);
Core
.
SetLP
(
player
,
value
);
await
UniTask
.
WaitForSeconds
(
0.5f
);
}
...
...
@@ -1568,6 +1585,10 @@ namespace MDPro3.Duel
duelBGManager
.
FinishDamageEffect
();
duelBGManager
.
UpdateBgEffects
(
player
);
if
(
diff
<
0
)
duelBGManager
.
OnPlayerDamaged
(
player
,
-
diff
);
duelBGManager
.
OnLifePointsChanged
(
0
,
life0
);
duelBGManager
.
OnLifePointsChanged
(
1
,
life1
);
if
(
diff
<
0
)
AudioManager
.
PlaySE
(
"SE_COST_DAMAGE"
);
Core
.
SetLP
(
player
,
diff
);
...
...
@@ -1947,6 +1968,7 @@ namespace MDPro3.Duel
opSpSummonCount
=
0
;
turns
++;
myTurn
=
isFirst
?
(
turns
%
2
!=
0
)
:
(
turns
%
2
==
0
);
duelBGManager
.
OnNewTurn
(
myTurn
,
turns
);
PhaseButtonHandler
.
TurnChange
(
myTurn
,
turns
);
PhaseButtonHandler
.
SetTextMain
(
string
.
Empty
);
...
...
@@ -1997,6 +2019,7 @@ namespace MDPro3.Duel
else
if
(
duelPhase
==
DuelPhase
.
End
)
PhaseButtonHandler
.
SetTextMain
(
"End"
);
duelBGManager
.
OnNewPhase
(
player
,
duelPhase
);
await
duelBGManager
.
ShowPhaseBanner
(
player
,
duelPhase
);
}
...
...
@@ -2296,7 +2319,10 @@ namespace MDPro3.Duel
if
(
currentMessage
==
GameMessage
.
CardTarget
)
cardFrom
.
AddTarget
(
cardTo
);
else
if
(
currentMessage
==
GameMessage
.
Equip
)
{
cardFrom
.
equipedCard
=
cardTo
;
duelBGManager
.
OnEquipApplied
(
from
.
InMyControl
()
?
0
:
1
);
}
return
UniTask
.
CompletedTask
;
}
...
...
Assets/Scripts/MDPro3/Game/Mate.cs
View file @
d20b6d9e
using
MDPro3.UI
;
using
System.Collections
;
using
System.Collections.Generic
;
using
System
;
using
UnityEngine
;
using
UnityEngine.Playables
;
using
Willow
;
using
MDPro3.Servant
;
using
MDPro3.UI
;
namespace
MDPro3
{
...
...
@@ -46,6 +47,37 @@ namespace MDPro3
public
PlayableDirector
directorO
;
//o_attack NA BOSS轻攻击
BoxCollider
m_collider
;
SkinnedMeshRenderer
mesh
;
private
readonly
Dictionary
<
Animator
,
HashSet
<
string
>>
masterDuelTriggersByAnimator
=
new
();
private
Animator
[]
masterDuelAnimators
=
Array
.
Empty
<
Animator
>();
private
Animator
masterDuelPrimaryAnimator
;
private
bool
masterDuelInitialized
;
private
bool
masterDuelHasAnyTriggers
;
private
static
readonly
string
[]
masterDuelPrimaryTriggerHints
=
{
"Entry"
,
"Attack"
,
"Damage"
,
"Victory"
,
"Defeat"
,
"Tap"
,
"Tap1"
,
"Tap2"
,
"Random1"
,
"Random2"
,
"Wait2"
,
"Wait3"
,
"Change"
,
"Change1"
,
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
};
private
static
readonly
string
[]
masterDuelTapFallbackStates
=
{
"Tap"
,
"Tap1"
,
"Tap2"
};
private
static
readonly
string
[]
masterDuelRandomFallbackStates
=
{
"Random1"
,
"Random2"
,
"Wait2"
,
"Wait3"
,
"Normal"
};
private
static
readonly
string
[]
masterDuelChangeFallbackStates
=
{
"Change"
,
"Change1"
,
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
};
private
static
readonly
string
[]
ipEntryVoice
=
{
"SE_M14676_c"
,
"SE_M14676_d"
};
private
static
readonly
string
[]
ipTapVoice
=
{
"SE_M14676_i"
,
"SE_M14676_j"
,
"SE_M14676_k"
,
"SE_M14676_d"
};
private
static
readonly
string
[]
ipAttackVoice
=
{
"SE_M14676_e"
,
"SE_M14676_f"
,
"SE_M14676_d1"
,
"SE_M14676_d"
};
private
static
readonly
string
[]
ipDamageVoice
=
{
"SE_M14676_h2"
,
"SE_M14676_h1"
,
"SE_M14676_h"
,
"SE_M14676_d1"
,
"SE_M14676_d"
};
private
static
readonly
string
[]
ipRandomVoice
=
{
"SE_M14676_f"
,
"SE_M14676_g"
,
"SE_M14676_e"
,
"SE_M14676_j"
};
private
static
readonly
string
[]
ipVictoryVoice
=
{
"SE_M14676_k"
,
"SE_M14676_g"
,
"SE_M14676_i"
};
private
static
readonly
string
[]
ipDefeatVoice
=
{
"SE_M14676_d1"
,
"SE_M14676_h2"
,
"SE_M14676_d"
};
private
static
readonly
string
[]
spEntryVoice
=
{
"SE_M15702_c"
,
"SE_M15702_f"
};
private
static
readonly
string
[]
spTapVoice
=
{
"SE_M15702_i"
,
"SE_M15702_j"
,
"SE_M15702_k"
};
private
static
readonly
string
[]
spAttackVoice
=
{
"SE_M15702_d"
,
"SE_M15702_d1"
,
"SE_M15702_g"
};
private
static
readonly
string
[]
spDamageVoice
=
{
"SE_M15702_h2"
,
"SE_M15702_h1"
,
"SE_M15702_h"
};
private
static
readonly
string
[]
spRandomVoice
=
{
"SE_M15702_f"
,
"SE_M15702_g"
,
"SE_M15702_e"
,
"SE_M15702_j"
};
private
static
readonly
string
[]
spVictoryVoice
=
{
"SE_M15702_k"
,
"SE_M15702_i"
,
"SE_M15702_e"
};
private
static
readonly
string
[]
spDefeatVoice
=
{
"SE_M15702_d1"
,
"SE_M15702_h2"
,
"SE_M15702_d"
};
private
Coroutine
masterDuelVoiceFallbackCoroutine
;
private
float
masterDuelVoiceFallbackStartTime
;
bool
Playing
()
{
if
(
directorA
!=
null
&&
directorA
.
state
==
PlayState
.
Playing
)
...
...
@@ -59,8 +91,6 @@ namespace MDPro3
if
(
directorI
!=
null
&&
directorI
.
state
==
PlayState
.
Playing
)
return
true
;
if
(
directorI
!=
null
&&
directorI
.
state
==
PlayState
.
Playing
)
return
true
;
if
(
directorJ
!=
null
&&
directorJ
.
state
==
PlayState
.
Playing
)
return
true
;
if
(
directorK
!=
null
&&
directorK
.
state
==
PlayState
.
Playing
)
...
...
@@ -75,17 +105,129 @@ namespace MDPro3
return
false
;
}
private
void
EnsureMasterDuelInitialized
(
bool
forceRefresh
=
false
)
{
if
(
type
!=
MateType
.
MasterDuel
)
return
;
if
(
forceRefresh
)
masterDuelInitialized
=
false
;
if
(
masterDuelInitialized
)
return
;
if
(
m_collider
==
null
)
{
m_collider
=
gameObject
.
GetComponent
<
BoxCollider
>();
if
(
m_collider
==
null
)
m_collider
=
gameObject
.
AddComponent
<
BoxCollider
>();
}
m_collider
.
size
=
new
Vector3
(
10
,
10
,
10
);
m_collider
.
center
=
new
Vector3
(
0
,
5
,
0
);
masterDuelAnimators
=
transform
.
GetComponentsInChildren
<
Animator
>(
true
);
masterDuelTriggersByAnimator
.
Clear
();
masterDuelPrimaryAnimator
=
null
;
masterDuelHasAnyTriggers
=
false
;
var
bestScore
=
-
1
;
foreach
(
var
animator
in
masterDuelAnimators
)
{
if
(
animator
==
null
)
continue
;
animator
.
cullingMode
=
AnimatorCullingMode
.
AlwaysAnimate
;
animator
.
applyRootMotion
=
false
;
KeepAnimatorStateOnDisable
(
animator
);
if
(
animator
.
gameObject
.
GetComponent
<
EventSEPlayer
>()
==
null
)
animator
.
gameObject
.
AddComponent
<
EventSEPlayer
>();
var
triggerSet
=
new
HashSet
<
string
>(
StringComparer
.
Ordinal
);
foreach
(
var
parameter
in
animator
.
parameters
)
if
(
parameter
.
type
==
AnimatorControllerParameterType
.
Trigger
)
triggerSet
.
Add
(
parameter
.
name
);
masterDuelTriggersByAnimator
[
animator
]
=
triggerSet
;
if
(
triggerSet
.
Count
>
0
)
masterDuelHasAnyTriggers
=
true
;
var
score
=
0
;
for
(
var
i
=
0
;
i
<
masterDuelPrimaryTriggerHints
.
Length
;
i
++)
if
(
triggerSet
.
Contains
(
masterDuelPrimaryTriggerHints
[
i
]))
score
++;
if
(
score
>
bestScore
)
{
bestScore
=
score
;
masterDuelPrimaryAnimator
=
animator
;
}
}
if
(
masterDuelPrimaryAnimator
==
null
&&
masterDuelAnimators
.
Length
>
0
)
masterDuelPrimaryAnimator
=
masterDuelAnimators
[
0
];
masterDuelInitialized
=
true
;
}
private
void
RefreshMasterDuelAnimatorCacheIfNeeded
()
{
if
(
type
!=
MateType
.
MasterDuel
)
return
;
if
(!
masterDuelInitialized
)
{
EnsureMasterDuelInitialized
();
return
;
}
var
stale
=
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
;
if
(!
stale
)
{
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
if
(
masterDuelAnimators
[
i
]
!=
null
)
continue
;
stale
=
true
;
break
;
}
}
if
(!
stale
&&
(
masterDuelPrimaryAnimator
==
null
||
!
masterDuelTriggersByAnimator
.
ContainsKey
(
masterDuelPrimaryAnimator
)))
stale
=
true
;
if
(!
stale
)
return
;
EnsureMasterDuelInitialized
(
forceRefresh
:
true
);
}
public
void
PrepareForPremiumSwapActivation
()
{
if
(
type
!=
MateType
.
MasterDuel
)
return
;
RefreshMasterDuelAnimatorCacheIfNeeded
();
EnsureMasterDuelInitialized
();
if
(
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
)
return
;
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
var
animator
=
masterDuelAnimators
[
i
];
if
(
animator
==
null
)
continue
;
animator
.
enabled
=
true
;
animator
.
cullingMode
=
AnimatorCullingMode
.
AlwaysAnimate
;
animator
.
applyRootMotion
=
false
;
KeepAnimatorStateOnDisable
(
animator
);
if
(!
animator
.
gameObject
.
activeInHierarchy
)
continue
;
animator
.
Rebind
();
animator
.
Update
(
0f
);
}
}
void
Start
()
{
if
(
type
==
MateType
.
MasterDuel
)
{
m_collider
=
gameObject
.
AddComponent
<
BoxCollider
>();
m_collider
.
size
=
new
Vector3
(
10
,
10
,
10
);
m_collider
.
center
=
new
Vector3
(
0
,
5
,
0
);
transform
.
GetChild
(
0
).
gameObject
.
AddComponent
<
EventSEPlayer
>();
var
animator
=
transform
.
GetChild
(
0
).
GetComponent
<
Animator
>();
if
(
animator
!=
null
)
animator
.
cullingMode
=
AnimatorCullingMode
.
AlwaysAnimate
;
EnsureMasterDuelInitialized
();
}
else
{
...
...
@@ -154,47 +296,67 @@ namespace MDPro3
switch
(
type
)
{
case
(
MateType
.
MasterDuel
):
if
(
action
!=
MateAction
.
Entry
&&
!
gameObject
.
activeInHierarchy
)
break
;
switch
(
action
)
{
case
MateAction
.
Entry
:
transform
.
SetParent
(
parent
,
false
);
Tools
.
PlayAnimation
(
transform
,
"Entry"
);
if
(
TryPlayMasterDuelTrigger
(
"Entry"
))
QueueMasterDuelVoiceFallback
(
MateAction
.
Entry
);
break
;
case
MateAction
.
Tap
:
int
i
=
Random
.
Range
(
0
,
3
);
switch
(
i
)
var
tapPriority
=
new
[]
{
"Tap"
,
"Tap1"
,
"Tap2"
};
var
tapStartIndex
=
UnityEngine
.
Random
.
Range
(
0
,
tapPriority
.
Length
);
var
tapped
=
false
;
for
(
var
tapTry
=
0
;
tapTry
<
tapPriority
.
Length
;
tapTry
++)
{
case
0
:
Tools
.
PlayAnimation
(
transform
,
"Tap"
);
break
;
case
1
:
Tools
.
PlayAnimation
(
transform
,
"Tap1"
);
break
;
case
2
:
Tools
.
PlayAnimation
(
transform
,
"Tap2"
);
break
;
var
triggerName
=
tapPriority
[(
tapStartIndex
+
tapTry
)
%
tapPriority
.
Length
];
var
logMissing
=
tapTry
==
tapPriority
.
Length
-
1
;
if
(!
TryPlayMasterDuelTrigger
(
triggerName
,
logMissing
))
continue
;
tapped
=
true
;
break
;
}
if
(
tapped
)
QueueMasterDuelVoiceFallback
(
MateAction
.
Tap
);
break
;
case
MateAction
.
BattlePhase
:
break
;
case
MateAction
.
Attack
:
Tools
.
PlayAnimation
(
transform
,
"Attack"
);
if
(
TryPlayMasterDuelTrigger
(
"Attack"
))
QueueMasterDuelVoiceFallback
(
MateAction
.
Attack
);
break
;
case
MateAction
.
GetDamage
:
Tools
.
PlayAnimation
(
transform
,
"Damage"
);
if
(
TryPlayMasterDuelTrigger
(
"Damage"
))
QueueMasterDuelVoiceFallback
(
MateAction
.
GetDamage
);
break
;
case
MateAction
.
Victory
:
Tools
.
PlayAnimation
(
transform
,
"Victory"
);
if
(
TryPlayMasterDuelTrigger
(
"Victory"
))
QueueMasterDuelVoiceFallback
(
MateAction
.
Victory
);
break
;
case
MateAction
.
Defeat
:
Tools
.
PlayAnimation
(
transform
,
"Defeat"
);
if
(
TryPlayMasterDuelTrigger
(
"Defeat"
))
QueueMasterDuelVoiceFallback
(
MateAction
.
Defeat
);
break
;
case
MateAction
.
Random
:
if
(
Random
.
Range
(
0
,
2
)
>
0.5f
)
Tools
.
PlayAnimation
(
transform
,
"Random1"
);
else
Tools
.
PlayAnimation
(
transform
,
"Random2"
);
var
preferWait3
=
UnityEngine
.
Random
.
value
<
0.2f
;
var
played
=
preferWait3
?
TryPlayMasterDuelTrigger
(
"Wait3"
,
false
)
||
TryPlayMasterDuelTrigger
(
"Wait2"
,
false
)
:
TryPlayMasterDuelTrigger
(
"Wait2"
,
false
)
||
TryPlayMasterDuelTrigger
(
"Wait3"
,
false
);
if
(!
played
)
{
var
preferRandom2
=
UnityEngine
.
Random
.
value
<
0.5f
;
played
=
preferRandom2
?
TryPlayMasterDuelTrigger
(
"Random2"
,
false
)
||
TryPlayMasterDuelTrigger
(
"Random1"
,
false
)
:
TryPlayMasterDuelTrigger
(
"Random1"
,
false
)
||
TryPlayMasterDuelTrigger
(
"Random2"
,
false
);
}
if
(!
played
)
played
=
TryPlayMasterDuelTrigger
(
"Normal"
);
if
(
played
)
QueueMasterDuelVoiceFallback
(
MateAction
.
Random
);
break
;
}
break
;
...
...
@@ -234,7 +396,7 @@ namespace MDPro3
break
;
if
(
directorM
!=
null
&&
directorN
!=
null
&&
directorO
!=
null
)
{
var
random
=
Random
.
Range
(
0
,
3
);
var
random
=
UnityEngine
.
Random
.
Range
(
0
,
3
);
switch
(
random
)
{
case
0
:
...
...
@@ -294,6 +456,95 @@ namespace MDPro3
}
}
private
void
QueueMasterDuelVoiceFallback
(
MateAction
action
)
{
if
(
type
!=
MateType
.
MasterDuel
)
return
;
if
(!
isActiveAndEnabled
)
return
;
if
(!
TryGetMasterDuelVoiceCandidates
(
action
,
out
var
candidates
))
return
;
if
(
masterDuelVoiceFallbackCoroutine
!=
null
)
StopCoroutine
(
masterDuelVoiceFallbackCoroutine
);
masterDuelVoiceFallbackStartTime
=
Time
.
unscaledTime
;
masterDuelVoiceFallbackCoroutine
=
StartCoroutine
(
PlayMasterDuelVoiceFallbackDelayed
(
candidates
,
GetMasterDuelVoicePrefix
(),
masterDuelVoiceFallbackStartTime
));
}
private
IEnumerator
PlayMasterDuelVoiceFallbackDelayed
(
IReadOnlyList
<
string
>
candidates
,
string
expectedPrefix
,
float
startTime
)
{
yield
return
null
;
yield
return
null
;
masterDuelVoiceFallbackCoroutine
=
null
;
if
(
startTime
!=
masterDuelVoiceFallbackStartTime
)
yield
break
;
if
(
EventSEPlayer
.
HasRecentEvent
(
expectedPrefix
,
startTime
))
yield
break
;
if
(
candidates
==
null
||
candidates
.
Count
==
0
)
yield
break
;
var
startIndex
=
UnityEngine
.
Random
.
Range
(
0
,
candidates
.
Count
);
for
(
var
i
=
0
;
i
<
candidates
.
Count
;
i
++)
{
var
candidate
=
candidates
[(
startIndex
+
i
)
%
candidates
.
Count
];
if
(
string
.
IsNullOrEmpty
(
candidate
))
continue
;
AudioManager
.
PlaySE
(
candidate
,
0.4f
);
yield
break
;
}
}
private
string
GetMasterDuelVoicePrefix
()
{
return
code
switch
{
1003005
=>
"SE_M14676_"
,
1003105
=>
"SE_M15702_"
,
_
=>
string
.
Empty
};
}
private
bool
TryGetMasterDuelVoiceCandidates
(
MateAction
action
,
out
IReadOnlyList
<
string
>
candidates
)
{
candidates
=
null
;
if
(
code
==
1003005
)
{
candidates
=
action
switch
{
MateAction
.
Entry
=>
ipEntryVoice
,
MateAction
.
Tap
=>
ipTapVoice
,
MateAction
.
Attack
=>
ipAttackVoice
,
MateAction
.
GetDamage
=>
ipDamageVoice
,
MateAction
.
Random
=>
ipRandomVoice
,
MateAction
.
Victory
=>
ipVictoryVoice
,
MateAction
.
Defeat
=>
ipDefeatVoice
,
_
=>
null
};
return
candidates
!=
null
;
}
if
(
code
==
1003105
)
{
candidates
=
action
switch
{
MateAction
.
Entry
=>
spEntryVoice
,
MateAction
.
Tap
=>
spTapVoice
,
MateAction
.
Attack
=>
spAttackVoice
,
MateAction
.
GetDamage
=>
spDamageVoice
,
MateAction
.
Random
=>
spRandomVoice
,
MateAction
.
Victory
=>
spVictoryVoice
,
MateAction
.
Defeat
=>
spDefeatVoice
,
_
=>
null
};
return
candidates
!=
null
;
}
return
false
;
}
public
void
ActiveCamera
(
MateAction
action
,
int
layerMask
)
{
if
(
action
==
MateAction
.
Entry
)
...
...
@@ -348,5 +599,505 @@ namespace MDPro3
Tools
.
SetAnimatorTimescale
(
transform
,
timeScale
);
}
}
public
bool
PlayChangeTransition
(
bool
toSubForm
)
{
if
(
type
!=
MateType
.
MasterDuel
)
return
false
;
if
(
toSubForm
)
{
return
PlayChangeTransition
(
new
[]
{
"Change"
,
"Change1"
,
"ChangePreHide"
,
"Change2"
});
}
return
PlayChangeTransition
(
new
[]
{
"Change2"
,
"ChangeBack"
,
"ChangePreHide"
,
"Change"
});
}
public
bool
PlayChangeTransition
(
IReadOnlyList
<
string
>
triggerPriority
)
{
return
PlayChangeTransition
(
triggerPriority
,
out
_
);
}
public
bool
PlayChangeTransition
(
IReadOnlyList
<
string
>
triggerPriority
,
out
float
suggestedDelaySeconds
)
{
suggestedDelaySeconds
=
0f
;
if
(
type
!=
MateType
.
MasterDuel
)
return
false
;
if
(
triggerPriority
==
null
||
triggerPriority
.
Count
==
0
)
return
false
;
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
triggerName
=
triggerPriority
[
i
];
if
(
string
.
IsNullOrEmpty
(
triggerName
))
continue
;
if
(
TryPlayMasterDuelTrigger
(
triggerName
,
out
suggestedDelaySeconds
,
false
))
return
true
;
}
return
false
;
}
public
bool
HasMasterDuelTriggerParameter
(
string
triggerName
)
{
if
(
type
!=
MateType
.
MasterDuel
||
string
.
IsNullOrEmpty
(
triggerName
))
return
false
;
RefreshMasterDuelAnimatorCacheIfNeeded
();
EnsureMasterDuelInitialized
();
if
(!
masterDuelHasAnyTriggers
||
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
)
return
false
;
if
(
masterDuelPrimaryAnimator
!=
null
&&
masterDuelTriggersByAnimator
.
TryGetValue
(
masterDuelPrimaryAnimator
,
out
var
primaryTriggers
)
&&
primaryTriggers
.
Contains
(
triggerName
))
return
true
;
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
var
animator
=
masterDuelAnimators
[
i
];
if
(
animator
==
null
)
continue
;
if
(
masterDuelTriggersByAnimator
.
TryGetValue
(
animator
,
out
var
triggers
)
&&
triggers
.
Contains
(
triggerName
))
return
true
;
}
return
false
;
}
public
bool
TryDescribeMasterDuelTransitionCandidates
(
IReadOnlyList
<
string
>
triggerPriority
,
out
string
summary
)
{
summary
=
"none"
;
if
(
type
!=
MateType
.
MasterDuel
)
return
false
;
if
(
triggerPriority
==
null
||
triggerPriority
.
Count
==
0
)
return
false
;
RefreshMasterDuelAnimatorCacheIfNeeded
();
EnsureMasterDuelInitialized
();
var
parts
=
new
List
<
string
>();
var
seen
=
new
HashSet
<
string
>(
StringComparer
.
OrdinalIgnoreCase
);
var
hasAnyCandidate
=
false
;
for
(
var
i
=
0
;
i
<
triggerPriority
.
Count
;
i
++)
{
var
triggerName
=
triggerPriority
[
i
];
if
(
string
.
IsNullOrEmpty
(
triggerName
))
continue
;
if
(!
seen
.
Add
(
triggerName
))
continue
;
var
hasParameter
=
HasMasterDuelTriggerParameter
(
triggerName
);
var
hasStateFallback
=
HasMasterDuelStateFallbackCandidate
(
triggerName
,
out
var
stateCandidate
);
var
hasClipFallback
=
HasMasterDuelClipFallbackCandidate
(
triggerName
,
out
var
clipCandidate
);
if
(
hasParameter
||
hasStateFallback
||
hasClipFallback
)
hasAnyCandidate
=
true
;
var
stateSummary
=
hasStateFallback
?
stateCandidate
:
"-"
;
var
clipSummary
=
hasClipFallback
?
clipCandidate
:
"-"
;
parts
.
Add
(
$"
{
triggerName
}
[param=
{(
hasParameter
?
1
:
0
)}
,state=
{
stateSummary
}
,clip=
{
clipSummary
}
]"
);
}
if
(
parts
.
Count
>
0
)
summary
=
string
.
Join
(
";"
,
parts
);
return
hasAnyCandidate
;
}
public
bool
TryGetMasterDuelPrimaryAnimatorSnapshot
(
out
int
stateHash
,
out
float
normalizedTime
,
out
float
stateLength
,
out
bool
inTransition
,
out
bool
activeInHierarchy
)
{
stateHash
=
0
;
normalizedTime
=
0f
;
stateLength
=
0f
;
inTransition
=
false
;
activeInHierarchy
=
false
;
if
(
type
!=
MateType
.
MasterDuel
)
return
false
;
RefreshMasterDuelAnimatorCacheIfNeeded
();
EnsureMasterDuelInitialized
();
if
(
masterDuelPrimaryAnimator
==
null
)
return
false
;
activeInHierarchy
=
masterDuelPrimaryAnimator
.
gameObject
!=
null
&&
masterDuelPrimaryAnimator
.
gameObject
.
activeInHierarchy
;
if
(
masterDuelPrimaryAnimator
.
layerCount
<=
0
)
return
true
;
var
stateInfo
=
masterDuelPrimaryAnimator
.
GetCurrentAnimatorStateInfo
(
0
);
stateHash
=
stateInfo
.
shortNameHash
;
normalizedTime
=
stateInfo
.
normalizedTime
;
stateLength
=
stateInfo
.
length
;
inTransition
=
masterDuelPrimaryAnimator
.
IsInTransition
(
0
);
return
true
;
}
private
bool
TryPlayMasterDuelTrigger
(
string
triggerName
)
{
return
TryPlayMasterDuelTrigger
(
triggerName
,
out
_
);
}
private
bool
TryPlayMasterDuelTrigger
(
string
triggerName
,
bool
logMissing
)
{
return
TryPlayMasterDuelTrigger
(
triggerName
,
out
_
,
logMissing
);
}
private
bool
TryPlayMasterDuelTrigger
(
string
triggerName
,
out
float
suggestedDelaySeconds
)
{
return
TryPlayMasterDuelTrigger
(
triggerName
,
out
suggestedDelaySeconds
,
true
);
}
private
bool
TryPlayMasterDuelTrigger
(
string
triggerName
,
out
float
suggestedDelaySeconds
,
bool
logMissing
)
{
suggestedDelaySeconds
=
0f
;
if
(
type
!=
MateType
.
MasterDuel
)
return
false
;
RefreshMasterDuelAnimatorCacheIfNeeded
();
EnsureMasterDuelInitialized
();
if
(
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
)
return
false
;
if
(!
masterDuelHasAnyTriggers
)
{
if
(
TryPlayMasterDuelStateFallback
(
triggerName
,
out
suggestedDelaySeconds
))
return
true
;
if
(
TryPlayMasterDuelClipFallback
(
triggerName
,
out
suggestedDelaySeconds
))
return
true
;
return
false
;
}
if
(
masterDuelPrimaryAnimator
!=
null
&&
CanDriveAnimator
(
masterDuelPrimaryAnimator
)
&&
masterDuelTriggersByAnimator
.
TryGetValue
(
masterDuelPrimaryAnimator
,
out
var
primaryTriggers
)
&&
primaryTriggers
.
Contains
(
triggerName
))
{
masterDuelPrimaryAnimator
.
SetTrigger
(
triggerName
);
suggestedDelaySeconds
=
EstimateTriggerDuration
(
masterDuelPrimaryAnimator
,
triggerName
);
return
true
;
}
foreach
(
var
animator
in
masterDuelAnimators
)
{
if
(!
CanDriveAnimator
(
animator
))
continue
;
if
(!
masterDuelTriggersByAnimator
.
TryGetValue
(
animator
,
out
var
triggers
))
continue
;
if
(!
triggers
.
Contains
(
triggerName
))
continue
;
animator
.
SetTrigger
(
triggerName
);
suggestedDelaySeconds
=
EstimateTriggerDuration
(
animator
,
triggerName
);
return
true
;
}
if
(
TryPlayMasterDuelStateFallback
(
triggerName
,
out
suggestedDelaySeconds
))
return
true
;
if
(
TryPlayMasterDuelClipFallback
(
triggerName
,
out
suggestedDelaySeconds
))
return
true
;
return
false
;
}
private
bool
TryPlayMasterDuelStateFallback
(
string
triggerName
,
out
float
suggestedDelaySeconds
)
{
suggestedDelaySeconds
=
0f
;
if
(
string
.
IsNullOrEmpty
(
triggerName
)
||
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
)
return
false
;
var
candidates
=
GetMasterDuelStateFallbackCandidates
(
triggerName
);
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
var
animator
=
masterDuelAnimators
[
i
];
if
(!
CanDriveAnimator
(
animator
))
continue
;
for
(
var
layer
=
0
;
layer
<
animator
.
layerCount
;
layer
++)
{
var
layerName
=
animator
.
GetLayerName
(
layer
);
for
(
var
j
=
0
;
j
<
candidates
.
Length
;
j
++)
{
var
candidate
=
candidates
[
j
];
if
(
string
.
IsNullOrEmpty
(
candidate
))
continue
;
var
shortHash
=
Animator
.
StringToHash
(
candidate
);
var
fullPathHash
=
Animator
.
StringToHash
(
$"
{
layerName
}
.
{
candidate
}
"
);
if
(!
animator
.
HasState
(
layer
,
shortHash
)
&&
!
animator
.
HasState
(
layer
,
fullPathHash
))
continue
;
if
(
animator
.
HasState
(
layer
,
fullPathHash
))
animator
.
Play
(
fullPathHash
,
layer
,
0f
);
else
animator
.
Play
(
shortHash
,
layer
,
0f
);
suggestedDelaySeconds
=
EstimateTriggerDuration
(
animator
,
candidate
);
if
(
suggestedDelaySeconds
<=
0f
)
suggestedDelaySeconds
=
EstimateTriggerDuration
(
animator
,
triggerName
);
if
(
suggestedDelaySeconds
<=
0f
)
{
var
stateInfo
=
animator
.
GetCurrentAnimatorStateInfo
(
layer
);
if
(
stateInfo
.
length
>
0f
)
suggestedDelaySeconds
=
stateInfo
.
length
;
}
if
(
suggestedDelaySeconds
<=
0f
&&
candidate
.
StartsWith
(
"Change"
,
StringComparison
.
OrdinalIgnoreCase
))
suggestedDelaySeconds
=
1.35f
;
return
true
;
}
}
}
return
false
;
}
private
bool
HasMasterDuelStateFallbackCandidate
(
string
triggerName
,
out
string
candidateSummary
)
{
candidateSummary
=
null
;
if
(
string
.
IsNullOrEmpty
(
triggerName
)
||
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
)
return
false
;
var
candidates
=
GetMasterDuelStateFallbackCandidates
(
triggerName
);
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
var
animator
=
masterDuelAnimators
[
i
];
if
(
animator
==
null
)
continue
;
for
(
var
layer
=
0
;
layer
<
animator
.
layerCount
;
layer
++)
{
var
layerName
=
animator
.
GetLayerName
(
layer
);
for
(
var
j
=
0
;
j
<
candidates
.
Length
;
j
++)
{
var
candidate
=
candidates
[
j
];
if
(
string
.
IsNullOrEmpty
(
candidate
))
continue
;
var
shortHash
=
Animator
.
StringToHash
(
candidate
);
var
fullPathHash
=
Animator
.
StringToHash
(
$"
{
layerName
}
.
{
candidate
}
"
);
if
(!
animator
.
HasState
(
layer
,
shortHash
)
&&
!
animator
.
HasState
(
layer
,
fullPathHash
))
continue
;
candidateSummary
=
$"
{
candidate
}
@
{
animator
.
name
}
:L
{
layer
}
"
;
return
true
;
}
}
}
return
false
;
}
private
static
string
[]
GetMasterDuelStateFallbackCandidates
(
string
triggerName
)
{
if
(
string
.
IsNullOrEmpty
(
triggerName
))
return
Array
.
Empty
<
string
>();
var
triggerLower
=
triggerName
.
ToLowerInvariant
();
if
(
triggerLower
==
"tap"
||
triggerLower
==
"tap1"
||
triggerLower
==
"tap2"
)
return
masterDuelTapFallbackStates
;
if
(
triggerLower
==
"wait2"
||
triggerLower
==
"wait3"
||
triggerLower
==
"random1"
||
triggerLower
==
"random2"
||
triggerLower
==
"normal"
)
return
masterDuelRandomFallbackStates
;
if
(
triggerLower
.
StartsWith
(
"change"
,
StringComparison
.
Ordinal
))
{
var
ordered
=
new
List
<
string
>(
masterDuelChangeFallbackStates
.
Length
+
1
);
ordered
.
Add
(
triggerName
);
for
(
var
i
=
0
;
i
<
masterDuelChangeFallbackStates
.
Length
;
i
++)
{
var
candidate
=
masterDuelChangeFallbackStates
[
i
];
if
(
string
.
Equals
(
candidate
,
triggerName
,
StringComparison
.
OrdinalIgnoreCase
))
continue
;
ordered
.
Add
(
candidate
);
}
return
ordered
.
ToArray
();
}
return
new
[]
{
triggerName
};
}
private
bool
TryPlayMasterDuelClipFallback
(
string
triggerName
,
out
float
suggestedDelaySeconds
)
{
suggestedDelaySeconds
=
0f
;
if
(
string
.
IsNullOrEmpty
(
triggerName
))
return
false
;
var
allowGenericFallback
=
triggerName
.
Equals
(
"Tap"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Tap1"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Tap2"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Random1"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Random2"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Wait2"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Wait3"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Normal"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Entry"
,
StringComparison
.
OrdinalIgnoreCase
);
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
var
animator
=
masterDuelAnimators
[
i
];
if
(!
CanDriveAnimator
(
animator
))
continue
;
var
controller
=
animator
.
runtimeAnimatorController
;
if
(
controller
==
null
)
continue
;
AnimationClip
bestClip
=
null
;
AnimationClip
genericClip
=
null
;
var
clips
=
controller
.
animationClips
;
var
triggerLower
=
triggerName
.
ToLowerInvariant
();
for
(
var
j
=
0
;
j
<
clips
.
Length
;
j
++)
{
var
clip
=
clips
[
j
];
if
(
clip
==
null
)
continue
;
var
clipName
=
clip
.
name
.
ToLowerInvariant
();
if
(
allowGenericFallback
)
{
var
looksIdle
=
clipName
.
Contains
(
"idle"
)
||
clipName
.
Contains
(
"wait"
);
if
(!
looksIdle
&&
(
genericClip
==
null
||
clip
.
length
>
genericClip
.
length
))
genericClip
=
clip
;
}
var
matches
=
clipName
.
Contains
(
triggerLower
)
||
((
triggerLower
==
"tap"
||
triggerLower
==
"tap1"
||
triggerLower
==
"tap2"
)
&&
clipName
.
Contains
(
"tap"
))
||
((
triggerLower
==
"wait2"
||
triggerLower
==
"wait3"
||
triggerLower
==
"random1"
||
triggerLower
==
"random2"
)
&&
(
clipName
.
Contains
(
"wait"
)
||
clipName
.
Contains
(
"random"
)))
||
(
triggerLower
.
StartsWith
(
"change"
,
StringComparison
.
Ordinal
)
&&
clipName
.
Contains
(
"change"
));
if
(!
matches
)
continue
;
if
(
bestClip
==
null
||
clip
.
length
>
bestClip
.
length
)
bestClip
=
clip
;
}
if
(
bestClip
==
null
)
bestClip
=
genericClip
;
if
(
bestClip
==
null
)
continue
;
animator
.
Play
(
bestClip
.
name
,
0
,
0f
);
suggestedDelaySeconds
=
bestClip
.
length
;
return
true
;
}
return
false
;
}
private
bool
HasMasterDuelClipFallbackCandidate
(
string
triggerName
,
out
string
clipName
)
{
clipName
=
null
;
if
(
string
.
IsNullOrEmpty
(
triggerName
)
||
masterDuelAnimators
==
null
||
masterDuelAnimators
.
Length
==
0
)
return
false
;
var
allowGenericFallback
=
triggerName
.
Equals
(
"Tap"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Tap1"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Tap2"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Random1"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Random2"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Wait2"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Wait3"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Normal"
,
StringComparison
.
OrdinalIgnoreCase
)
||
triggerName
.
Equals
(
"Entry"
,
StringComparison
.
OrdinalIgnoreCase
);
var
triggerLower
=
triggerName
.
ToLowerInvariant
();
for
(
var
i
=
0
;
i
<
masterDuelAnimators
.
Length
;
i
++)
{
var
animator
=
masterDuelAnimators
[
i
];
if
(
animator
==
null
)
continue
;
var
controller
=
animator
.
runtimeAnimatorController
;
if
(
controller
==
null
)
continue
;
AnimationClip
bestClip
=
null
;
AnimationClip
genericClip
=
null
;
var
clips
=
controller
.
animationClips
;
for
(
var
j
=
0
;
j
<
clips
.
Length
;
j
++)
{
var
clip
=
clips
[
j
];
if
(
clip
==
null
)
continue
;
var
clipLower
=
clip
.
name
.
ToLowerInvariant
();
if
(
allowGenericFallback
)
{
var
looksIdle
=
clipLower
.
Contains
(
"idle"
)
||
clipLower
.
Contains
(
"wait"
);
if
(!
looksIdle
&&
(
genericClip
==
null
||
clip
.
length
>
genericClip
.
length
))
genericClip
=
clip
;
}
var
matches
=
clipLower
.
Contains
(
triggerLower
)
||
((
triggerLower
==
"tap"
||
triggerLower
==
"tap1"
||
triggerLower
==
"tap2"
)
&&
clipLower
.
Contains
(
"tap"
))
||
((
triggerLower
==
"wait2"
||
triggerLower
==
"wait3"
||
triggerLower
==
"random1"
||
triggerLower
==
"random2"
)
&&
(
clipLower
.
Contains
(
"wait"
)
||
clipLower
.
Contains
(
"random"
)))
||
(
triggerLower
.
StartsWith
(
"change"
,
StringComparison
.
Ordinal
)
&&
clipLower
.
Contains
(
"change"
));
if
(!
matches
)
continue
;
if
(
bestClip
==
null
||
clip
.
length
>
bestClip
.
length
)
bestClip
=
clip
;
}
if
(
bestClip
==
null
)
bestClip
=
genericClip
;
if
(
bestClip
==
null
)
continue
;
clipName
=
bestClip
.
name
;
return
true
;
}
return
false
;
}
private
static
bool
CanDriveAnimator
(
Animator
animator
)
{
return
animator
!=
null
&&
animator
.
enabled
&&
animator
.
gameObject
!=
null
&&
animator
.
gameObject
.
activeInHierarchy
;
}
private
static
float
EstimateTriggerDuration
(
Animator
animator
,
string
triggerName
)
{
if
(
animator
==
null
||
string
.
IsNullOrEmpty
(
triggerName
))
return
0f
;
var
controller
=
animator
.
runtimeAnimatorController
;
if
(
controller
==
null
)
return
0f
;
var
triggerLower
=
triggerName
.
ToLowerInvariant
();
var
best
=
0f
;
var
clips
=
controller
.
animationClips
;
for
(
var
i
=
0
;
i
<
clips
.
Length
;
i
++)
{
var
clip
=
clips
[
i
];
if
(
clip
==
null
)
continue
;
var
clipName
=
clip
.
name
.
ToLowerInvariant
();
var
matches
=
clipName
.
Contains
(
triggerLower
)
||
(
triggerLower
.
StartsWith
(
"change"
,
StringComparison
.
Ordinal
)
&&
clipName
.
Contains
(
"change"
))
||
(
triggerLower
==
"wait2"
&&
(
clipName
.
Contains
(
"wait2"
)
||
clipName
.
Contains
(
"random1"
)))
||
(
triggerLower
==
"wait3"
&&
(
clipName
.
Contains
(
"wait3"
)
||
clipName
.
Contains
(
"random2"
)));
if
(!
matches
)
continue
;
if
(
clip
.
length
>
best
)
best
=
clip
.
length
;
}
if
(
best
<=
0f
&&
triggerLower
.
StartsWith
(
"change"
,
StringComparison
.
Ordinal
))
return
1.35f
;
return
best
;
}
private
static
void
KeepAnimatorStateOnDisable
(
Animator
animator
)
{
var
type
=
typeof
(
Animator
);
var
keepController
=
type
.
GetProperty
(
"keepAnimatorControllerStateOnDisable"
);
if
(
keepController
!=
null
&&
keepController
.
PropertyType
==
typeof
(
bool
)
&&
keepController
.
CanWrite
)
{
keepController
.
SetValue
(
animator
,
true
,
null
);
return
;
}
var
keepState
=
type
.
GetProperty
(
"keepAnimatorStateOnDisable"
);
if
(
keepState
!=
null
&&
keepState
.
PropertyType
==
typeof
(
bool
)
&&
keepState
.
CanWrite
)
keepState
.
SetValue
(
animator
,
true
,
null
);
}
}
}
Assets/Scripts/MDPro3/Game/Tools.cs
View file @
d20b6d9e
...
...
@@ -55,7 +55,23 @@ namespace MDPro3
Animator
[]
animators
=
animationContainer
.
GetComponentsInChildren
<
Animator
>();
foreach
(
Animator
animator
in
animators
)
{
animator
.
SetTrigger
(
animationName
);
if
(
animator
==
null
||
string
.
IsNullOrEmpty
(
animationName
))
continue
;
var
hasTrigger
=
false
;
var
parameters
=
animator
.
parameters
;
for
(
var
i
=
0
;
i
<
parameters
.
Length
;
i
++)
{
var
param
=
parameters
[
i
];
if
(
param
.
type
==
AnimatorControllerParameterType
.
Trigger
&&
param
.
name
==
animationName
)
{
hasTrigger
=
true
;
break
;
}
}
if
(
hasTrigger
)
animator
.
SetTrigger
(
animationName
);
}
}
...
...
@@ -224,7 +240,7 @@ namespace MDPro3
}
else
{
UnityEngine
.
Debug
.
LogErrorFormat
(
$
"Image [
{
0
}
]:
{
1
}
"
,
url
,
request
.
error
);
UnityEngine
.
Debug
.
LogErrorFormat
(
"Image [{0}]: {1}"
,
url
,
request
.
error
);
return
null
;
}
}
...
...
@@ -384,4 +400,4 @@ namespace MDPro3
#
endregion
}
}
\ No newline at end of file
}
Assets/Scripts/MDPro3/Helper/ABLoader.cs
View file @
d20b6d9e
...
...
@@ -69,23 +69,36 @@ namespace MDPro3
}
AssetBundle
ab
=
await
AssetBundle
.
LoadFromFileAsync
(
Program
.
root
+
path
);
var
assets
=
ab
.
LoadAllAssets
();
var
expectedName
=
Path
.
GetFileName
(
path
);
if
(!
string
.
IsNullOrEmpty
(
expectedName
))
{
var
assetRequest
=
ab
.
LoadAssetAsync
<
GameObject
>(
expectedName
);
await
assetRequest
;
returnValue
=
assetRequest
.
asset
as
GameObject
;
}
foreach
(
UnityEngine
.
Object
asset
in
assets
)
if
(
returnValue
==
null
)
{
if
(
typeof
(
GameObject
).
IsInstanceOfType
(
asset
))
var
assets
=
ab
.
LoadAllAssets
();
foreach
(
UnityEngine
.
Object
asset
in
assets
)
{
if
(
cache
)
{
if
(!
cachedAB
.
TryAdd
(
path
,
asset
as
GameObject
))
Debug
.
LogWarning
(
$"Failed to cache
{
path
}
"
)
;
}
returnValue
=
asset
as
GameObject
;
//
break;
if
(
!
typeof
(
GameObject
).
IsInstanceOfType
(
asset
)
)
continue
;
var
candidate
=
asset
as
GameObject
;
returnValue
=
candidate
;
if
(
candidate
!=
null
&&
candidate
.
name
==
expectedName
)
break
;
}
}
ab
.
Unload
(
false
);
if
(
cache
&&
returnValue
!=
null
)
{
if
(!
cachedAB
.
TryAdd
(
path
,
returnValue
))
Debug
.
LogWarning
(
$"Failed to cache
{
path
}
"
);
}
if
(
instantiate
&&
returnValue
!=
null
)
return
UnityEngine
.
Object
.
Instantiate
(
returnValue
);
else
...
...
@@ -590,4 +603,4 @@ namespace MDPro3
#
endregion
}
}
\ No newline at end of file
}
Assets/Scripts/MDPro3/ScriptableObjects/Items.cs
View file @
d20b6d9e
...
...
@@ -100,7 +100,8 @@ namespace MDPro3
public
List
<
Item
>
wallpapers
;
// 113
public
List
<
List
<
Item
>>
kinds
;
private
const
string
ADDRESS_DEFAULT_DECK_CASE
=
"DeckCase0001_L"
;
private
const
int
CODE_DEFAULT_DECK_CASE
=
1080001
;
private
const
string
ADDRESS_DEFAULT_DECK_CASE_LEGACY
=
"DeckCase0001_L"
;
public
const
string
STRING_NULL
=
"coming soon"
;
public
const
int
CODE_NONE
=
0
;
...
...
@@ -627,15 +628,35 @@ namespace MDPro3
}
public
async
UniTask
<
Sprite
>
LoadDeckCaseIconAsync
(
int
code
,
string
suffix
)
{
var
sprite
=
await
TryLoadAddressableSprite
(
GetDeckCaseAddress
(
code
,
suffix
));
if
(
sprite
!=
null
)
return
sprite
;
sprite
=
await
TryLoadAddressableSprite
(
GetDeckCaseAddress
(
CODE_DEFAULT_DECK_CASE
,
suffix
));
if
(
sprite
!=
null
)
return
sprite
;
return
await
TryLoadAddressableSprite
(
ADDRESS_DEFAULT_DECK_CASE_LEGACY
);
}
private
static
string
GetDeckCaseAddress
(
int
code
,
string
suffix
)
{
if
(
code
<
1080000
||
code
>
1089999
)
code
=
CODE_DEFAULT_DECK_CASE
;
return
$"DeckCase
{
code
.
ToString
()[
3.
.]}{
suffix
??
string
.
Empty
}
"
;
}
private
async
UniTask
<
Sprite
>
TryLoadAddressableSprite
(
string
address
)
{
try
{
return
await
LoadAddressableSprite
(
$"DeckCase
{
code
.
ToString
()[
3.
.]}{
suffix
}
"
);
return
await
LoadAddressableSprite
(
address
);
}
catch
{
Debug
.
LogError
(
"Addressables Not Found: "
+
$"DeckCase
{
code
}
_
{
suffix
}
"
);
return
await
LoadAddressableSprite
(
ADDRESS_DEFAULT_DECK_CASE
);
return
null
;
}
}
...
...
Assets/Scripts/MDPro3/UI/Function/EventSEPlayer.cs
View file @
d20b6d9e
using
UnityEngine
;
using
System
;
namespace
MDPro3.UI
{
public
class
EventSEPlayer
:
MonoBehaviour
{
public
static
float
LastEventTime
{
get
;
private
set
;
}
=
float
.
NegativeInfinity
;
public
static
string
LastEventLabel
{
get
;
private
set
;
}
=
string
.
Empty
;
void
PlayAnimationEventSe
(
string
se
)
{
RegisterEvent
(
se
);
AudioManager
.
PlaySE
(
se
,
0.4f
);
}
void
NewEvent
(
string
se
)
{
RegisterEvent
(
se
);
AudioManager
.
PlaySE
(
se
,
0.4f
);
}
public
static
bool
HasRecentEvent
(
string
expectedPrefix
,
float
sinceTime
)
{
if
(
LastEventTime
<
sinceTime
)
return
false
;
if
(
string
.
IsNullOrEmpty
(
expectedPrefix
))
return
true
;
return
!
string
.
IsNullOrEmpty
(
LastEventLabel
)
&&
LastEventLabel
.
StartsWith
(
expectedPrefix
,
StringComparison
.
OrdinalIgnoreCase
);
}
private
static
void
RegisterEvent
(
string
se
)
{
LastEventTime
=
Time
.
unscaledTime
;
LastEventLabel
=
se
??
string
.
Empty
;
}
}
}
Assets/Scripts/MDPro3/UI/SelectionButton/SelectionToggle_AppearanceItem.cs
View file @
d20b6d9e
...
...
@@ -10,6 +10,7 @@ using MDPro3.Servant;
using
MDPro3.UI.ServantUI
;
using
MDPro3.Utility
;
using
Cysharp.Threading.Tasks
;
using
MDPro3.Duel
;
namespace
MDPro3.UI
{
...
...
@@ -28,6 +29,7 @@ namespace MDPro3.UI
m_Protector
=
m_Protector
!=
null
?
m_Protector
:
Manager
.
GetElement
<
RawImage
>(
LABEL_RIMG_PROTECTOR
);
private
const
string
LABEL_IMG_WALLPAPER_BG
=
"WallpaperBG"
;
private
Image
m_WallpaperBG
;
private
Image
WallpaperBG
=>
...
...
@@ -52,6 +54,11 @@ namespace MDPro3.UI
private
Coroutine
refreshCoroutine
;
private
Coroutine
hideCoroutine
;
private
Image
premiumOverlayIcon
;
private
Coroutine
premiumCrossfadeCoroutine
;
private
const
float
CrossfadeHoldSeconds
=
2.0f
;
private
const
float
CrossfadeFadeSeconds
=
0.6f
;
protected
override
void
Awake
()
{
base
.
Awake
();
...
...
@@ -131,6 +138,7 @@ namespace MDPro3.UI
Icon
.
material
=
Appearance
.
matForFace
;
loaded
=
true
;
StartPremiumCrossfade
();
}
catch
(
OperationCanceledException
)
{
...
...
@@ -208,9 +216,10 @@ namespace MDPro3.UI
}
else
{
if
(
DeckEditor
.
Deck
.
Mate
!=
itemID
)
var
normalizedMateId
=
PremiumMateRules
.
GetBaseMateId
(
itemID
);
if
(
DeckEditor
.
Deck
.
Mate
!=
normalizedMateId
)
{
DeckEditor
.
Deck
.
Mate
=
itemID
;
DeckEditor
.
Deck
.
Mate
=
normalizedMateId
;
Program
.
instance
.
deckEditor
.
GetUI
<
DeckEditorUI
>().
DeckView
.
SetDirty
(
true
);
Program
.
instance
.
deckEditor
.
GetUI
<
DeckEditorUI
>().
IconMate
.
sprite
=
Icon
.
sprite
;
}
...
...
@@ -220,6 +229,9 @@ namespace MDPro3.UI
{
if
(
AppearanceUI
.
currentContent
==
"Wallpaper"
)
Config
.
Set
(
"Wallpaper"
,
itemID
.
ToString
());
else
if
(
AppearanceUI
.
currentContent
==
"Mate"
)
Config
.
Set
(
Appearance
.
condition
.
ToString
()
+
AppearanceUI
.
currentContent
+
Appearance
.
player
,
PremiumMateRules
.
GetBaseMateId
(
itemID
).
ToString
());
else
Config
.
Set
(
Appearance
.
condition
.
ToString
()
+
AppearanceUI
.
currentContent
+
Appearance
.
player
,
itemID
.
ToString
());
...
...
@@ -384,6 +396,7 @@ namespace MDPro3.UI
if
(
hideCoroutine
!=
null
||
!
gameObject
.
activeSelf
)
return
;
hideCoroutine
=
StartCoroutine
(
HideAsync
());
StopPremiumCrossfade
();
GetComponent
<
LayoutElement
>().
ignoreLayout
=
true
;
GetComponent
<
RectTransform
>().
anchoredPosition
=
Vector2
.
zero
;
...
...
@@ -412,10 +425,13 @@ namespace MDPro3.UI
GetComponent
<
LayoutElement
>().
ignoreLayout
=
false
;
transform
.
SetSiblingIndex
(
index
);
StartPremiumCrossfade
();
}
public
void
Dispose
()
{
StopPremiumCrossfade
();
if
(
refreshCoroutine
!=
null
)
StopCoroutine
(
refreshCoroutine
);
...
...
@@ -424,5 +440,150 @@ namespace MDPro3.UI
Destroy
(
gameObject
);
}
#
region
Premium
Mate
Crossfade
private
void
StartPremiumCrossfade
()
{
StopPremiumCrossfade
();
if
(!
loaded
)
return
;
if
(
AppearanceUI
.
currentContent
!=
"Mate"
)
return
;
if
(!
PremiumMateRules
.
IsPremiumBaseId
(
itemID
))
return
;
if
(
Icon
==
null
||
!
Icon
.
gameObject
.
activeSelf
)
return
;
premiumCrossfadeCoroutine
=
StartCoroutine
(
PremiumCrossfadeAsync
());
}
private
void
StopPremiumCrossfade
()
{
if
(
premiumCrossfadeCoroutine
!=
null
)
{
StopCoroutine
(
premiumCrossfadeCoroutine
);
premiumCrossfadeCoroutine
=
null
;
}
if
(
premiumOverlayIcon
!=
null
)
{
Destroy
(
premiumOverlayIcon
.
gameObject
);
premiumOverlayIcon
=
null
;
}
if
(
Icon
!=
null
)
{
var
c
=
Icon
.
color
;
c
.
a
=
1f
;
Icon
.
color
=
c
;
}
}
private
Image
CreateOverlayIcon
()
{
var
overlayGo
=
new
GameObject
(
"PremiumOverlay"
);
overlayGo
.
transform
.
SetParent
(
Icon
.
transform
.
parent
,
false
);
overlayGo
.
transform
.
SetSiblingIndex
(
Icon
.
transform
.
GetSiblingIndex
()
+
1
);
var
overlayImg
=
overlayGo
.
AddComponent
<
Image
>();
overlayImg
.
raycastTarget
=
false
;
overlayImg
.
preserveAspect
=
Icon
.
preserveAspect
;
overlayImg
.
type
=
Icon
.
type
;
overlayImg
.
maskable
=
Icon
.
maskable
;
var
overlayRt
=
overlayImg
.
rectTransform
;
var
iconRt
=
Icon
.
rectTransform
;
overlayRt
.
anchorMin
=
iconRt
.
anchorMin
;
overlayRt
.
anchorMax
=
iconRt
.
anchorMax
;
overlayRt
.
pivot
=
iconRt
.
pivot
;
overlayRt
.
anchoredPosition
=
iconRt
.
anchoredPosition
;
overlayRt
.
sizeDelta
=
iconRt
.
sizeDelta
;
overlayRt
.
localScale
=
iconRt
.
localScale
;
overlayRt
.
localRotation
=
iconRt
.
localRotation
;
var
c
=
Color
.
white
;
c
.
a
=
0f
;
overlayImg
.
color
=
c
;
return
overlayImg
;
}
private
IEnumerator
PremiumCrossfadeAsync
()
{
if
(!
PremiumMateRules
.
TryGetRuleByBaseId
(
itemID
,
out
var
rule
))
yield
break
;
Sprite
subSprite
=
null
;
foreach
(
var
variantId
in
rule
.
VariantIds
)
{
var
task
=
Program
.
items
.
LoadItemIconAsync
(
variantId
.
ToString
(),
Items
.
ItemType
.
Mate
);
while
(
task
.
Status
==
UniTaskStatus
.
Pending
)
yield
return
null
;
try
{
subSprite
=
task
.
GetAwaiter
().
GetResult
();
if
(
subSprite
!=
null
)
break
;
}
catch
{
// Icon not available for this variant, try next
}
}
if
(
subSprite
==
null
||
this
==
null
||
Icon
==
null
)
yield
break
;
premiumOverlayIcon
=
CreateOverlayIcon
();
premiumOverlayIcon
.
sprite
=
subSprite
;
// Icon shows base (alpha=1), overlay shows sub (alpha=0) initially.
// Crossfade loop: hold → fade overlay in → hold → fade overlay out → repeat.
while
(
true
)
{
// Hold on base icon
yield
return
new
WaitForSecondsRealtime
(
CrossfadeHoldSeconds
);
// Fade in overlay (base → sub)
yield
return
FadeOverlay
(
0f
,
1f
,
CrossfadeFadeSeconds
);
// Hold on sub icon
yield
return
new
WaitForSecondsRealtime
(
CrossfadeHoldSeconds
);
// Fade out overlay (sub → base)
yield
return
FadeOverlay
(
1f
,
0f
,
CrossfadeFadeSeconds
);
}
}
private
IEnumerator
FadeOverlay
(
float
fromAlpha
,
float
toAlpha
,
float
duration
)
{
if
(
premiumOverlayIcon
==
null
)
yield
break
;
var
elapsed
=
0f
;
while
(
elapsed
<
duration
)
{
elapsed
+=
Time
.
unscaledDeltaTime
;
var
t
=
Mathf
.
Clamp01
(
elapsed
/
duration
);
t
=
t
*
t
*
(
3f
-
2f
*
t
);
// smoothstep
var
alpha
=
Mathf
.
Lerp
(
fromAlpha
,
toAlpha
,
t
);
if
(
premiumOverlayIcon
!=
null
)
{
var
c
=
premiumOverlayIcon
.
color
;
c
.
a
=
alpha
;
premiumOverlayIcon
.
color
=
c
;
}
yield
return
null
;
}
if
(
premiumOverlayIcon
!=
null
)
{
var
c
=
premiumOverlayIcon
.
color
;
c
.
a
=
toAlpha
;
premiumOverlayIcon
.
color
=
c
;
}
}
#
endregion
}
}
Assets/Scripts/MDPro3/UI/ServantUI/AppearanceUI.cs
View file @
d20b6d9e
...
...
@@ -8,6 +8,7 @@ using UnityEngine.EventSystems;
using
UnityEngine.UI
;
using
static
MDPro3
.
Servant
.
Appearance
;
using
static
YgomGame
.
Duel
.
BattleAimingEffect
;
using
MDPro3.Duel
;
namespace
MDPro3.UI.ServantUI
{
...
...
@@ -390,6 +391,8 @@ namespace MDPro3.UI.ServantUI
int
itemCount
=
0
;
foreach
(
var
itemInfo
in
targetItems
)
{
if
(
currentContent
==
"Mate"
&&
PremiumMateRules
.
IsPremiumVariantId
(
itemInfo
.
id
))
continue
;
if
(
itemInfo
.
notReady
)
continue
;
GameObject
item
=
Instantiate
(
Template
);
...
...
@@ -520,44 +523,68 @@ namespace MDPro3.UI.ServantUI
foreach
(
var
item
in
currentList
)
{
var
itemMono
=
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>();
if
(
currentContent
==
"Mate"
&&
PremiumMateRules
.
IsPremiumVariantId
(
itemMono
.
itemID
))
{
itemMono
.
Hide
();
continue
;
}
if
(
player
.
Contains
(
"0"
)
&&
onlyOpSideShowItems
.
Contains
(
item
))
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>()
.
Hide
();
item
Mono
.
Hide
();
else
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>()
.
Show
();
item
Mono
.
Show
();
}
foreach
(
var
item
in
currentList
)
{
var
itemMono
=
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>();
if
(
currentContent
==
"Wallpaper"
)
{
if
(
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>()
.
itemID
.
ToString
()
==
Config
.
Get
(
"Wallpaper"
,
targetItems
[
0
].
id
.
ToString
()))
if
(
item
Mono
.
itemID
.
ToString
()
==
Config
.
Get
(
"Wallpaper"
,
targetItems
[
0
].
id
.
ToString
()))
{
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>()
.
SetToggleOn
();
item
Mono
.
SetToggleOn
();
break
;
}
}
else
{
var
itemID
=
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>().
itemID
;
var
itemID
=
itemMono
.
itemID
;
if
(
currentContent
==
"Mate"
&&
PremiumMateRules
.
IsPremiumVariantId
(
itemID
))
continue
;
if
(
condition
==
Condition
.
DeckEditor
)
{
if
(
itemID
==
DeckEditor
.
Deck
.
Case
if
(
currentContent
==
"Mate"
)
{
var
selectedMate
=
PremiumMateRules
.
GetBaseMateId
(
DeckEditor
.
Deck
.
Mate
);
if
(
itemID
==
selectedMate
)
{
itemMono
.
SetToggleOn
();
break
;
}
}
else
if
(
itemID
==
DeckEditor
.
Deck
.
Case
||
itemID
==
DeckEditor
.
Deck
.
Protector
||
itemID
==
DeckEditor
.
Deck
.
Field
||
itemID
==
DeckEditor
.
Deck
.
Grave
||
itemID
==
DeckEditor
.
Deck
.
Stand
||
itemID
==
DeckEditor
.
Deck
.
Mate
)
{
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>()
.
SetToggleOn
();
break
;
itemMono
.
SetToggleOn
();
break
;
}
}
else
{
if
(
itemID
.
ToString
()
==
Config
.
Get
(
condition
.
ToString
()
+
currentContent
+
player
,
targetItems
[
0
].
id
.
ToString
()))
var
selectedCode
=
Config
.
Get
(
condition
.
ToString
()
+
currentContent
+
player
,
targetItems
[
0
].
id
.
ToString
());
if
(
currentContent
==
"Mate"
&&
int
.
TryParse
(
selectedCode
,
out
var
selectedMateCode
))
selectedCode
=
PremiumMateRules
.
GetBaseMateId
(
selectedMateCode
).
ToString
();
if
(
itemID
.
ToString
()
==
selectedCode
)
{
item
.
GetComponent
<
SelectionToggle_AppearanceItem
>()
.
SetToggleOn
();
item
Mono
.
SetToggleOn
();
break
;
}
}
...
...
@@ -618,4 +645,4 @@ namespace MDPro3.UI.ServantUI
}
}
}
\ 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