diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index 0b0c350..c7e54e4 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -12,21 +12,62 @@ internal enum AnimationClip {
SitLook2,
SitLook3,
SitLook4,
+ SitLean,
+ SitLookLeft,
+ SittingAsleep,
GetOff,
+
Land,
+ HardLand,
+ HardLandGreymoor,
+ HardLandQuick,
+ SuperHardLand,
+ LandToSprint,
+
+ Hop,
+ HopLand,
+ HopToSomersault,
+
Run,
RunQ,
RunToIdle,
+ RunToIdleC,
+
Idle,
+ IdleBackward,
+ IdleBG,
+ IdleHurtListen,
+ IdleHurtListenBackward,
+ IdleHurtListenWindy,
+ IdleHurtNoNeedle,
+ IdleHurtTalk,
+ IdleHurtTalkBackward,
+ IdleHurtTalkTurnBackward,
+ IdleHurtTalkTurnForward,
+ IdleHurtTalkWindy,
+ IdleHurtWindy,
+ IdleSlap,
+ IdleToRunShort,
+ IdleToRunWeak,
+ IdleWindy,
IdleToRun,
+
WalkToIdle,
RunToWalk,
+ RunToWalkWeak,
LandToWalk,
Turn,
Dash,
DashDown,
DashDownLand,
+ DashDownEnd,
+ //DashStabEffect,
+ //DashStabEffectGlow,
SprintLv2,
+ SprintLv3,
+ SprintRecoil,
+ SprintSkidToRun,
+ SprintToRun,
MantleCling,
MantleLand,
MantleLandToIdle,
@@ -38,10 +79,13 @@ internal enum AnimationClip {
SprintAir,
SprintAirLoop,
DoubleJump,
+ DoubleJumpWings2,
LandToRun,
DashAttackAntic,
DashAttack,
DashAttackRecover,
+ DashAttackAnticLong,
+ DashAttackAnticShort,
SkidEnd1,
SkidEnd2,
DashToIdle,
@@ -53,6 +97,7 @@ internal enum AnimationClip {
SlashLandRunAlt,
DownSpikeAntic,
DownSpike,
+ DownSpikeBurst,
DownSpikeRecovery,
DownSpikeRecoveryLand,
DownSpikeFollowup,
@@ -65,20 +110,62 @@ internal enum AnimationClip {
SlashChargedLoop,
UmbrellaInflateAntic,
WallJump,
+ WallJumpAntic,
+ WallJumpPuff,
+ WallJumpSomersault,
+ Wallrun,
+ WallrunAntic,
WallSlide,
+ SlideBrake,
+ SlideBrakeEnd,
+ SlideFast,
+ SlideFastEnd,
+ SlideNormal,
+ SlideStart,
+
WallCling,
SitFallAsleep,
+
UmbrellaInflate,
UmbrellaFloat,
UmbrellaDeflate,
+ UmbrellaTurn,
+
IdleRest,
MantleLandToRun,
Sit,
Sprint,
SprintBackflip,
+
+ AirSphere,
AirSphereAntic,
+ AirSphereDissipate,
+ AirSphereRepeatAntic,
+
SilkBossNeedleCast,
+
Taunt,
+ TauntBack,
+ TauntBackEnd,
+ TauntBackUp,
+ TauntEndtoIdle,
+ TauntIdle,
+ TauntRings,
+ TauntRingsFlash,
+ TauntStraightBack,
+ TauntStraightBackQ,
+ TauntThread,
+ ChallengeStrong,
+ ChallengeTalk,
+ ToChallengeTalk,
+ ChallengeTalkEnd,
+ ChallengeTalkEndToIdle,
+ ChallengeTalkEndToTalk,
+ ChallengeTalkIdle,
+ ChallengeTalkIdleStart,
+ ChallengeTalkStart,
+ ChallengeStrongToIdle,
+
NeedolinStart,
NeedolinStartCancelable,
NeedolinEnd,
@@ -86,25 +173,63 @@ internal enum AnimationClip {
NeedolinSitStart,
NeedolinSitPlay,
NeedolinSitEnd,
+ NeedolinPlayLowTransition,
+ NeedolinPlayLow,
+ NeedolinPlayHighTransition,
+ NeedolinPlayHigh,
+ NeedolinDeepEnd,
+ NeedolinPromptedIdle,
+ NeedolinPromptedPlayEnd,
+ NeedolinPromptedPlayStart,
+ NeedolinPromptedStart,
+ NeedolinThread,
+ NeedolinTurn,
+ NeedolinSitTurn,
+
HarpoonAntic,
HarpoonThrow,
HarpoonCatch,
- SilkChargeEnd,
+ HarpoonCatchBack,
+ HarpoonDash,
+ HarpoonNeedle,
+ HarpoonNeedleReturn,
+ HarpoonNeedleWallHit,
+ HarpoonThread,
+
MapOpen,
MapIdle,
MapAway,
+ MapTurn,
+ MapUpdate,
+ MapWalk,
SitMapOpen,
SitMapClose,
+
BindChargeGround,
BindChargeAir,
BindBurstAir,
BindBurstGround,
+ BindCursedEnd,
+ BindCursedMid,
+ BindCursedStart,
+ BindFirstEnd,
+ BindFirstStand,
+ BindFlash,
+ BindSilk,
+ BindSilkFirstWeaver,
+ BindSilkLoop,
+ BindSilkQuick,
+ BindCancelAir,
+ BindCancelGround,
+ BindChargeGrabNeedle,
+ ReserveBindBurstAir,
+ ReserveBindBurstGround,
+ ReserveBindChargeAir,
+ ReserveBindChargeGround,
+
LookDown,
LookDownEnd,
- NeedolinPlayLowTransition,
- NeedolinPlayLow,
- NeedolinPlayHighTransition,
- NeedolinPlayHigh,
+
SuperJumpAntic,
SuperJumpThrow,
SuperJumpThrowWait,
@@ -112,13 +237,28 @@ internal enum AnimationClip {
SuperJumpLoop,
SuperJumpHitRoof,
SuperJumpFall,
+ SuperJumpAnticCancel,
+ SuperJumpAnticEffect,
+ SuperJumpAnticEffectEnd,
+ SuperJumpCatchCancel,
+ SuperJumpHitRoofQ,
+ SuperJumpLoopCancel,
+ SuperJumpThread,
+ SuperJumpThreadLoop,
+
+ Fall,
+ FallToProstrate,
+
LookUp,
LookUpEnd,
+
SurfaceIn,
SurfaceInToIdle,
SurfaceIdle,
SurfaceIdleToSwim,
SurfaceTurnToSwim,
+ SurfaceCurrentInRecover,
+ SurfaceCurrentInTumble,
SwimDash,
SwimDashTurn,
SwimDashBonk,
@@ -126,14 +266,21 @@ internal enum AnimationClip {
MantleCancelToJumpBackwards,
WallScrambleAntic,
WallScramble,
+ WallScrambleMantle,
+ WallScrambleQuickened,
+ WallScrambleRepeat,
WallScrambleEnd,
WallSlash,
- WallJumpSomersault,
+ SomersaultPinDrop,
Airborne,
///
/// Sprinting into a wall
///
+ Bonked,
SprintBonk,
+ BonkedFast,
+ BonkedLand,
+
SlashLandRun,
///
/// Down slash with Reaper crest
@@ -268,6 +415,10 @@ internal enum AnimationClip {
///
DownSpikeCharge,
///
+ /// Fully charged down slash with Architect crest
+ ///
+ DownSpikeCharged,
+ ///
/// Slash while sprinting/dashing with Architect crest
///
DashAttackCharge,
@@ -321,6 +472,12 @@ internal enum AnimationClip {
NeedleThrowAnticA,
NeedleThrowThrowing,
NeedleThrowCatch,
+ NeedleThrowBurst,
+ NeedleThrowOut,
+ NeedleThrowReturn,
+ NeedleThrowReturnShort,
+ NeedleThrowThread,
+ NeedleThrowThunk,
///
/// Cross Stitch in air
///
@@ -331,6 +488,18 @@ internal enum AnimationClip {
ParryStanceGround,
ParryRecover,
ParryRecoverGround,
+ ParryClash,
+ ParryClashEffect,
+ ParryDash,
+ ParryDashBurst,
+ ParryReady,
+ ParryRecoverySkid,
+ ParryStanceFlash,
+ ParryStanceFlashQ,
+ ParryThread,
+ GetParryDash,
+ GetParryEnd,
+ GetParryPrepare,
///
/// Thread Storm
///
@@ -339,24 +508,32 @@ internal enum AnimationClip {
///
/// Sharpdart
///
- SilkChargeAntic,
SilkCharge,
+ SilkChargeAntic,
+ SilkChargeAnticZap,
SilkChargeRecover,
+ SilkChargeRecoverZap,
+ SilkChargeZap,
+ SilkChargeEnd,
///
/// Rune Rage
///
SilkBombAntic,
+ SilkBombAnticQ,
SilkBombLoop,
SilkBombRecover,
SitCraft,
+ SitCraftSilk,
ToolThrowUp,
///
/// Tools thrown forwards such as Straight Pin
///
ToolThrowQ,
ToolThrowAltQ,
-
+ ToolThrowM,
+ ToolThrowWall,
+
///
/// Moving when exiting Delver's Drill attack
///
@@ -366,53 +543,215 @@ internal enum AnimationClip {
///
DownSpikeBounce1,
DownSpikeBounce2,
-
///
/// Using Flea Brew
///
ChargeUp,
-
+ ChargeUpAir,
+ ChargeUpBench,
+ ChargeUpBenchSilk,
+ ChargeUpBurst,
+
IdleHurt,
TurnWalk,
+ TurnBackThreeQuarter,
+ TurnBackThreeQuarterEndToIdle,
+ TurnHeadBackward,
+ TurnHeadForward,
+ TurnQuick,
+ TurnFromBG,
+ TurnFromBGLoop,
+ TurnToBG,
+ TurnToBGLoop,
+ TurnToChallengeIdle,
+ TurnToChallengeStrong,
+ TurnToChallengeTalk,
+ TurnToFG,
+ TurnToIdle,
+
+
Walk,
+ WalkQ,
LookUpHalf,
LookUpHalfEnd,
LookDownSlight,
LookDownSlightEnd,
-
+ LookDownTalk,
+ LookUpHalfFlinch,
+ LookUpHalfTalk,
+ LookUpTalk,
+ LookDownUpdraft,
+ LookDownWindy,
+ LookDownEndUpdraft,
+ LookDownEndWindy,
+ LookDownToIdle,
+ LookingUp,
+ LookUpUpdraft,
+ LookUpWindy,
+ LookUpEndUpdraft,
+ LookUpEndWindy,
+ LookUpToIdle,
+
ScuttleStart,
ScuttleLoop,
+ ScuttleTurnToLoop,
ScuttleEnd,
ScuttleFall,
ScuttleVault,
-
+ ScuttleJump,
+ ScuttleClimb,
+ ScuttleClimbEnd,
+
Stun,
Recoil,
GrabEscape,
IdleUpdraft,
+ UpdraftAntic,
UpdraftAnticDJ,
UpdraftRise,
UpdraftRiseTurn,
+ UpdraftEnd,
+ UpdraftIdle,
+ UpdraftShoot,
ProstrateRiseNoNeedle,
BindFirst,
BindBurstFirst,
ThwipToIdle,
-
+ GetUpToIdle,
+
WeakenedStun,
WeakRiseToIdle,
WeakWalk,
WeakTryJumpAntic,
WeakTryAttack,
WeakenedStunEnd,
-
+ WeakFall,
+ WeakFlinchToIdle,
+ WeakIdleLookUp,
+ WeakWalkFaster,
+ WeakWalkToIdle,
+
TauntCollapse1,
TauntCollapse2,
TauntCollapseHit,
+ Prostrate,
ProstrateRise,
+ ProstrateNoNeedle,
+ ProstrateRiseSlow,
+ ProstrateRiseToKneel,
+ ProstrateRiseToKneelNoLoop,
+ ProstrateRiseToWound,
+
TalkingStandard,
+ TalkingBackward,
+
+ AbyssKneel,
+ AbyssKneelBackIdle,
+ AbyssKneelBackTalk,
+ AbyssKneelIdle,
+ AbyssKneeltoStand,
+ AbyssKneelTurnBack,
+ KneelToProstrate,
+ Kneeling,
+
+ AcidDeath,
+ Death,
+ DeathFinal,
+ SpikeDeath,
+ SpikeDeathAntic,
+
+ BeastlingCallFail,
+ BeastlingCallFailWindy,
+ BellwayCall,
+ FastTravelCall,
+ FastTravelChildArrive,
+ FastTravelFail,
+ FastTravelLeap,
+
+ CollectHeartPiece,
+ CollectHeartPieceEnd,
+ CollectMemoryOrb,
+ CollectNormal1,
+ CollectNormal1Q,
+ CollectNormal2,
+ CollectNormal3,
+ CollectNormal3Q,
+ CollectSilkHeart,
+ CollectStand1,
+ CollectStand2,
+ CollectStand3,
+ CollectToWound,
+
+ DropToWounded,
+ BlueHealthOverBurst,
+ CrestShrinePowerupLoop,
+ HazardRespawn,
+ RespawnWake,
+ PodBounce,
+ QuickCharged,
+ QuickCraftSilk,
+ SprintmasterLow,
+ SprintmasterStart,
+
+ DressFlourish,
+ GiveDress,
+ GiveDressIdle,
+
+ Enter,
+ Exit,
+ ExitDoorToIdle,
+ ExitToIdle,
+ Wake,
+ WakeUpGround,
+
+ HurtListenDown,
+ HurtListenUp,
+ HurtLookDown,
+ HurtLookDownWindy,
+ HurtLookDownWindyEnd,
+ HurtLookUp,
+ HurtLookUpEnd,
+ HurtLookUpWindy,
+ HurtLookUpWindyEnd,
+ HurtTalkDown,
+ HurtTalkUp,
+ HurtTalkUpWindy,
+ HurtToIdle,
+
+ NeedleFall,
+ NeedleLand,
+
+ RingDropImpact,
+ RingEject,
+ RingGrabHornet,
+ RingGrabRail,
+ RingHarpoonConnect,
+ RingLookDown,
+ RingLookDownEnd,
+ RingLookUp,
+ RingLookUpEnd,
+ RingTurn,
+
+ RoarLock,
+ RoarToLookUp,
+
+ SpaSurfaceIdle,
+ SpaSurfaceIdleToSwim,
+ SpaSurfaceIn,
+ SpaSurfaceInToIdle,
+ SpaSurfaceTurnToSwim,
+
+ WeaverPray,
+ WeaverPrayEnd,
+ WeaverPrayPrepare,
+ WeaverPrayPrepareFront,
+
+ Wound,
+ WoundDoubleStrike,
+ WoundZap,
// Custom clip names
DashEnd,
@@ -421,4 +760,9 @@ internal enum AnimationClip {
NailArtChargeEnd,
WallSlideEnd,
HazardDeath,
+
+ // Sub-animation names
+ WitchTentacles,
+ ShamanCancel,
+ BindInterrupt,
}
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 919c667..6d300ee 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -3,6 +3,7 @@
using System.Linq;
using SSMP.Animation.Effects;
using SSMP.Collection;
+using SSMP.Fsm;
using SSMP.Game;
using SSMP.Game.Client;
using SSMP.Game.Settings;
@@ -57,21 +58,58 @@ internal class AnimationManager {
{ "SitLook 2", AnimationClip.SitLook2 },
{ "SitLook 3", AnimationClip.SitLook3 },
{ "SitLook 4", AnimationClip.SitLook4 },
+ { "Sit Lean", AnimationClip.SitLean },
+ { "Sit Look Left", AnimationClip.SitLookLeft },
+ { "Sitting Asleep", AnimationClip.SittingAsleep },
{ "Get Off", AnimationClip.GetOff },
{ "Land", AnimationClip.Land },
+ { "HardLand", AnimationClip.HardLand },
+ { "HardLand Greymoor", AnimationClip.HardLandGreymoor },
+ { "HardLand Quick", AnimationClip.HardLandQuick },
+ { "Super Hard Land", AnimationClip.SuperHardLand },
+ { "Hop", AnimationClip.Hop },
+ { "Hop Land", AnimationClip.HopLand },
+ { "Hop To Somersault", AnimationClip.HopToSomersault },
{ "Run", AnimationClip.Run },
{ "RunQ", AnimationClip.RunQ },
{ "Run To Idle", AnimationClip.RunToIdle },
+ { "Run To Idle C", AnimationClip.RunToIdleC },
{ "Idle", AnimationClip.Idle },
+ { "Idle Backward", AnimationClip.IdleBackward},
+ { "Idle BG", AnimationClip.IdleBG},
+ { "Idle Hurt", AnimationClip.IdleHurt},
+ { "Idle Hurt Listen", AnimationClip.IdleHurtListen},
+ { "Idle Hurt Listen Backward", AnimationClip.IdleHurtListenBackward},
+ { "Idle Hurt Listen Windy", AnimationClip.IdleHurtListenWindy},
+ { "Idle Hurt NoNeedle", AnimationClip.IdleHurtNoNeedle},
+ { "Idle Hurt Talk", AnimationClip.IdleHurtTalk},
+ { "Idle Hurt Talk Backward", AnimationClip.IdleHurtTalkBackward},
+ { "Idle Hurt Talk Turn Backward", AnimationClip.IdleHurtTalkTurnBackward},
+ { "Idle Hurt Talk Turn Forward", AnimationClip.IdleHurtTalkTurnForward},
+ { "Idle Hurt Talk Windy", AnimationClip.IdleHurtTalkWindy},
+ { "Idle Hurt Windy", AnimationClip.IdleHurtWindy},
+ { "Idle Slap", AnimationClip.IdleSlap},
+ { "Idle To Run Short", AnimationClip.IdleToRunShort},
+ { "Idle To Run Weak", AnimationClip.IdleToRunWeak},
{ "Idle To Run", AnimationClip.IdleToRun },
+ { "Idle Windy", AnimationClip.IdleWindy },
{ "Walk To Idle", AnimationClip.WalkToIdle },
{ "Run To Walk", AnimationClip.RunToWalk },
+ { "Run To Walk Weak", AnimationClip.RunToWalkWeak },
{ "Land To Walk", AnimationClip.LandToWalk },
+ { "Land to Sprint", AnimationClip.LandToSprint },
{ "Turn", AnimationClip.Turn },
{ "Dash", AnimationClip.Dash },
{ "Dash Down", AnimationClip.DashDown },
{ "Dash Down Land", AnimationClip.DashDownLand },
+ { "Dash Down End", AnimationClip.DashDownEnd },
+ //{ "DashStabEffect", AnimationClip.DashStabEffect },
+ //{ "DashStabEffect_Glow", AnimationClip.DashStabEffectGlow },
{ "Sprint Lv2", AnimationClip.SprintLv2 },
+ { "Sprint Lv3", AnimationClip.SprintLv3 },
+ { "Sprint Recoil", AnimationClip.SprintRecoil },
+ { "Sprint Skid To Run", AnimationClip.SprintSkidToRun },
+ { "Sprint To Run", AnimationClip.SprintToRun },
{ "Mantle Cling", AnimationClip.MantleCling },
{ "Mantle Land", AnimationClip.MantleLand },
{ "Mantle Land To Idle", AnimationClip.MantleLandToIdle },
@@ -83,10 +121,13 @@ internal class AnimationManager {
{ "Sprint Air", AnimationClip.SprintAir },
{ "Sprint Air Loop", AnimationClip.SprintAirLoop },
{ "Double Jump", AnimationClip.DoubleJump },
+ { "Double Jump Wings 2", AnimationClip.DoubleJumpWings2 },
{ "Land To Run", AnimationClip.LandToRun },
{ "Dash Attack Antic", AnimationClip.DashAttackAntic },
{ "Dash Attack", AnimationClip.DashAttack },
{ "Dash Attack Recover", AnimationClip.DashAttackRecover },
+ { "Dash Attack Antic Long", AnimationClip.DashAttackAnticLong },
+ { "Dash Attack Antic Short", AnimationClip.DashAttackAnticShort },
{ "Skid End 1", AnimationClip.SkidEnd1 },
{ "Skid End 2", AnimationClip.SkidEnd2 },
{ "Dash To Idle", AnimationClip.DashToIdle },
@@ -98,6 +139,7 @@ internal class AnimationManager {
{ "Slash Land Run Alt", AnimationClip.SlashLandRunAlt },
{ "DownSpike Antic", AnimationClip.DownSpikeAntic },
{ "DownSpike", AnimationClip.DownSpike },
+ { "DownSpike Burst", AnimationClip.DownSpikeBurst },
{ "Downspike Recovery", AnimationClip.DownSpikeRecovery },
{ "Downspike Recovery Land", AnimationClip.DownSpikeRecoveryLand },
{ "Downspike Followup", AnimationClip.DownSpikeFollowup },
@@ -107,46 +149,118 @@ internal class AnimationManager {
{ "Slash_Charged_Loop", AnimationClip.SlashChargedLoop },
{ "Umbrella Inflate Antic", AnimationClip.UmbrellaInflateAntic },
{ "Walljump", AnimationClip.WallJump },
+ { "Walljump Antic", AnimationClip.WallJumpAntic },
+ { "Walljump Puff", AnimationClip.WallJumpPuff },
+ { "Walljump Somersault", AnimationClip.WallJumpSomersault },
+ { "Wallrun", AnimationClip.Wallrun },
+ { "Wallrun Antic", AnimationClip.WallrunAntic },
{ "Wall Slide", AnimationClip.WallSlide },
{ "Wall Cling", AnimationClip.WallCling },
+ { "Slide Brake", AnimationClip.SlideBrake },
+ { "Slide Brake End", AnimationClip.SlideBrakeEnd },
+ { "Slide Fast", AnimationClip.SlideFast },
+ { "Slide Fast End", AnimationClip.SlideFastEnd },
+ { "Slide Normal", AnimationClip.SlideNormal },
+ { "Slide Start", AnimationClip.SlideStart },
{ "Sit Fall Asleep", AnimationClip.SitFallAsleep },
{ "Umbrella Inflate", AnimationClip.UmbrellaInflate },
{ "Umbrella Float", AnimationClip.UmbrellaFloat },
{ "Umbrella Deflate", AnimationClip.UmbrellaDeflate },
+ { "Umbrella Turn", AnimationClip.UmbrellaTurn },
{ "Idle Rest", AnimationClip.IdleRest },
{ "Mantle Land To Run", AnimationClip.MantleLandToRun },
{ "Sit", AnimationClip.Sit },
{ "Sprint", AnimationClip.Sprint },
{ "Sprint Backflip", AnimationClip.SprintBackflip },
+ { "AirSphere", AnimationClip.AirSphere },
{ "AirSphere Antic", AnimationClip.AirSphereAntic },
+ { "AirSphere Dissipate", AnimationClip.AirSphereDissipate },
+ { "AirSphere RepeatAntic", AnimationClip.AirSphereRepeatAntic },
{ "Silk Boss Needle Cast", AnimationClip.SilkBossNeedleCast },
{ "Taunt", AnimationClip.Taunt },
+ { "Taunt Back", AnimationClip.TauntBack },
+ { "Taunt Back End", AnimationClip.TauntBackEnd },
+ { "Taunt Back Up", AnimationClip.TauntBackUp },
+ { "Taunt End to Idle", AnimationClip.TauntEndtoIdle },
+ { "Taunt Idle", AnimationClip.TauntIdle },
+ { "Taunt Rings", AnimationClip.TauntRings },
+ { "Taunt Rings Flash", AnimationClip.TauntRingsFlash },
+ { "Taunt Straight Back", AnimationClip.TauntStraightBack },
+ { "Taunt Straight Back Q", AnimationClip.TauntStraightBackQ },
+ { "Taunt Thread", AnimationClip.TauntThread },
+ { "Challenge Strong", AnimationClip.ChallengeStrong },
+ { "Challenge Talk", AnimationClip.ChallengeTalk },
+ { "ToChallengeTalk", AnimationClip.ToChallengeTalk },
+ { "Challenge Talk End", AnimationClip.ChallengeTalkEnd },
+ { "Challenge Talk End ToIdle", AnimationClip.ChallengeTalkEndToIdle },
+ { "Challenge Talk End ToTalk", AnimationClip.ChallengeTalkEndToTalk },
+ { "Challenge Talk Idle", AnimationClip.ChallengeTalkIdle },
+ { "Challenge Talk Idle Start", AnimationClip.ChallengeTalkIdleStart },
+ { "Challenge Talk Start", AnimationClip.ChallengeTalkStart },
+ { "ChallengeStrongToIdle", AnimationClip.ChallengeStrongToIdle },
+
{ "Needolin Start", AnimationClip.NeedolinStart },
{ "Needolin StartCancelable", AnimationClip.NeedolinStartCancelable },
{ "Needolin End", AnimationClip.NeedolinEnd },
{ "Needolin Play", AnimationClip.NeedolinPlay },
{ "NeedolinSit Start", AnimationClip.NeedolinSitStart },
{ "NeedolinSit Play", AnimationClip.NeedolinSitPlay },
+ { "Needolin Play Low", AnimationClip.NeedolinPlayLow },
+ { "Needolin Play Low Transition", AnimationClip.NeedolinPlayLowTransition },
+ { "Needolin Play High", AnimationClip.NeedolinPlayHigh },
+ { "Needolin Play High Transition", AnimationClip.NeedolinPlayHighTransition },
{ "NeedolinSit End", AnimationClip.NeedolinSitEnd },
+ { "NeedolinSit Turn", AnimationClip.NeedolinSitTurn },
+ { "Needolin Deep End", AnimationClip.NeedolinDeepEnd },
+ { "Needolin Prompted Idle", AnimationClip.NeedolinPromptedIdle },
+ { "Needolin Prompted Play End", AnimationClip.NeedolinPromptedPlayEnd },
+ { "Needolin Prompted Play Start", AnimationClip.NeedolinPromptedPlayStart },
+ { "Needolin Prompted Start", AnimationClip.NeedolinPromptedStart },
+ { "Needolin Thread", AnimationClip.NeedolinThread },
+ { "Needolin Turn", AnimationClip.NeedolinTurn },
{ "Harpoon Antic", AnimationClip.HarpoonAntic },
{ "Harpoon Throw", AnimationClip.HarpoonThrow },
{ "Harpoon Catch", AnimationClip.HarpoonCatch },
+ { "Harpoon Catch Back", AnimationClip.HarpoonCatchBack },
+ { "Harpoon Dash", AnimationClip.HarpoonDash },
+ { "Harpoon Needle", AnimationClip.HarpoonNeedle },
+ { "Harpoon Needle Return", AnimationClip.HarpoonNeedleReturn },
+ { "Harpoon Needle Wall Hit", AnimationClip.HarpoonNeedleWallHit },
+ { "Harpoon Thread", AnimationClip.HarpoonThread },
+
{ "Silk Charge End", AnimationClip.SilkChargeEnd },
{ "Map Open", AnimationClip.MapOpen },
{ "Map Idle", AnimationClip.MapIdle },
{ "Map Away", AnimationClip.MapAway },
+ { "Map Turn", AnimationClip.MapTurn },
+ { "Map Update", AnimationClip.MapUpdate },
+ { "Map Walk", AnimationClip.MapWalk },
{ "Sit Map Open", AnimationClip.SitMapOpen },
{ "Sit Map Close", AnimationClip.SitMapClose },
{ "BindCharge Ground", AnimationClip.BindChargeGround },
{ "BindCharge Air", AnimationClip.BindChargeAir },
{ "BindBurst Air", AnimationClip.BindBurstAir },
{ "BindBurst Ground", AnimationClip.BindBurstGround },
+ { "Reserve BindBurst Air", AnimationClip.ReserveBindBurstAir },
+ { "Reserve BindBurst Ground", AnimationClip.ReserveBindBurstGround },
+ { "Bind Cursed End", AnimationClip.BindCursedEnd },
+ { "Bind Cursed Start", AnimationClip.BindCursedStart },
+ { "Bind Cursed Mid", AnimationClip.BindCursedMid },
+ { "Bind First End", AnimationClip.BindFirstEnd },
+ { "Bind First Stand", AnimationClip.BindFirstStand },
+ { "Bind Flash", AnimationClip.BindFlash },
+ { "Bind Silk", AnimationClip.BindSilk },
+ { "Bind Silk FirstWeaver", AnimationClip.BindSilkFirstWeaver },
+ { "Bind Silk Loop", AnimationClip.BindSilkLoop },
+ { "Bind Silk Quick", AnimationClip.BindSilkQuick },
+ { "BindCancel Air", AnimationClip.BindCancelAir },
+ { "BindCancel Ground", AnimationClip.BindCancelGround },
+ { "BindCharge GrabNeedle", AnimationClip.BindChargeGrabNeedle },
+ { "Reserve BindCharge Air", AnimationClip.ReserveBindChargeAir },
+ { "Reserve BindCharge Ground", AnimationClip.ReserveBindChargeGround },
{ "LookDown", AnimationClip.LookDown },
{ "LookDownEnd", AnimationClip.LookDownEnd },
- { "Needolin Play Low Transition", AnimationClip.NeedolinPlayLowTransition },
- { "Needolin Play Low", AnimationClip.NeedolinPlayLow },
- { "Needolin Play High Transition", AnimationClip.NeedolinPlayHighTransition },
- { "Needolin Play High", AnimationClip.NeedolinPlayHigh },
+
{ "Super Jump Antic", AnimationClip.SuperJumpAntic },
{ "Super Jump Throw", AnimationClip.SuperJumpThrow },
{ "Super Jump Throw Wait", AnimationClip.SuperJumpThrowWait },
@@ -154,6 +268,18 @@ internal class AnimationManager {
{ "Super Jump Loop", AnimationClip.SuperJumpLoop },
{ "Super Jump Hit Roof", AnimationClip.SuperJumpHitRoof },
{ "Super Jump Fall", AnimationClip.SuperJumpFall },
+ { "Super Jump Antic Cancel", AnimationClip.SuperJumpAnticCancel },
+ { "Super Jump Antic Effect", AnimationClip.SuperJumpAnticEffect },
+ { "Super Jump Antic Effect End", AnimationClip.SuperJumpAnticEffectEnd },
+ { "Super Jump Catch Cancel", AnimationClip.SuperJumpCatchCancel },
+ { "Super Jump Hit Roof Q", AnimationClip.SuperJumpHitRoofQ },
+ { "Super Jump Loop Cancel", AnimationClip.SuperJumpLoopCancel },
+ { "Super Jump Thread", AnimationClip.SuperJumpThread },
+ { "Super Jump Thread Loop", AnimationClip.SuperJumpThreadLoop },
+
+ { "Fall", AnimationClip.Fall },
+ { "FallToProstrate", AnimationClip.FallToProstrate },
+
{ "LookUp", AnimationClip.LookUp },
{ "LookUpEnd", AnimationClip.LookUpEnd },
{ "Surface In", AnimationClip.SurfaceIn },
@@ -161,6 +287,8 @@ internal class AnimationManager {
{ "Surface Idle", AnimationClip.SurfaceIdle },
{ "Surface IdleToSwim", AnimationClip.SurfaceIdleToSwim },
{ "Surface TurnToSwim", AnimationClip.SurfaceTurnToSwim },
+ { "Surface Current In Recover", AnimationClip.SurfaceCurrentInRecover },
+ { "Surface Current In Tumble", AnimationClip.SurfaceCurrentInTumble },
{ "Swim Dash", AnimationClip.SwimDash },
{ "Swim Dash Turn", AnimationClip.SwimDashTurn },
{ "Swim Dash Bonk", AnimationClip.SwimDashBonk },
@@ -169,10 +297,16 @@ internal class AnimationManager {
{ "Wall Scramble Antic", AnimationClip.WallScrambleAntic },
{ "Wall Scramble", AnimationClip.WallScramble },
{ "Wall Scramble End", AnimationClip.WallScrambleEnd },
+ { "Wall Scramble Mantle", AnimationClip.WallScrambleMantle },
+ { "Wall Scramble Quickened", AnimationClip.WallScrambleQuickened },
+ { "Wall Scramble Repeat", AnimationClip.WallScrambleRepeat },
{ "Wall Slash", AnimationClip.WallSlash },
- { "Walljump Somersault", AnimationClip.WallJumpSomersault },
+ { "Somersault Pin Drop", AnimationClip.SomersaultPinDrop },
{ "Airborne", AnimationClip.Airborne },
+ { "Bonked", AnimationClip.Bonked },
{ "Sprint Bonk", AnimationClip.SprintBonk },
+ { "Bonked Fast", AnimationClip.BonkedFast },
+ { "Bonked Land", AnimationClip.BonkedLand },
{ "Slash Land Run", AnimationClip.SlashLandRun },
{ "v3 Down Slash Antic", AnimationClip.V3DownSlashAntic },
{ "v3 Down Slash", AnimationClip.V3DownSlash },
@@ -210,6 +344,7 @@ internal class AnimationManager {
{ "BindCharge Witch Long", AnimationClip.BindChargeWitchLong },
{ "DownSpike Charge", AnimationClip.DownSpikeCharge },
+ { "DownSpike Charged", AnimationClip.DownSpikeCharged },
{ "Dash Attack Charge", AnimationClip.DashAttackCharge },
{ "Quick Craft Ground", AnimationClip.QuickCraftGround },
{ "Quick Craft Air", AnimationClip.QuickCraftAir },
@@ -226,47 +361,111 @@ internal class AnimationManager {
{ "NeedleThrow AnticA", AnimationClip.NeedleThrowAnticA },
{ "NeedleThrow Throwing", AnimationClip.NeedleThrowThrowing },
{ "NeedleThrow Catch", AnimationClip.NeedleThrowCatch },
-
+ { "NeedleThrow Burst", AnimationClip.NeedleThrowBurst },
+ { "NeedleThrow Out", AnimationClip.NeedleThrowOut },
+ { "NeedleThrow Return", AnimationClip.NeedleThrowReturn },
+ { "NeedleThrow Return Short", AnimationClip.NeedleThrowReturnShort },
+ { "NeedleThrow Thread", AnimationClip.NeedleThrowThread },
+ { "NeedleThrow Thunk", AnimationClip.NeedleThrowThunk },
+
{ "Parry Stance", AnimationClip.ParryStance },
{ "Parry Stance Ground", AnimationClip.ParryStanceGround },
{ "Parry Recover", AnimationClip.ParryRecover },
{ "Parry Recover Ground", AnimationClip.ParryRecoverGround },
-
+ { "Parry Clash", AnimationClip.ParryClash },
+ { "Parry Clash Effect", AnimationClip.ParryClashEffect },
+ { "Parry Dash", AnimationClip.ParryDash },
+ { "Parry DashBurst", AnimationClip.ParryDashBurst },
+ { "Parry Ready", AnimationClip.ParryReady },
+ { "Parry Recovery Skid", AnimationClip.ParryRecoverySkid },
+ { "Parry Stance Flash", AnimationClip.ParryStanceFlash },
+ { "Parry Stance Flash Q", AnimationClip.ParryStanceFlashQ },
+ { "Parry Thread", AnimationClip.ParryThread },
+ { "Get Parry Dash", AnimationClip.GetParryDash },
+ { "Get Parry End", AnimationClip.GetParryEnd },
+ { "Get Parry Prepare", AnimationClip.GetParryPrepare },
+
{ "AirSphere Attack", AnimationClip.AirSphereAttack },
{ "AirSphere End", AnimationClip.AirSphereEnd },
{ "Silk Charge Antic", AnimationClip.SilkChargeAntic },
{ "Silk Charge", AnimationClip.SilkCharge },
{ "Silk Charge Recover", AnimationClip.SilkChargeRecover },
-
+ { "Silk Charge Zap", AnimationClip.SilkChargeZap },
+ { "Silk Charge Recover Zap", AnimationClip.SilkChargeRecoverZap },
+ { "Silk Charge Antic Zap", AnimationClip.SilkChargeAnticZap },
+
+
{ "Silk Bomb Antic", AnimationClip.SilkBombAntic },
+ { "Silk Bomb Antic Q", AnimationClip.SilkBombAnticQ },
{ "Silk Bomb Loop", AnimationClip.SilkBombLoop },
{ "Silk Bomb Recover", AnimationClip.SilkBombRecover },
{ "Sit Craft", AnimationClip.SitCraft },
+ { "Sit Craft Silk", AnimationClip.SitCraftSilk },
{ "ToolThrow Up", AnimationClip.ToolThrowUp },
{ "ToolThrow Q", AnimationClip.ToolThrowQ },
{ "ToolThrowAlt Q", AnimationClip.ToolThrowAltQ },
-
+ { "ToolThrow M", AnimationClip.ToolThrowM },
+ { "ToolThrow Wall", AnimationClip.ToolThrowWall },
+
{ "Recoil Twirl", AnimationClip.RecoilTwirl },
{ "DownSpikeBounce 1", AnimationClip.DownSpikeBounce1 },
{ "DownSpikeBounce 2", AnimationClip.DownSpikeBounce2 },
{ "Charge Up", AnimationClip.ChargeUp },
-
- { "Idle Hurt", AnimationClip.IdleHurt },
+ { "Charge Up Air", AnimationClip.ChargeUpAir },
+ { "Charge Up Bench", AnimationClip.ChargeUpBench },
+ { "Charge Up Bench Silk", AnimationClip.ChargeUpBenchSilk },
+ { "Charge Up Burst", AnimationClip.ChargeUpBurst },
+
{ "TurnWalk", AnimationClip.TurnWalk },
+ { "Turn Back Three Quarter", AnimationClip.TurnBackThreeQuarter },
+ { "Turn Back Three Quarter EndToIdle", AnimationClip.TurnBackThreeQuarterEndToIdle },
+ { "Turn Head Backward", AnimationClip.TurnHeadBackward },
+ { "Turn Head Forward", AnimationClip.TurnHeadForward },
+ { "Turn Quick", AnimationClip.TurnQuick },
+ { "TurnFromBG", AnimationClip.TurnFromBG },
+ { "TurnFromBG Loop", AnimationClip.TurnFromBGLoop },
+ { "TurnToBG", AnimationClip.TurnToBG },
+ { "TurnToBG Loop", AnimationClip.TurnToBGLoop },
+ { "TurnToChallengeIdle", AnimationClip.TurnToChallengeIdle },
+ { "TurnToChallengeStrong", AnimationClip.TurnToChallengeStrong },
+ { "TurnToChallengeTalk", AnimationClip.TurnToChallengeTalk },
+ { "TurnToFG", AnimationClip.TurnToFG },
+ { "TurnToIdle", AnimationClip.TurnToIdle },
+
{ "Walk", AnimationClip.Walk },
+ { "Walk Q", AnimationClip.WalkQ },
{ "Look Up Half", AnimationClip.LookUpHalf },
{ "Look Up Half End", AnimationClip.LookUpHalfEnd },
{ "LookDown Slight", AnimationClip.LookDownSlight },
{ "LookDown Slight End", AnimationClip.LookDownSlightEnd },
-
+ { "Look Down Talk", AnimationClip.LookDownTalk },
+ { "Look Up Half Flinch", AnimationClip.LookUpHalfFlinch },
+ { "Look Up Half Talk", AnimationClip.LookUpHalfTalk },
+ { "Look Up Talk", AnimationClip.LookUpTalk },
+ { "LookDown Updraft", AnimationClip.LookDownUpdraft },
+ { "LookDown Windy", AnimationClip.LookDownWindy },
+ { "LookDownEnd Updraft", AnimationClip.LookDownEndUpdraft },
+ { "LookDownEnd Windy", AnimationClip.LookDownEndWindy },
+ { "LookDownToIdle", AnimationClip.LookDownToIdle },
+ { "LookingUp", AnimationClip.LookingUp },
+ { "LookUp Updraft", AnimationClip.LookUpUpdraft },
+ { "LookUp Windy", AnimationClip.LookUpWindy },
+ { "LookUpEnd Updraft", AnimationClip.LookUpEndUpdraft },
+ { "LookUpEnd Windy", AnimationClip.LookUpEndWindy },
+ { "LookUpToIdle", AnimationClip.LookUpToIdle },
+
{ "Scuttle Start", AnimationClip.ScuttleStart },
{ "Scuttle Loop", AnimationClip.ScuttleLoop },
+ { "Scuttle TurnToLoop", AnimationClip.ScuttleTurnToLoop },
{ "Scuttle End", AnimationClip.ScuttleEnd },
{ "Scuttle Fall", AnimationClip.ScuttleFall },
{ "Scuttle Vault", AnimationClip.ScuttleVault },
+ { "Scuttle Jump", AnimationClip.ScuttleJump },
+ { "Scuttle Climb", AnimationClip.ScuttleClimb },
+ { "Scuttle Climb End", AnimationClip.ScuttleClimbEnd },
{ "Stun", AnimationClip.Stun },
{ "Recoil", AnimationClip.Recoil },
@@ -274,27 +473,153 @@ internal class AnimationManager {
{ "Grab Escape", AnimationClip.GrabEscape },
{ "Idle Updraft", AnimationClip.IdleUpdraft },
+ { "Updraft Antic", AnimationClip.UpdraftAntic },
{ "Updraft Antic DJ", AnimationClip.UpdraftAnticDJ },
{ "Updraft Rise", AnimationClip.UpdraftRise },
{ "Updraft Rise Turn", AnimationClip.UpdraftRiseTurn },
-
+ { "Updraft End", AnimationClip.UpdraftEnd },
+ { "Updraft Idle", AnimationClip.UpdraftIdle },
+ { "Updraft Shoot", AnimationClip.UpdraftShoot },
+
{ "Prostrate Rise NoNeedle", AnimationClip.ProstrateRiseNoNeedle },
{ "Bind First", AnimationClip.BindFirst },
{ "BindBurst First", AnimationClip.BindBurstFirst },
{ "Thwip To Idle", AnimationClip.ThwipToIdle },
-
+ { "GetUpToIdle", AnimationClip.GetUpToIdle },
+
{ "Weakened Stun", AnimationClip.WeakenedStun },
{ "Weak Rise To Idle", AnimationClip.WeakRiseToIdle },
{ "Weak Walk", AnimationClip.WeakWalk },
{ "Weak TryJumpAntic", AnimationClip.WeakTryJumpAntic },
{ "Weak TryAttack", AnimationClip.WeakTryAttack },
{ "Weakened StunEnd", AnimationClip.WeakenedStunEnd },
-
+ { "Weak Fall", AnimationClip.WeakFall },
+ { "Weak Flinch To Idle", AnimationClip.WeakFlinchToIdle },
+ { "Weak Idle Look Up", AnimationClip.WeakIdleLookUp },
+ { "Weak Walk Faster", AnimationClip.WeakWalkFaster },
+ { "Weak Walk To Idle", AnimationClip.WeakWalkToIdle },
+
+
{ "Taunt Collapse1", AnimationClip.TauntCollapse1 },
{ "Taunt Collapse2", AnimationClip.TauntCollapse2 },
{ "Taunt CollapseHit", AnimationClip.TauntCollapseHit },
+ { "Prostrate", AnimationClip.Prostrate },
{ "Prostrate Rise", AnimationClip.ProstrateRise },
+ { "Prostrate NoNeedle", AnimationClip.ProstrateNoNeedle },
+ { "Prostrate Rise Slow", AnimationClip.ProstrateRiseSlow },
+ { "ProstrateRiseToKneel", AnimationClip.ProstrateRiseToKneel },
+ { "ProstrateRiseToKneel NoLoop", AnimationClip.ProstrateRiseToKneelNoLoop },
+ { "ProstrateRiseToWound", AnimationClip.ProstrateRiseToWound },
+
{ "Talking Standard", AnimationClip.TalkingStandard },
+ { "Talking Backward", AnimationClip.TalkingBackward },
+
+ { "Abyss Kneel", AnimationClip.AbyssKneel },
+ { "Abyss Kneel Back Idle", AnimationClip.AbyssKneelBackIdle },
+ { "Abyss Kneel Back Talk", AnimationClip.AbyssKneelBackTalk },
+ { "Abyss Kneel Idle", AnimationClip.AbyssKneelIdle },
+ { "Abyss Kneel to Stand", AnimationClip.AbyssKneeltoStand },
+ { "Abyss Kneel Turn Back", AnimationClip.AbyssKneelTurnBack },
+ { "Kneel To Prostrate", AnimationClip.KneelToProstrate },
+ { "Kneeling", AnimationClip.Kneeling },
+
+ { "Acid Death", AnimationClip.AcidDeath },
+ { "Death", AnimationClip.Death },
+ { "Death Final", AnimationClip.DeathFinal },
+ { "Spike Death", AnimationClip.SpikeDeath },
+ { "Spike Death Antic", AnimationClip.SpikeDeathAntic },
+
+ { "Beastling Call Fail", AnimationClip.BeastlingCallFail },
+ { "Beastling Call Fail Windy", AnimationClip.BeastlingCallFailWindy },
+ { "Bellway Call", AnimationClip.BellwayCall },
+ { "Fast Travel Call", AnimationClip.FastTravelCall },
+ { "Fast Travel Child Arrive", AnimationClip.FastTravelChildArrive },
+ { "Fast Travel Fail", AnimationClip.FastTravelFail },
+ { "Fast Travel Leap", AnimationClip.FastTravelLeap },
+
+ { "Collect Heart Piece", AnimationClip.CollectHeartPiece },
+ { "Collect Heart Piece End", AnimationClip.CollectHeartPieceEnd },
+ { "Collect Memory Orb", AnimationClip.CollectMemoryOrb },
+ { "Collect Normal 1", AnimationClip.CollectNormal1 },
+ { "Collect Normal 1 Q", AnimationClip.CollectNormal1Q },
+ { "Collect Normal 2", AnimationClip.CollectNormal2 },
+ { "Collect Normal 3", AnimationClip.CollectNormal3 },
+ { "Collect Normal 3 Q", AnimationClip.CollectNormal3Q },
+ { "Collect Silk Heart", AnimationClip.CollectSilkHeart },
+ { "Collect Stand 1", AnimationClip.CollectStand1 },
+ { "Collect Stand 2", AnimationClip.CollectStand2 },
+ { "Collect Stand 3", AnimationClip.CollectStand3 },
+ { "CollectToWound", AnimationClip.CollectToWound },
+ { "DropToWounded", AnimationClip.DropToWounded },
+ { "Blue Health Over Burst", AnimationClip.BlueHealthOverBurst },
+ { "Crest Shrine Powerup Loop", AnimationClip.CrestShrinePowerupLoop },
+ { "Hazard Respawn", AnimationClip.HazardRespawn },
+ { "Respawn Wake", AnimationClip.RespawnWake },
+ { "Pod Bounce", AnimationClip.PodBounce },
+ { "Quick Charged", AnimationClip.QuickCharged },
+ { "Quick Craft Silk", AnimationClip.QuickCraftSilk },
+ { "Sprintmaster Low", AnimationClip.SprintmasterLow },
+ { "Sprintmaster Start", AnimationClip.SprintmasterStart },
+
+ { "Dress Flourish", AnimationClip.DressFlourish },
+ { "Give Dress", AnimationClip.GiveDress },
+ { "Give Dress Idle", AnimationClip.GiveDressIdle },
+ { "Enter", AnimationClip.Enter },
+ { "Exit", AnimationClip.Exit },
+ { "Exit Door To Idle", AnimationClip.ExitDoorToIdle },
+ { "Exit To Idle", AnimationClip.ExitToIdle },
+ { "Wake", AnimationClip.Wake },
+ { "Wake Up Ground", AnimationClip.WakeUpGround },
+
+ { "Hurt Listen Down", AnimationClip.HurtListenDown },
+ { "Hurt Listen Up", AnimationClip.HurtListenUp },
+ { "Hurt Look Down", AnimationClip.HurtLookDown },
+ { "Hurt Look Down Windy", AnimationClip.HurtLookDownWindy },
+ { "Hurt Look Down Windy End", AnimationClip.HurtLookDownWindyEnd },
+ { "Hurt Look Up", AnimationClip.HurtLookUp },
+ { "Hurt Look Up End", AnimationClip.HurtLookUpEnd },
+ { "Hurt Look Up Windy", AnimationClip.HurtLookUpWindy },
+ { "Hurt Look Up Windy End", AnimationClip.HurtLookUpWindyEnd },
+ { "Hurt Talk Down", AnimationClip.HurtTalkDown },
+ { "Hurt Talk Up", AnimationClip.HurtTalkUp },
+ { "Hurt Talk Up Windy", AnimationClip.HurtTalkUpWindy },
+ { "Hurt To Idle", AnimationClip.HurtToIdle },
+
+ { "Needle Fall", AnimationClip.NeedleFall },
+ { "Needle Land", AnimationClip.NeedleLand },
+
+ { "Ring Drop Impact", AnimationClip.RingDropImpact },
+ { "Ring Eject", AnimationClip.RingEject },
+ { "Ring Grab Hornet", AnimationClip.RingGrabHornet },
+ { "Ring Grab Rail", AnimationClip.RingGrabRail },
+ { "Ring Harpoon Connect", AnimationClip.RingHarpoonConnect },
+ { "Ring Look Down", AnimationClip.RingLookDown },
+ { "Ring Look Down End", AnimationClip.RingLookDownEnd },
+ { "Ring Look Up", AnimationClip.RingLookUp },
+ { "Ring Look Up End", AnimationClip.RingLookUpEnd },
+ { "Ring Turn", AnimationClip.RingTurn },
+
+ { "Roar Lock", AnimationClip.RoarLock },
+ { "Roar To LookUp", AnimationClip.RoarToLookUp },
+
+ { "Spa Surface Idle", AnimationClip.SpaSurfaceIdle },
+ { "Spa Surface IdleToSwim", AnimationClip.SpaSurfaceIdleToSwim },
+ { "Spa Surface In", AnimationClip.SpaSurfaceIn },
+ { "Spa Surface InToIdle", AnimationClip.SpaSurfaceInToIdle },
+ { "Spa Surface TurnToSwim", AnimationClip.SpaSurfaceTurnToSwim },
+
+ { "Weaver Pray", AnimationClip.WeaverPray },
+ { "Weaver Pray End", AnimationClip.WeaverPrayEnd },
+ { "Weaver Pray Prepare", AnimationClip.WeaverPrayPrepare },
+ { "Weaver Pray Prepare Front", AnimationClip.WeaverPrayPrepareFront },
+
+ { "Wound", AnimationClip.Wound },
+ { "Wound Double Strike", AnimationClip.WoundDoubleStrike },
+ { "Wound Zap", AnimationClip.WoundZap },
+
+ { "Witch Tentacles!", AnimationClip.WitchTentacles },
+ { "Shaman Cancel", AnimationClip.ShamanCancel },
+ { "Bind Fail Burst", AnimationClip.BindInterrupt }
};
///
@@ -306,6 +631,7 @@ internal class AnimationManager {
{ AnimationClip.UpSlash, new Slash(SlashBase.SlashType.Up) },
{ AnimationClip.WallSlash, new Slash(SlashBase.SlashType.Wall) },
{ AnimationClip.DownSpike, new DownSpike() }, // Hunter Crest down slash
+ { AnimationClip.DownSpikeCharged, new DownSpike() }, // Architect Crest charged down slash
{ AnimationClip.V3DownSlash, new Slash(SlashBase.SlashType.Down) },
{ AnimationClip.DownSlash, new Slash(SlashBase.SlashType.Down) },
{ AnimationClip.DownSlashAlt, new Slash(SlashBase.SlashType.DownAlt) },
@@ -325,7 +651,18 @@ internal class AnimationManager {
{ AnimationClip.SlashCharged, new NeedleStrike(false) },
{ AnimationClip.SlashChargedLoop, new NeedleStrike(true) },
{ AnimationClip.NeedleArtDash, new NeedleStrike(false) },
- { AnimationClip.BindChargeGround, new Bind() }
+ { AnimationClip.BindChargeGround, new Bind() },
+ { AnimationClip.BindChargeGroundLand, new Bind { BindState = Bind.State.ShamanDoneFalling } },
+ { AnimationClip.BindBurstGround, BindBurst.Instance },
+ { AnimationClip.BindChargeHealBurst, BindBurst.Instance },
+ { AnimationClip.BindBurstAir, BindBurst.Instance },
+ { AnimationClip.RageBindBurst, BindBurst.Instance }
+ };
+
+ private static readonly Dictionary SubAnimationEffects = new() {
+ { AnimationClip.WitchTentacles, BindBurst.Instance },
+ { AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } },
+ { AnimationClip.BindInterrupt, BindInterrupt.Instance }
};
///
@@ -407,6 +744,10 @@ public void Initialize(ServerSettings serverSettings) {
foreach (var effect in AnimationEffects.Values) {
effect.SetServerSettings(serverSettings);
}
+
+ foreach (var effect in SubAnimationEffects.Values) {
+ effect.SetServerSettings(serverSettings);
+ }
}
///
@@ -424,6 +765,13 @@ public void RegisterHooks() {
EventHooks.SpriteAnimatorWarpClipToLocalTime += Tk2dSpriteAnimatorOnWarpClipToLocalTime;
EventHooks.SpriteAnimatorProcessEvents += Tk2dSpriteAnimatorOnProcessEvents;
+ // Register FSM hooks for certain bind actions
+ HeroController.OnHeroInstanceSet += CreateBindHooks;
+ if (HeroController.SilentInstance != null) {
+ CreateBindHooks(HeroController.instance);
+ }
+
+
// Register a callback so we know when the dash has finished
// On.HeroController.CancelDash += HeroControllerOnCancelDash;
@@ -448,6 +796,8 @@ public void RegisterHooks() {
public void DeregisterHooks() {
SceneManager.activeSceneChanged -= OnSceneChange;
+ HeroController.OnHeroInstanceSet -= CreateBindHooks;
+ FsmStateActionInjector.UninjectAll();
// On.HeroAnimationController.Play -= HeroAnimationControllerOnPlay;
// On.HeroAnimationController.PlayFromFrame -= HeroAnimationControllerOnPlayFromFrame;
@@ -481,7 +831,11 @@ public void OnPlayerAnimationUpdate(ushort id, int clipId, int frame, byte[]? ef
var animationClip = (AnimationClip) clipId;
- if (AnimationEffects.TryGetValue(animationClip, out var animationEffect)) {
+ if (!AnimationEffects.TryGetValue(animationClip, out var animationEffect)) {
+ SubAnimationEffects.TryGetValue(animationClip, out animationEffect);
+ }
+
+ if (animationEffect != null) {
var playerObject = _playerManager.GetPlayerObject(id);
if (!playerObject) {
// Logger.Get().Warn(this, $"Tried to play animation effect {clipName} with ID: {id}, but player object doesn't exist");
@@ -529,6 +883,11 @@ public void UpdatePlayerAnimation(ushort id, int clipId, int frame, CrestType cr
if (_debugLogAnimations) Logger.Info($"Received PlayerAnimationUpdate: {animationClip}");
+ if (SubAnimationEffects.ContainsKey(animationClip)) {
+ if (_debugLogAnimations) Logger.Info($"PlayerAnimationUpdate was sub-effect: {animationClip}");
+ return;
+ }
+
if (!ClipEnumNames.ContainsSecond(animationClip)) {
// This happens when we send custom clips, that can't be played by the sprite animator, so for now we
// don't log it. This warning might be useful if we seem to be missing animations from the Knights
@@ -672,6 +1031,10 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
if (AnimationEffects.TryGetValue(animationClip, out var effect)) {
var effectInfo = effect.GetEffectInfo();
+ _netClient.UpdateManager.UpdatePlayerAnimation(animationClip, 0, effectInfo);
+ } else if (SubAnimationEffects.TryGetValue(animationClip, out var subEffect)) {
+ var effectInfo = subEffect.GetEffectInfo();
+
_netClient.UpdateManager.UpdatePlayerAnimation(animationClip, 0, effectInfo);
} else {
_netClient.UpdateManager.UpdatePlayerAnimation(animationClip);
@@ -686,6 +1049,67 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
// _animationControllerWasLastSent = false;
}
+ ///
+ /// Creates hooks for the Witch Tentacles and Shaman Cancel states in
+ /// the Bind fsm once the HeroController is ready.
+ ///
+ private void CreateBindHooks(HeroController hc) {
+ // Initialize warding bell FSM if it isn't already.
+ // This fills it in with the template
+ var bellFsm = HeroController.instance.bellBindFSM;
+ if (!bellFsm.fsm.initialized) {
+ bellFsm.Init();
+ }
+
+ // Find bind FSM
+ var heroFsms = hc.GetComponents();
+
+ var bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
+ if (bindFsm == null) {
+ Logger.Warn("Unable to find Bind FSM to hook.");
+ return;
+ }
+
+ // Find FSM states to inject
+ var tentacles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
+ FsmStateActionInjector.Inject(tentacles, OnWitchTentacles, 4);
+
+ var shamanCancel = bindFsm.GetState("Shaman Air Cancel");
+ FsmStateActionInjector.Inject(shamanCancel, OnShamanCancel);
+
+ var bindInterrupt = bindFsm.GetState("Remove Silk?");
+ FsmStateActionInjector.Inject(bindInterrupt, OnBindInterrupt, 2);
+ }
+
+ ///
+ /// Animation subanimation hook for the Witch Tentacles FSM state
+ ///
+ private void OnWitchTentacles(PlayMakerFSM fsm) {
+ var dummyClip = new tk2dSpriteAnimationClip();
+ dummyClip.name = "Witch Tentacles!";
+ dummyClip.wrapMode = tk2dSpriteAnimationClip.WrapMode.Once;
+ OnAnimationEvent(dummyClip);
+ }
+
+ ///
+ /// Animation subanimation hook for the Shaman Air Cancel FSM state
+ ///
+ private void OnShamanCancel(PlayMakerFSM fsm) {
+ var dummyClip = new tk2dSpriteAnimationClip();
+ dummyClip.name = "Shaman Cancel";
+ dummyClip.wrapMode = tk2dSpriteAnimationClip.WrapMode.Once;
+ OnAnimationEvent(dummyClip);
+ }
+ ///
+ /// Animation subanimation hook for interrupted binds
+ ///
+ private void OnBindInterrupt(PlayMakerFSM fsm) {
+ var dummyClip = new tk2dSpriteAnimationClip();
+ dummyClip.name = "Bind Fail Burst";
+ dummyClip.wrapMode = tk2dSpriteAnimationClip.WrapMode.Once;
+ OnAnimationEvent(dummyClip);
+ }
+
// ///
// /// Callback method on the HeroAnimationController#Play method.
// ///
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index d12de77..aedc240 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -33,9 +33,41 @@ public void SetShouldDoDamage(bool shouldDoDamage) {
///
/// The target game object to attach the component to.
/// The number of mask of damage it should deal.
- protected static void AddDamageHeroComponent(GameObject target, int damage = 1) {
- var damageHero = target.AddComponent();
+ /// The component that was added to the game object
+ protected static DamageHero AddDamageHeroComponent(GameObject target, int damage = 1) {
+ var damageHero = target.AddComponentIfNotPresent();
damageHero.damageDealt = damage;
damageHero.OnDamagedHero = new UnityEvent();
+
+ return damageHero;
+ }
+
+ ///
+ /// Removes a component from the given game object.
+ ///
+ /// The target game object to detach the component from.
+ protected static void RemoveDamageHeroComponent(GameObject target) {
+ var damageHero = target.GetComponent();
+ if (damageHero == null) {
+ return;
+ }
+
+ Object.DestroyImmediate(damageHero);
+ }
+
+ ///
+ /// Adds or removes a component from the given game object,
+ /// depending on the PVP and team settings.
+ ///
+ /// The target game object to detatch the component to.
+ /// The number of mask of damage it should deal.
+ /// The component that was added if PVP was turned on
+ protected DamageHero? SetDamageHeroState(GameObject target, int damage = 1) {
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ return AddDamageHeroComponent(target, damage);
+ }
+
+ RemoveDamageHeroComponent(target);
+ return null;
}
}
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index dcdde7a..d3d9174 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -1,5 +1,6 @@
using System;
-using System.Collections;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using HutongGames.PlayMaker.Actions;
using SSMP.Internals;
using SSMP.Util;
@@ -12,95 +13,312 @@ namespace SSMP.Animation.Effects;
///
/// Class for the animation effect of bind (healing).
///
-internal class Bind : AnimationEffect {
+internal class Bind : DamageAnimationEffect {
+ // Effect object names
+ protected const string BindBellObjectName = "bind_bell_appear_instance";
+ protected const string BindSilkObjectName = "Bind Silk";
+ protected const string ShamanFallAnticObjectName = "Shaman_Bind_antic_silk";
+ private const string BeastCrestAnticObjectName = "Warrior_Bind_antic_silk";
+ private const string CursedBindFailObjectName = "Cursed Bind Hornet";
+ private const string WitchBindObjectName = "Whip_Bind_silk_antic";
+
+ ///
+ /// Keeps track of special bind scenarios, mostly to do with Shaman Crest.
+ ///
+ public State BindState = State.Normal;
+
///
/// Cached FSM for Hornet's bind ability.
///
private static PlayMakerFSM? _bindFsm;
+ ///
+ /// Cached effects object for Hornet's bind ability.
+ ///
private static GameObject? _localBindEffects;
-
+
+ ///
public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
- MonoBehaviourUtil.Instance.StartCoroutine(PlayBindEffect(playerObject, crestType));
- }
+ var flags = new Flags(effectInfo);
+
+ // The maggot state is cleared by the time the bind burst is sent.
+ // This method keeps track of it, although at a slight possible loss of consistency
+ var playerObjectIdentifier = playerObject.GetInstanceID();
+ if (flags.Maggoted && !(crestType == CrestType.Shaman && BindState == State.ShamanCancel)) {
+ BindBurst.MaggotedPlayers.Add(playerObjectIdentifier);
+ } else {
+ BindBurst.MaggotedPlayers.Remove(playerObjectIdentifier);
+ }
- private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType) {
var randomClipAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
-
- AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
- playAudioAction,
- randomClipAction,
- playerObject
- );
- var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
- AudioUtil.PlayAudioOneShotSingleAtPlayerObject(oneShotSingleAction, playerObject);
+ if (BindState == State.Normal) {
+ AudioUtil.PlayAudio(playAudioAction, randomClipAction, playerObject);
- _localBindEffects ??= HeroController.instance.gameObject.FindGameObjectInChildren("Bind Effects");
- if (_localBindEffects == null) {
- Logger.Warn("Could not find local Bind Effects object in hero object");
- yield break;
+ var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
+ AudioUtil.PlayAudio(oneShotSingleAction, playerObject);
}
- var bindEffects = playerObject.FindGameObjectInChildren("Bind Effects");
- if (bindEffects == null) {
- Logger.Warn("Player object does not have Bind Effects child, cannot play bind");
- yield break;
+
+ if (!CreateObjects(playerObject, out var bindEffects)) {
+ return;
}
- var bindSilkObj = bindEffects.FindGameObjectInChildren("Bind Silk");
- if (bindSilkObj == null) {
- var localBindSilkObj = _localBindEffects.FindGameObjectInChildren("Bind Silk");
- if (localBindSilkObj == null) {
- Logger.Warn("Could not find local Bind Silk object, cannot play bind");
- yield break;
+ switch (crestType) {
+ case CrestType.Beast:
+ PlayBeastBindStart(bindEffects);
+ break;
+ case CrestType.Cursed:
+ PlayCursedFail(bindEffects);
+ return;
+ case CrestType.Witch:
+ PlayWitchAnimationAntic(bindEffects);
+ break;
+ case CrestType.Shaman:
+ var shouldContinue = PickShamanAnimation(playerObject, bindEffects, flags);
+ if (!shouldContinue) {
+ return;
+ }
+ break;
+ default:
+ PlayNormalStart(bindEffects, flags);
+ break;
+ }
+
+ // If bind bell, do effects in state "Bind Bell?" and "Bind Bell Disappear?"
+ if (flags.BindBell) {
+ StartBindBell(bindEffects);
+ }
+
+ // TODO: If using reserve bind, use reserve bind animation?
+
+ // TODO: Quick Craft animations
+ }
+
+ ///
+ /// Creates the bind bell
+ ///
+ private void StartBindBell(GameObject bindEffects) {
+ Logger.Debug("Starting warding bell");
+ var bindBell = bindEffects.FindGameObjectInChildren(BindBellObjectName);
+
+ if (bindBell == null) {
+ var allObjects = Resources.FindObjectsOfTypeAll();
+ var localBell = allObjects.FirstOrDefault(o => o.name == "bind_bell_appear");
+
+ if (localBell == null) {
+ Logger.Warn("Couldn't find warding bell object");
+ return;
}
- bindSilkObj = Object.Instantiate(localBindSilkObj, bindEffects.transform);
+ bindBell = Object.Instantiate(localBell, bindEffects.transform);
+ bindBell.name = BindBellObjectName;
+
+ var follower = bindBell.GetComponent();
+ follower.target = bindEffects.transform;
+ follower.useHero = false;
+
+ var delay = bindBell.AddComponentIfNotPresent();
+ delay.time = 5f;
+ }
+
+ bindBell.SetActive(false);
+ bindBell.SetActive(true);
+ }
+
+ ///
+ /// Plays the normal silk animation
+ ///
+ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
+ Logger.Debug("Playing normal bind start animation");
+ var bindSilkObj = CreateEffectIfNotExists(bindEffects, BindSilkObjectName);
+ if (bindSilkObj == null) {
+ return;
}
var bindSilkMeshRenderer = bindSilkObj.GetComponent();
bindSilkMeshRenderer.enabled = true;
var bindSilkAnimator = bindSilkObj.GetComponent();
- bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk"));
-
- // TODO: if Beast is equipped, activate rage burst object (State: Rage Burst?)
- // TODO: if claw mirrors are equipped, do effects in state "Dazzle?"
- // TODO: if maggoted, do effects in state "Maggoted?"
- var playerAnimator = playerObject.GetComponent();
- var currentClip = playerAnimator.currentClip;
- Logger.Error($"Player Animator current clip for Bind: {currentClip.name}");
- // TODO: figure out when animation triggers happen
+ Logger.Info("Playing Bind Silk animation");
- // TODO: adjust bind time based on crest and tools
- var bindTime = 1.2f;
+ if (flags.QuickBind) {
+ bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk Quick"));
+ } else {
+ bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk"));
+ }
+ bindSilkObj.SetActive(false);
+ bindSilkObj.SetActive(true);
+ }
+
+ ///
+ /// Plays the Beast Crest specific silk animation
+ ///
+ private void PlayBeastBindStart(GameObject bindEffects) {
+ Logger.Debug("Playing Beast Crest start antic");
+ var beastAntic = CreateEffectIfNotExists(bindEffects, BeastCrestAnticObjectName);
+ if (beastAntic == null) {
+ return;
+ }
- yield return new WaitForSeconds(bindTime);
+ beastAntic.SetActive(false);
+ beastAntic.SetActive(true);
+ }
- bindSilkMeshRenderer.enabled = false;
-
- // TODO: activate object in state "Heal", last action
-
+ ///
+ /// Starts the Cursed Crest bind animation
+ ///
+ private void PlayCursedFail(GameObject bindEffects) {
+ Logger.Debug("Playing Cursed Crest bind fail animation");
+ var failAntic = bindEffects.FindGameObjectInChildren(CursedBindFailObjectName);
+
+ tk2dSpriteAnimator animator;
+ if (failAntic == null) {
+ var effects = HeroController.instance.gameObject.FindGameObjectInChildren("Effects");
+ if (effects == null) {
+ Logger.Warn("Unable to find local effects object");
+ return;
+ }
+
+ var localFailAntic = effects.FindGameObjectInChildren(CursedBindFailObjectName);
+ if (localFailAntic == null) {
+ Logger.Warn("Unable to find local cursed bind object");
+ return;
+ }
+
+ failAntic = Object.Instantiate(localFailAntic, bindEffects.transform);
+ failAntic.name = CursedBindFailObjectName;
+ failAntic.transform.SetLocalPositionZ(failAntic.transform.localPosition.z - 0.25f);
+
+ animator = failAntic.GetComponent();
+ animator.AnimationCompletedEvent += PlayNextCursedPart;
+ } else {
+ animator = failAntic.GetComponent();
+ }
+
+ failAntic.SetActive(false);
+ failAntic.SetActive(true);
+
+ animator.Play("Bind Cursed Start");
+ }
+
+ ///
+ /// Starts the next part of the Cursed Crest animation
+ ///
+ private void PlayNextCursedPart(tk2dSpriteAnimator animator, tk2dSpriteAnimationClip clip) {
+ if (clip.name == "Bind Cursed Start") {
+ animator.Play("Bind Cursed Mid");
+ } else if (clip.name == "Bind Cursed Mid") {
+ animator.Play("Bind Cursed End");
+ }
+ }
+
+ ///
+ /// Plays the Witch Crest silk bind animation
+ ///
+ private void PlayWitchAnimationAntic(GameObject bindEffects) {
+ Logger.Debug("Playing Witch Crest bind antic");
+ var silkAntic = CreateEffectIfNotExists(bindEffects, WitchBindObjectName);
+ if (silkAntic == null) {
+ return;
+ }
+
+ silkAntic.SetActive(false);
+ silkAntic.SetActive(true);
+ }
+
+ ///
+ /// Picks and plays a Shaman bind animation
+ ///
+ private bool PickShamanAnimation(GameObject playerObject, GameObject bindEffects, Flags flags) {
+ if (BindState == State.Normal) {
+ PlayShamanFall(bindEffects);
+ return true;
+ } else if (BindState == State.ShamanCancel) {
+ PlayShamanCancel(playerObject, bindEffects);
+ return false;
+ }
+
+ PlayShamanFallEnd(bindEffects);
+ PlayNormalStart(bindEffects, flags);
+ return false;
+ }
+
+ ///
+ /// Plays the Shaman Crest falling silk animation
+ ///
+ private void PlayShamanFall(GameObject bindEffects) {
+ Logger.Debug("Playing Shaman Crest bind fall animation");
+ var shamanAntic = CreateEffectIfNotExists(bindEffects, ShamanFallAnticObjectName);
+ if (shamanAntic == null) {
+ return;
+ }
+
+ var delay = shamanAntic.AddComponentIfNotPresent();
+ delay.time = 5;
+
+ shamanAntic.SetActive(false);
+ shamanAntic.SetActive(true);
+ }
+
+ ///
+ /// Plays the Shaman Crest 'failed to bind' animation
+ ///
+ private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
+ Logger.Debug("Playing Shaman Crest bind cancel/fail animation");
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
+ if (shamanAntic != null) {
+ shamanAntic.SetActive(false);
+ }
+
+ var cancelStateName = "Shaman Air Cancel";
+ var silkPuffSpawner = GetOrFindBindFsm().GetFirstAction(cancelStateName);
+
+ var audio = GetOrFindBindFsm().GetFirstAction(cancelStateName);
+ AudioUtil.PlayAudio(audio, playerObject);
+ EffectUtils.SpawnGlobalPoolObject(silkPuffSpawner, playerObject.transform, 5f);
+
+ BindBurst.StopBindBell(bindEffects);
}
+ ///
+ /// Transitions from the falling silk animation to the normal silk animation
+ ///
+ private void PlayShamanFallEnd(GameObject bindEffects) {
+ Logger.Debug("Playing Shaman Crest bind fall finished transition animation");
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
+ if (shamanAntic == null) {
+ return;
+ }
+ var animator = shamanAntic.GetComponent();
+ animator.Play("End");
+ }
+
+
+
///
- public override byte[]? GetEffectInfo() {
- // Warding bell
- // Claw mirrors
- return null;
+ public override byte[] GetEffectInfo() {
+ byte[] effectInfo = [
+ (byte) ((ToolItemManager.IsToolEquipped("Bell Bind") && !ToolItemManager.GetToolByName("Bell Bind").IsEmpty) ? 1 : 0),
+ (byte) (ToolItemManager.IsToolEquipped("Dazzle Bind") ? 1 : 0),
+ (byte) (ToolItemManager.IsToolEquipped("Dazzle Bind Upgraded") ? 1 : 0),
+ (byte) (ToolItemManager.IsToolEquipped("Quickbind") ? 1 : 0),
+ (byte) ((ToolItemManager.IsToolEquipped("Reserve Bind") && !ToolItemManager.GetToolByName("Reserve Bind").IsEmpty) ? 1 : 0),
+ (byte) (HeroController.instance.cState.isMaggoted ? 1 : 0)
+ ];
+ return effectInfo;
}
-
+
///
/// Get or find the Bind FSM on the hero object. Will be cached to .
///
/// The FSM for Bind.
/// Thrown if the FSM cannot be found, which shouldn't happen.
///
- private PlayMakerFSM GetOrFindBindFsm() {
+ protected PlayMakerFSM GetOrFindBindFsm() {
if (_bindFsm != null) {
return _bindFsm;
}
@@ -115,4 +333,126 @@ private PlayMakerFSM GetOrFindBindFsm() {
throw new InvalidOperationException("Could not find Bind FSM on hero");
}
+
+ ///
+ /// Attempts to locate and bind the 'Bind Effects' GameObject to the specified player object.
+ ///
+ /// The player's object.
+ /// The player's 'Bind Effects' object, or null if not found.
+ /// true if the 'Bind Effects' GameObject is successfully found and bound; otherwise, false.
+ protected bool CreateObjects(GameObject playerObject, [MaybeNullWhen(false)] out GameObject bindEffects) {
+ _localBindEffects ??= HeroController.instance.gameObject.FindGameObjectInChildren("Bind Effects");
+ if (_localBindEffects == null) {
+ Logger.Warn("Could not find local Bind Effects object in hero object");
+ bindEffects = null;
+ return false;
+ }
+
+ bindEffects = playerObject.FindGameObjectInChildren("Bind Effects");
+ if (bindEffects == null) {
+ Logger.Warn("Player object does not have Bind Effects child, cannot play bind");
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Finds and returns a bind effect with the specified name, creating it if it doesn't already exist.
+ ///
+ /// The player's Bind Effects object.
+ /// The name of the effect to find or create.
+ /// The effect that was found or created
+ /// Returns true if the effect was created, false if it was found.
+ protected bool CreateEffectIfNotExists(GameObject bindEffects, string objectName, out GameObject? effect) {
+ effect = bindEffects.FindGameObjectInChildren(objectName);
+ if (effect != null) {
+ return false;
+ }
+
+ var localObj = _localBindEffects!.FindGameObjectInChildren(objectName);
+ if (localObj == null) {
+ Logger.Warn($"Could not find local {objectName} object, cannot play bind effect");
+ return false;
+ }
+
+ effect = Object.Instantiate(localObj, bindEffects.transform);
+ effect.name = objectName;
+ return true;
+ }
+
+ ///
+ /// Finds and returns a bind effect with the specified name, creating it if it doesn't already exist.
+ ///
+ /// The player's Bind Effects object.
+ /// The name of the effect to find or create.
+ /// The effect that was found or created
+ protected GameObject? CreateEffectIfNotExists(GameObject bindEffects, string objectName) {
+ CreateEffectIfNotExists(bindEffects, objectName, out var effect);
+ return effect;
+ }
+
+ ///
+ /// Turns off all bind effects
+ ///
+ public static void ForceStopAllEffects(GameObject bindEffects) {
+ BindBurst.StopBindBell(bindEffects);
+
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
+ if (shamanAntic != null) {
+ shamanAntic.SetActive(false);
+ }
+
+ var silkAntic = bindEffects.FindGameObjectInChildren(WitchBindObjectName);
+ if (silkAntic != null) {
+ silkAntic.SetActive(false);
+ }
+
+ var cursedFailAntic = bindEffects.FindGameObjectInChildren(CursedBindFailObjectName);
+ if (cursedFailAntic != null) {
+ cursedFailAntic.SetActive(false);
+ }
+
+ var beastAntic = bindEffects.FindGameObjectInChildren(BeastCrestAnticObjectName);
+ if (beastAntic != null) {
+ beastAntic.SetActive(false);
+ }
+
+ var bindSilkObj = bindEffects.FindGameObjectInChildren(BindSilkObjectName);
+ if (bindSilkObj != null) {
+ bindSilkObj.SetActive(false);
+ }
+ }
+
+ ///
+ /// Shaman Crest has special animations depending on which animation is
+ /// sent, but they're all related to the bind starting
+ ///
+ public enum State {
+ Normal,
+ ShamanCancel,
+ ShamanDoneFalling
+ }
+
+ ///
+ /// Effect flags sent by the other player. Mostly items they have equipped.
+ ///
+ protected class Flags {
+ public readonly bool BindBell;
+ public readonly bool BaseMirror;
+ public readonly bool UpgradedMirror;
+ public readonly bool QuickBind;
+ public readonly bool ReserveBind;
+ public bool Maggoted; // Needs to be writable by BindBurst
+
+ public Flags(byte[]? info) {
+ if (info == null) return;
+ BindBell = info[0] == 1;
+ BaseMirror = info[1] == 1;
+ UpgradedMirror = info[2] == 1;
+ QuickBind = info[3] == 1;
+ ReserveBind = info[4] == 1;
+ Maggoted = info[5] == 1;
+ }
+ }
}
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
new file mode 100644
index 0000000..193caa5
--- /dev/null
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -0,0 +1,275 @@
+using System.Collections.Generic;
+using HutongGames.PlayMaker.Actions;
+using SSMP.Internals;
+using SSMP.Util;
+using UnityEngine;
+using Logger = SSMP.Logging.Logger;
+
+namespace SSMP.Animation.Effects;
+
+///
+/// Class for the animation effect of a bind (healing) finishing.
+///
+internal class BindBurst : Bind {
+ // Effect object names
+ private const string BeastCrestRageObjectName = "crest rage_burst_effect(Clone)";
+ private const string WitchBindCleanseObjectName = "Witch Bind Maggot Cleanse";
+ private const string WitchBindObjectName = "Witch Bind";
+ private const string HealAnimObjectName = "Heal Anim";
+ private const string HealParticleObjectName = "Pt Heal";
+
+ ///
+ /// Static instance for access by multiple animation clips in .
+ ///
+ public static readonly BindBurst Instance = new();
+
+ ///
+ /// A set of players who recently bound while maggoted
+ ///
+ public static readonly HashSet MaggotedPlayers = [];
+
+ ///
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ // Set maggot info
+ var flags = new Flags(effectInfo);
+
+ var playerObjectIdentifier = playerObject.GetInstanceID();
+ if (MaggotedPlayers.Contains(playerObjectIdentifier)) {
+ flags.Maggoted = true;
+ MaggotedPlayers.Remove(playerObjectIdentifier);
+ }
+
+ if (!CreateObjects(playerObject, out var bindEffects)) {
+ return;
+ }
+
+ // Play flag-specific effects
+ if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(playerObject, flags.UpgradedMirror);
+ if (flags.Maggoted) PlayMaggotCleanse(bindEffects, playerObject);
+
+ // Stop regardless of if it's on or not
+ StopBindBell(bindEffects);
+
+ // Play crest-specific animations
+ switch (crestType) {
+ case CrestType.Beast:
+ if (!flags.Maggoted) {
+ PlayBeastRage(bindEffects);
+ }
+ break;
+ case CrestType.Witch:
+ if (!flags.Maggoted) {
+ PlayWitchEnd(bindEffects);
+ } else {
+ PlayWitchMaggoted(bindEffects);
+ }
+ return;
+ case CrestType.Shaman:
+ PlayShamanEnd(bindEffects);
+ break;
+ }
+
+ PlayNormalEnd(bindEffects);
+ }
+
+ ///
+ /// Plays the maggot cleanse animation
+ ///
+ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
+ // Grab assets from Maggoted state
+ var stateName = "Maggoted?";
+ var maggotBurst = GetOrFindBindFsm().GetAction(stateName, 2);
+ var maggotFlash = GetOrFindBindFsm().GetAction(stateName, 4);
+ var maggotAudio = GetOrFindBindFsm().GetFirstAction(stateName);
+
+ // Spawn copies of those assets
+ EffectUtils.SpawnGlobalPoolObject(maggotBurst, bindEffects.transform, 5f);
+ EffectUtils.SpawnGlobalPoolObject(maggotFlash, bindEffects.transform, 5f);
+
+ // Play audio
+ AudioUtil.PlayAudio(maggotAudio, playerObject);
+ }
+
+ ///
+ /// Creates the appropriate Claw Mirror object.
+ /// The object will be destroyed after it finishes.
+ ///
+ private static GameObject? PrepareMirror(GameObject playerObject, SetGameObject mirrorSource) {
+ // Spawn mirror
+ var mirror = EffectUtils.SpawnGlobalPoolObject(mirrorSource.gameObject.Value, playerObject.transform, 3f);
+
+ if (mirror == null) {
+ return null;
+ }
+
+ // Remove camera and haze components
+ var shaker = mirror.GetComponentInChildren();
+ if (shaker != null) {
+ Object.DestroyImmediate(shaker);
+ }
+
+ mirror.DestroyGameObjectInChildren("haze2");
+
+ return mirror;
+ }
+
+ ///
+ /// Plays the appropriate Claw Mirror animation.
+ /// Adds a damage component if appropriate.
+ ///
+ private void PlayMirror(GameObject playerObject, bool upgraded) {
+ // Get claw prefabs
+ Logger.Debug("Playing Claw Mirror Animation");
+ var regularClaw = GetOrFindBindFsm().GetAction("Dazzle?", 3)!;
+ var upgradedClaw = GetOrFindBindFsm().GetAction("Dazzle?", 4)!;
+
+ // Create the claw
+ GameObject? claw;
+ if (upgraded) {
+ claw = PrepareMirror(playerObject, upgradedClaw);
+ } else {
+ claw = PrepareMirror(playerObject, regularClaw);
+ }
+
+ if (claw == null) {
+ return;
+ }
+
+ claw.SetActive(true);
+
+ // Add hitbox if appropriate
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ var damagerParent = claw.FindGameObjectInChildren("Trobbio_dazzle_flash");
+ var damager = damagerParent?.FindGameObjectInChildren("hero_dazzle_flash_damager");
+ if (damager == null) {
+ Logger.Warn("Couldn't find claw mirror damager");
+ return;
+ }
+
+ damager.layer = (int) GlobalEnums.PhysLayers.HERO_ATTACK;
+
+ var damageComponent = AddDamageHeroComponent(damager);
+ damageComponent.hazardType = GlobalEnums.HazardType.EXPLOSION;
+ }
+ }
+
+ ///
+ /// Stops the bind bell animation
+ ///
+ public static void StopBindBell(GameObject bindEffects) {
+ // Only bother turning it off if it's on
+ var bindBell = bindEffects.FindGameObjectInChildren(BindBellObjectName);
+ bindBell?.SetActive(false);
+ }
+
+ ///
+ /// Plays the Beast Crest specific rage animation
+ ///
+ private void PlayBeastRage(GameObject bindEffects) {
+ // Create and reactivate the rage effect
+ var beastRage = CreateEffectIfNotExists(bindEffects, BeastCrestRageObjectName);
+
+ if (beastRage != null) {
+ beastRage.SetActive(false);
+ beastRage.SetActive(true);
+ }
+ }
+
+ ///
+ /// Plays the witch maggot cleanse animation
+ ///
+ private void PlayWitchMaggoted(GameObject bindEffects) {
+ // Spawn in the special tentacles
+ var maggotCleanse = CreateEffectIfNotExists(bindEffects, WitchBindCleanseObjectName);
+
+ if (maggotCleanse != null) {
+ maggotCleanse.SetActive(false);
+ maggotCleanse.SetActive(true);
+ }
+ }
+
+ ///
+ /// Plays the Witch Crest tentancles animation.
+ /// Yes it's called Tentancles internally. Thanks TC.
+ ///
+ private void PlayWitchEnd(GameObject bindEffects) {
+ // Get bind effect
+ var effectWasCreated = CreateEffectIfNotExists(bindEffects, WitchBindObjectName, out var witchBind);
+ if (witchBind == null) {
+ return;
+ }
+
+ // Remove camera controls if object was created
+ if (effectWasCreated) {
+ var shaker = witchBind.GetComponent();
+ if (shaker != null) {
+ Object.DestroyImmediate(shaker);
+ }
+ }
+
+ // Toggle damage depending on if PVP is on or not
+ SetWitchDamagers(witchBind);
+
+ // Play tentacles audio
+ var audio = GetOrFindBindFsm().GetFirstAction("Witch Tentancles!");
+ AudioUtil.PlayAudio(audio, bindEffects.transform.parent.gameObject);
+
+ witchBind.SetActive(false);
+ witchBind.SetActive(true);
+ }
+
+ ///
+ /// Adds or removes hero damage components from Witch Crest bind
+ ///
+ private void SetWitchDamagers(GameObject witchBind) {
+ // Loop through all children, looking for all the ones called "Damager"
+ for (var i = 0; i < witchBind.transform.childCount; i++) {
+ var child = witchBind.transform.GetChild(i);
+ if (!child.name.StartsWith("Damager")) {
+ continue;
+ }
+
+ // Add or remove damage component from Damager object
+ SetDamageHeroState(child.gameObject);
+ }
+ }
+
+ ///
+ /// Stops the Shaman Crest specific silk animation
+ ///
+ private static void PlayShamanEnd(GameObject bindEffects) {
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
+ if (shamanAntic == null) {
+ return;
+ }
+ shamanAntic.SetActive(false);
+ }
+
+ ///
+ /// Plays particles and shows a flash. Happens at the end of a normal bind.
+ ///
+ private void PlayNormalEnd(GameObject bindEffects) {
+ // Play particle effect
+ CreateEffectIfNotExists(bindEffects, HealParticleObjectName, out var healParticle);
+ if (healParticle != null) {
+ healParticle.GetComponent().Play();
+ }
+
+ // Play silk animation
+ var healAnim = CreateEffectIfNotExists(bindEffects, HealAnimObjectName);
+ if (healAnim != null) {
+ healAnim.SetActive(true);
+ }
+
+ // Disable mesh renderer because the game does that. Is this needed?
+ var bindSilkObj = bindEffects.FindGameObjectInChildren(BindSilkObjectName);
+ if (bindSilkObj != null) {
+ var bindSilkMeshRenderer = bindSilkObj.GetComponent();
+ bindSilkMeshRenderer.enabled = false;
+ }
+
+ // Play audio
+ var audio = GetOrFindBindFsm().GetFirstAction("Bind Burst");
+ AudioUtil.PlayAudio(audio, bindEffects.transform.parent.gameObject);
+ }
+}
diff --git a/SSMP/Animation/Effects/BindInterrupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
new file mode 100644
index 0000000..fe8b979
--- /dev/null
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -0,0 +1,112 @@
+using HutongGames.PlayMaker.Actions;
+using SSMP.Internals;
+using SSMP.Util;
+using UnityEngine;
+using Logger = SSMP.Logging.Logger;
+
+namespace SSMP.Animation.Effects;
+
+///
+/// Class for the animation effect of a bind (healing) being interrupted by an attack.
+///
+internal class BindInterrupt : Bind {
+ ///
+ /// Static instance for access by multiple animation clips in .
+ ///
+ public static readonly BindInterrupt Instance = new();
+
+ ///
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ var flags = new Flags(effectInfo);
+
+ // Create bind effects object
+ if (!CreateObjects(playerObject, out var bindEffects)) {
+ return;
+ }
+
+ // Stop any effects currently playing, this effect "interrupts" everything else
+ ForceStopAllEffects(bindEffects);
+
+ // Play a warding bell explosion if they had it equipped, otherwise do the normal one
+ if (flags.BindBell) {
+ PlayBellExplode(bindEffects);
+ } else {
+ PlayBindInterrupt(bindEffects);
+ }
+ }
+
+ ///
+ /// Plays the normal bind interrupt animation
+ ///
+ private void PlayBindInterrupt(GameObject bindEffects) {
+ // Find prefab
+ var stateName = "Remove Silk?";
+ var bindBurstSpawner = GetOrFindBindFsm().GetFirstAction(stateName);
+
+ // Spawn in effect
+ var burst = EffectUtils.SpawnGlobalPoolObject(bindBurstSpawner, bindEffects.transform, 5f);
+ if (burst == null) {
+ Logger.Warn("Unable to create bind interrupt effect");
+ return;
+ }
+
+ // Play audio
+ var audio = GetOrFindBindFsm().GetFirstAction(stateName);
+ AudioUtil.PlayAudio(audio, bindEffects.transform.parent.gameObject);
+
+ // Remove haze and camera controls
+ burst.DestroyGameObjectInChildren("haze2");
+
+ var shaker = burst.GetComponentInChildren();
+ if (shaker != null) {
+ Object.DestroyImmediate(shaker);
+ }
+ }
+
+ ///
+ /// Creates a Warding Bell explosion
+ ///
+ private void PlayBellExplode(GameObject bindEffects) {
+ Logger.Debug("Playing Bell Burst");
+
+ // Locate warding bell FSM
+ var bellFsm = HeroController.instance.bellBindFSM;
+
+ if (bellFsm == null) {
+ Logger.Warn("Unable to find warding bell fsm");
+ return;
+ }
+
+ var stateName = "Burst";
+ var spawner = bellFsm.GetFirstAction(stateName);
+
+ // Spawn warding bell
+ var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner, bindEffects.transform, 5f);
+ if (bindBell == null) {
+ return;
+ }
+
+ // Play sound
+ var audio = bellFsm.GetFirstAction(stateName);
+ AudioUtil.PlayAudio(audio, bindBell);
+
+
+ // Remove camera control and haze
+ var shaker = bindBell.GetComponentInChildren();
+ if (shaker != null) {
+ Object.DestroyImmediate(shaker);
+ }
+
+ bindBell.DestroyGameObjectInChildren("haze2 (1)");
+
+ // Add hitbox if appropriate
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ var damager = bindBell.FindGameObjectInChildren("damager");
+ if (damager != null) {
+ AddDamageHeroComponent(damager);
+ } else {
+ Logger.Warn("Unable to add damager to warding bell burst");
+ }
+ }
+ }
+}
diff --git a/SSMP/Animation/Effects/DashSlash.cs b/SSMP/Animation/Effects/DashSlash.cs
index 43b3f08..38ba946 100644
--- a/SSMP/Animation/Effects/DashSlash.cs
+++ b/SSMP/Animation/Effects/DashSlash.cs
@@ -1,4 +1,4 @@
-using SSMP.Internals;
+using SSMP.Internals;
using SSMP.Networking.Packet;
using SSMP.Util;
using UnityEngine;
@@ -124,7 +124,7 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
// TODO: Add charged dash stab/slash, see alternative states in Sprint FSM for Architect/Toolmaster
var playAudioEvent = sprintFsm.GetFirstAction("Drill Attack Unch");
- AudioUtil.PlayAudioEventAtPlayerObject(playAudioEvent, playerObject);
+ AudioUtil.PlayAudio(playAudioEvent, playerObject);
}
}
diff --git a/SSMP/Animation/Effects/DashSlashAntic.cs b/SSMP/Animation/Effects/DashSlashAntic.cs
index 0e23af9..adf5511 100644
--- a/SSMP/Animation/Effects/DashSlashAntic.cs
+++ b/SSMP/Animation/Effects/DashSlashAntic.cs
@@ -1,4 +1,4 @@
-using HutongGames.PlayMaker.Actions;
+using HutongGames.PlayMaker.Actions;
using SSMP.Internals;
using SSMP.Util;
using UnityEngine;
@@ -30,23 +30,23 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
case CrestType.Hunter:
case CrestType.Witch:
playAudioAction = sprintFsm.GetFirstAction("Attack Antic");
- AudioUtil.PlayAudioEventAtPlayerObject(playAudioAction, playerObject);
+ AudioUtil.PlayAudio(playAudioAction, playerObject);
break;
case CrestType.Reaper:
var playRandomAudioClipAction = sprintFsm.GetFirstAction("Reaper Antic");
- AudioUtil.PlayRandomAudioClipAtPlayerObject(playRandomAudioClipAction, playerObject);
+ AudioUtil.PlayAudio(playRandomAudioClipAction, playerObject);
break;
case CrestType.Beast:
playAudioAction = sprintFsm.GetFirstAction("Warrior Antic");
- AudioUtil.PlayAudioEventAtPlayerObject(playAudioAction, playerObject);
+ AudioUtil.PlayAudio(playAudioAction, playerObject);
break;
case CrestType.Architect:
playAudioAction = sprintFsm.GetFirstAction("Drill Charge Start");
- AudioUtil.PlayAudioEventAtPlayerObject(playAudioAction, playerObject);
+ AudioUtil.PlayAudio(playAudioAction, playerObject);
break;
case CrestType.Shaman:
playAudioAction = sprintFsm.GetFirstAction("Shaman Antic");
- AudioUtil.PlayAudioEventAtPlayerObject(playAudioAction, playerObject);
+ AudioUtil.PlayAudio(playAudioAction, playerObject);
break;
default:
return;
diff --git a/SSMP/Animation/Effects/DashSlashReaper.cs b/SSMP/Animation/Effects/DashSlashReaper.cs
index 1444fae..8795dc3 100644
--- a/SSMP/Animation/Effects/DashSlashReaper.cs
+++ b/SSMP/Animation/Effects/DashSlashReaper.cs
@@ -1,4 +1,4 @@
-using SSMP.Internals;
+using SSMP.Internals;
using SSMP.Util;
using UnityEngine;
@@ -16,6 +16,6 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
// Also play an additional sound from the Sprint FSM
var sprintFsm = HeroController.instance.sprintFSM;
var playAudioAction = sprintFsm.GetFirstAction("Reaper Upper");
- AudioUtil.PlayAudioEventAtPlayerObject(playAudioAction, playerObject);
+ AudioUtil.PlayAudio(playAudioAction, playerObject);
}
}
diff --git a/SSMP/Animation/Effects/EffectUtils.cs b/SSMP/Animation/Effects/EffectUtils.cs
new file mode 100644
index 0000000..8abd61f
--- /dev/null
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -0,0 +1,79 @@
+using HutongGames.PlayMaker.Actions;
+using SSMP.Util;
+using UnityEngine;
+using Logger = SSMP.Logging.Logger;
+using Object = UnityEngine.Object;
+
+namespace SSMP.Animation.Effects;
+
+internal static class EffectUtils {
+ ///
+ /// Removes the AutoRecycleSelf component from a specified GameObject, ensuring that any active timers and
+ /// subscriptions are stopped before removal.
+ ///
+ /// The GameObject from which to remove the AutoRecycleSelf from.
+ public static void SafelyRemoveAutoRecycle(GameObject obj) {
+ var recycler = obj.GetComponent();
+ if (recycler != null) {
+ // Stop listeners before destroying
+ recycler.recycleTimerRunning = false;
+ recycler.subbed = false;
+
+ Object.Destroy(recycler);
+ }
+ }
+
+ ///
+ /// Instantiates an object from the global pool at the specified location
+ ///
+ /// The spawner responsible for providing the global pool object.
+ /// The location where the object will be spawned.
+ /// The duration, in seconds, after which the spawned object will be destroyed.
+ /// Whether to keep the parent or unparent the new object
+ /// A newly spawned GameObject from the global pool.
+ public static GameObject? SpawnGlobalPoolObject(SpawnObjectFromGlobalPool? spawner, Transform spawnLocation, float destroyAfterDelay, bool keepParent = false) {
+ if (spawner == null) {
+ Logger.Warn("Unable to find global pool object");
+ return null;
+ }
+
+ return SpawnGlobalPoolObject(spawner.gameObject.Value, spawnLocation, destroyAfterDelay, keepParent);
+ }
+
+ ///
+ /// The GameObject to spawn
+ /// The location where the object will be spawned.
+ /// The duration, in seconds, after which the spawned object will be destroyed.
+ /// Whether to keep the parent or unparent the new object
+ /// A newly spawned GameObject from the global pool.
+ public static GameObject? SpawnGlobalPoolObject(GameObject? globalObj, Transform spawnLocation, float destroyAfterDelay, bool keepParent = false) {
+ if (globalObj == null) {
+ Logger.Warn("Unable to find global pool object");
+ return null;
+ }
+
+ // Regardless of if the parent is kept, the object needs to be moved outside
+ // of the HideAndDontSave flag, which can be done by moving to the other object's scene.
+ var newObj = Object.Instantiate(globalObj, spawnLocation);
+ if (newObj == null) {
+ Logger.Warn($"Unable to spawn global pool object {globalObj.name}");
+ return null;
+ }
+
+ // Now it can be orphaned if needed.
+ if (!keepParent) {
+ newObj.transform.SetParent(null);
+ newObj.transform.position = spawnLocation.position;
+ }
+
+ newObj.SetActive(true);
+
+ SafelyRemoveAutoRecycle(newObj);
+
+ if (destroyAfterDelay > 0) {
+ newObj.DestroyAfterTime(destroyAfterDelay);
+ }
+
+ return newObj;
+ }
+}
diff --git a/SSMP/Animation/Effects/NeedleStrike.cs b/SSMP/Animation/Effects/NeedleStrike.cs
index 72b580f..7dd5ca4 100644
--- a/SSMP/Animation/Effects/NeedleStrike.cs
+++ b/SSMP/Animation/Effects/NeedleStrike.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using SSMP.Internals;
using SSMP.Networking.Packet;
using SSMP.Util;
@@ -165,7 +165,7 @@ out _
}
_reaperAudioEvent ??= GetOrFindNailArtsFsm().GetFirstAction("Antic Rpr");
- AudioUtil.PlayAudioEventAtPlayerObject(_reaperAudioEvent, playerObject);
+ AudioUtil.PlayAudio(_reaperAudioEvent, playerObject);
var strikeObj = Object.Instantiate(_chargeSlashScythe, playerAttacks.transform);
strikeObj.layer = 17;
@@ -198,7 +198,7 @@ out _
strikeObj.SetActive(true);
_wandererAudioEvent ??= GetOrFindNailArtsFsm().GetFirstAction("Wanderer Attack");
- AudioUtil.PlayAudioEventAtPlayerObject(_wandererAudioEvent, playerObject);
+ AudioUtil.PlayAudio(_wandererAudioEvent, playerObject);
}, 0.1666667f);
strikeObj.DestroyAfterTime(5f);
@@ -214,7 +214,7 @@ out _
AnimationUtil.ExecuteActionAfterDelay(() => {
_beastLeapAudioEvent ??= GetOrFindNailArtsFsm().GetFirstAction("Warrior2 Leap");
- AudioUtil.PlayAudioEventAtPlayerObject(_beastLeapAudioEvent, playerObject);
+ AudioUtil.PlayAudio(_beastLeapAudioEvent, playerObject);
}, 0.1666667f);
AnimationUtil.ExecuteActionAfterDelay(() => {
@@ -254,7 +254,7 @@ out _
strikeObj.SetActive(true);
_beastSlashAudioEvent ??= GetOrFindNailArtsFsm().GetFirstAction("Warrior2 Slash");
- AudioUtil.PlayAudioEventAtPlayerObject(_beastSlashAudioEvent, playerObject);
+ AudioUtil.PlayAudio(_beastSlashAudioEvent, playerObject);
}, 0.4166667f);
return;
@@ -289,7 +289,7 @@ out _
ModifyDamagingSlashObject(strikeObj.FindGameObjectInChildren("damager 02"), damage);
AnimationUtil.ExecuteActionAfterDelay(() => {
- AudioUtil.PlayAudioEventAtPlayerObject(_witchAudioEvent, playerObject);
+ AudioUtil.PlayAudio(_witchAudioEvent, playerObject);
strikeObj.SetActive(true);
}, 0.08f);
@@ -324,7 +324,7 @@ out _
);
AnimationUtil.ExecuteActionAfterDelay(() => {
- AudioUtil.PlayAudioEventAtPlayerObject(_architectAudioEvent, playerObject);
+ AudioUtil.PlayAudio(_architectAudioEvent, playerObject);
strikeObj.SetActive(true);
}, 0.2777778f);
diff --git a/SSMP/Animation/Effects/Slash.cs b/SSMP/Animation/Effects/Slash.cs
index 93ea975..18636cf 100644
--- a/SSMP/Animation/Effects/Slash.cs
+++ b/SSMP/Animation/Effects/Slash.cs
@@ -1,4 +1,4 @@
-using SSMP.Internals;
+using SSMP.Internals;
using UnityEngine;
namespace SSMP.Animation.Effects;
diff --git a/SSMP/Animation/IAnimationEffect.cs b/SSMP/Animation/IAnimationEffect.cs
index 1999f4e..d8a9709 100644
--- a/SSMP/Animation/IAnimationEffect.cs
+++ b/SSMP/Animation/IAnimationEffect.cs
@@ -1,4 +1,4 @@
-using SSMP.Game.Settings;
+using SSMP.Game.Settings;
using SSMP.Internals;
using UnityEngine;
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
new file mode 100644
index 0000000..034ffb9
--- /dev/null
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Linq;
+using HutongGames.PlayMaker;
+using SSMP.Logging;
+
+namespace SSMP.Fsm;
+
+internal sealed class FsmStateActionInjector : FsmStateAction {
+ private static Action? _onUninject;
+ private Action? _onStateEnter;
+ private FsmStateActionInjector(FsmState state, Action onEnter) {
+ Init(state);
+ _onStateEnter = onEnter;
+ _onUninject += Uninject;
+ }
+
+ ///
+ /// Injects a delegate action into an FSM state
+ ///
+ private void DoInjection(int index) {
+ var stateActions = State.Actions.ToList();
+ stateActions.Insert(index, this);
+ State.Actions = stateActions.ToArray();
+ State.SaveActions();
+ }
+
+ ///
+ /// Removes the delegate action from the FSM state
+ ///
+ public void Uninject() {
+ var actions = State.Actions.ToList();
+ actions.Remove(this);
+ State.Actions = actions.ToArray();
+ State.SaveActions();
+
+ _onStateEnter = null;
+ _onUninject -= Uninject;
+ }
+
+ ///
+ public override void OnEnter() {
+ if (_onStateEnter != null) {
+ try {
+ _onStateEnter.Invoke(Fsm.FsmComponent);
+ } catch (Exception e) {
+ Logger.Error(e.ToString());
+ }
+ }
+ Finish();
+ }
+
+ ///
+ /// Injects a custom action into the specified FSM state to execute when the state is entered.
+ ///
+ /// The FSM state into which the action will be injected.
+ /// An action to execute when the state is entered.
+ /// The index at which to inject the action within the state's action list. Defaults to 0.
+ /// The injected action.
+ public static FsmStateActionInjector Inject(FsmState state, Action onEnter, int actionIndex = 0) {
+ if (state == null) {
+ throw new NullReferenceException("Received null state when injecting FSM");
+ }
+
+ var action = new FsmStateActionInjector(state, onEnter);
+ action.DoInjection(actionIndex);
+
+ return action;
+ }
+
+ ///
+ /// Removes all injected FSM actions
+ ///
+ public static void UninjectAll() {
+ _onUninject?.Invoke();
+ }
+}
diff --git a/SSMP/Util/AudioUtil.cs b/SSMP/Util/AudioUtil.cs
index c675bb9..7abc643 100644
--- a/SSMP/Util/AudioUtil.cs
+++ b/SSMP/Util/AudioUtil.cs
@@ -31,7 +31,7 @@ public static AudioSource GetAudioSourceObject(GameObject gameObject) {
/// The PlayAudioEvent instance from an FSM.
/// The action to get a random audio clip from.
/// The player object to play the audio at.
- public static void PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
+ public static void PlayAudio(
PlayAudioEvent playAudioEvent,
GetRandomAudioClipFromTable getRandomAudioClipFromTable,
GameObject playerObject
@@ -53,7 +53,7 @@ GameObject playerObject
///
/// The PlayAudioEvent instance from an FSM.
/// The player object to play the audio at.
- public static void PlayAudioEventAtPlayerObject(PlayAudioEvent playAudioEvent, GameObject playerObject) {
+ public static void PlayAudio(PlayAudioEvent playAudioEvent, GameObject playerObject) {
var audioClip = playAudioEvent.audioClip.value as AudioClip;
if (audioClip == null) {
Logger.Warn("Audio clip for PlayAudioEvent is null");
@@ -99,7 +99,7 @@ GameObject playerObject
///
/// The action instance from an FSM.
/// The player object to play the audio at.
- public static void PlayRandomAudioClipAtPlayerObject(
+ public static void PlayAudio(
PlayRandomAudioClipTableV2 playAudioClip,
GameObject playerObject
) {
@@ -127,7 +127,7 @@ GameObject playerObject
///
/// The audio player action.
/// The game object for the player.
- public static void PlayAudioOneShotSingleAtPlayerObject(
+ public static void PlayAudio(
AudioPlayerOneShotSingle action,
GameObject playerObject
) {
diff --git a/SSMP/Util/GameObjectUtil.cs b/SSMP/Util/GameObjectUtil.cs
index 18dd6f1..a1a54bc 100644
--- a/SSMP/Util/GameObjectUtil.cs
+++ b/SSMP/Util/GameObjectUtil.cs
@@ -37,6 +37,26 @@ string name
return null;
}
+ ///
+ /// Destroys a GameObject with the given name in the children of the given GameObject.
+ ///
+ /// The GameObject to search in.
+ /// The name of the GameObject to search for.
+ /// Returns true if the object was destroyed, false otherwise.
+ public static bool DestroyGameObjectInChildren(this GameObject gameObject, string name) {
+ if (gameObject == null) {
+ return false;
+ }
+
+ var child = FindGameObjectInChildren(gameObject, name);
+ if (child != null) {
+ Object.Destroy(child.gameObject);
+ return true;
+ }
+
+ return false;
+ }
+
///
/// Get a list of the children of the given GameObject.
///