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
eea6cdcb
Commit
eea6cdcb
authored
Mar 13, 2026
by
ElderLich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feature: Implemented Deluxe Mates like in MD
parent
2046e5d8
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
2869 additions
and
240 deletions
+2869
-240
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
+17
-1
Assets/Scripts/MDPro3/Helper/ABLoader.cs
Assets/Scripts/MDPro3/Helper/ABLoader.cs
+24
-11
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 @
eea6cdcb
...
...
@@ -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 @
eea6cdcb
...
...
@@ -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 @
eea6cdcb
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 @
eea6cdcb
fileFormatVersion: 2
guid: 555a55c03fd6e3d45bc341e9808a721c
\ No newline at end of file
Assets/Scripts/MDPro3/Duel/BG/PremiumMateSwapEffects.cs
0 → 100644
View file @
eea6cdcb
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 @
eea6cdcb
fileFormatVersion: 2
guid: fd603fb2afd594547a10c88eda27f8dd
\ No newline at end of file
Assets/Scripts/MDPro3/Duel/Message/DuelMessage.cs
View file @
eea6cdcb
...
...
@@ -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 @
eea6cdcb
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 @
eea6cdcb
...
...
@@ -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
);
}
}
...
...
Assets/Scripts/MDPro3/Helper/ABLoader.cs
View file @
eea6cdcb
...
...
@@ -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/UI/Function/EventSEPlayer.cs
View file @
eea6cdcb
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 @
eea6cdcb
...
...
@@ -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 @
eea6cdcb
...
...
@@ -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