From 893b81d314e47ef35746a79fcf08b7f079758959 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 22 Feb 2026 18:29:28 -0500
Subject: [PATCH 01/33] Add missing animations
---
SSMP/Animation/AnimationClip.cs | 363 +++++++++++++++++++++++++++--
SSMP/Animation/AnimationManager.cs | 348 +++++++++++++++++++++++++--
2 files changed, 684 insertions(+), 27 deletions(-)
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index 0b0c350..d53f2f1 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
@@ -321,6 +468,12 @@ internal enum AnimationClip {
NeedleThrowAnticA,
NeedleThrowThrowing,
NeedleThrowCatch,
+ NeedleThrowBurst,
+ NeedleThrowOut,
+ NeedleThrowReturn,
+ NeedleThrowReturnShort,
+ NeedleThrowThread,
+ NeedleThrowThunk,
///
/// Cross Stitch in air
///
@@ -331,6 +484,18 @@ internal enum AnimationClip {
ParryStanceGround,
ParryRecover,
ParryRecoverGround,
+ ParryClash,
+ ParryClashEffect,
+ ParryDash,
+ ParryDashBurst,
+ ParryReady,
+ ParryRecoverySkid,
+ ParryStanceFlash,
+ ParryStanceFlashQ,
+ ParryThread,
+ GetParryDash,
+ GetParryEnd,
+ GetParryPrepare,
///
/// Thread Storm
///
@@ -339,24 +504,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 +539,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,
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 919c667..1f00a6f 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -57,21 +57,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 +120,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 +138,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 +148,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 +267,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 +286,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 +296,17 @@ 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 },
+ { "Somersault Pin Drop", AnimationClip.SomersaultPinDrop },
{ "Walljump Somersault", AnimationClip.WallJumpSomersault },
{ "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 },
@@ -226,47 +360,112 @@ 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 },
-
+ { "Charge Up Air", AnimationClip.ChargeUpAir },
+ { "Charge Up Bench", AnimationClip.ChargeUpBench },
+ { "Charge Up Bench Silk", AnimationClip.ChargeUpBenchSilk },
+ { "Charge Up Burst", AnimationClip.ChargeUpBurst },
+
{ "Idle Hurt", AnimationClip.IdleHurt },
{ "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,150 @@ 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 },
+
};
///
From b962ff7f4bc432617461a6aed27a2103a7013193 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 00:19:24 -0500
Subject: [PATCH 02/33] remove duplicate animations
---
SSMP/Animation/AnimationManager.cs | 2 --
1 file changed, 2 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 1f00a6f..bff6591 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -301,7 +301,6 @@ internal class AnimationManager {
{ "Wall Scramble Repeat", AnimationClip.WallScrambleRepeat },
{ "Wall Slash", AnimationClip.WallSlash },
{ "Somersault Pin Drop", AnimationClip.SomersaultPinDrop },
- { "Walljump Somersault", AnimationClip.WallJumpSomersault },
{ "Airborne", AnimationClip.Airborne },
{ "Bonked", AnimationClip.Bonked },
{ "Sprint Bonk", AnimationClip.SprintBonk },
@@ -418,7 +417,6 @@ internal class AnimationManager {
{ "Charge Up Bench Silk", AnimationClip.ChargeUpBenchSilk },
{ "Charge Up Burst", AnimationClip.ChargeUpBurst },
- { "Idle Hurt", AnimationClip.IdleHurt },
{ "TurnWalk", AnimationClip.TurnWalk },
{ "Turn Back Three Quarter", AnimationClip.TurnBackThreeQuarter },
{ "Turn Back Three Quarter EndToIdle", AnimationClip.TurnBackThreeQuarterEndToIdle },
From b21b1a24d5660cc6a4ce169e57a447941600017f Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 00:20:11 -0500
Subject: [PATCH 03/33] prevent duplicate damageHero components
---
SSMP/Animation/DamageAnimationEffect.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index d12de77..527eac7 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -34,7 +34,7 @@ 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();
+ var damageHero = target.AddComponentIfNotPresent();
damageHero.damageDealt = damage;
damageHero.OnDamagedHero = new UnityEvent();
}
From 5b5d95da11ba17e8f3a48515a1cfe7565a5f8286 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 00:22:06 -0500
Subject: [PATCH 04/33] Finalize basic bind start and end animations
---
SSMP/Animation/Effects/Bind.cs | 198 ++++++++++++++++++++++------
SSMP/Animation/Effects/BindBurst.cs | 158 ++++++++++++++++++++++
2 files changed, 313 insertions(+), 43 deletions(-)
create mode 100644 SSMP/Animation/Effects/BindBurst.cs
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index dcdde7a..40c5019 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Linq;
using HutongGames.PlayMaker.Actions;
using SSMP.Internals;
using SSMP.Util;
@@ -12,22 +13,45 @@ namespace SSMP.Animation.Effects;
///
/// Class for the animation effect of bind (healing).
///
-internal class Bind : AnimationEffect {
+internal class Bind : DamageAnimationEffect {
+
+ protected string BIND_BELL_NAME = "bind_bell_appear_instance";
+
+ protected class Flags {
+ public bool BindBell = false;
+ public bool BaseMirror = false;
+ public bool UpgradedMirror = false;
+ public bool QuickBind = false;
+ public bool ReserveBind = false;
+ public bool Maggoted = false;
+
+ 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;
+ }
+ }
+
///
/// Cached FSM for Hornet's bind ability.
///
- private static PlayMakerFSM? _bindFsm;
+ protected static PlayMakerFSM? _bindFsm;
+
+ protected static GameObject? _localBindEffects;
- private static GameObject? _localBindEffects;
-
public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
- MonoBehaviourUtil.Instance.StartCoroutine(PlayBindEffect(playerObject, crestType));
+ Flags flags = new Flags(effectInfo);
+ MonoBehaviourUtil.Instance.StartCoroutine(PlayBindEffect(playerObject, crestType, flags));
}
- private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType) {
+ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType, Flags flags) {
var randomClipAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
-
+
AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
playAudioAction,
randomClipAction,
@@ -37,70 +61,125 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType)
var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
AudioUtil.PlayAudioOneShotSingleAtPlayerObject(oneShotSingleAction, playerObject);
- _localBindEffects ??= HeroController.instance.gameObject.FindGameObjectInChildren("Bind Effects");
- if (_localBindEffects == null) {
- Logger.Warn("Could not find local Bind Effects object in hero object");
+ var created = CreateObjects(playerObject, out var bindEffects);
+ if (!created) {
yield break;
}
- var bindEffects = playerObject.FindGameObjectInChildren("Bind Effects");
- if (bindEffects == null) {
- Logger.Warn("Player object does not have Bind Effects child, cannot play bind");
- yield break;
+ // TODO: if Beast is equipped, activate rage burst object (State: Rage Burst?)
+
+ Logger.Info("Determining crest animation...");
+
+ if (crestType == CrestType.Beast) {
+ Logger.Info("Playing Beast Crest Animation");
+ PlayBeastBindStart(bindEffects);
+ } else if (crestType == CrestType.Cursed) {
+ Logger.Info("Playing Cursed Crest Animation");
+ } else if (crestType == CrestType.Witch) {
+ Logger.Info("Playing Witch Crest Animation");
+ } else if (crestType == CrestType.Shaman) {
+ Logger.Info("Playing Shaman Crest Animation");
+ } else {
+ Logger.Info("Playing Default Animation");
+ PlayNormalStart(bindEffects, flags);
}
- 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;
+ // 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: Use air animations if applicable
+
+ // TODO: Quick Craft animations
+
+ Logger.Info("Getting clip name for no reason...");
+ var playerAnimator = playerObject.GetComponent();
+ var currentClip = playerAnimator?.currentClip;
+ Logger.Info($"Player Animator current clip for Bind: {currentClip?.name}");
+ // TODO: figure out when animation triggers happen
+
+ // TODO: adjust bind time based on crest and tools
+ }
+
+ private void StartBindBell(GameObject bindEffects) {
+ var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
+
+ 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 bind bell object");
+ return;
}
- bindSilkObj = Object.Instantiate(localBindSilkObj, bindEffects.transform);
+ bindBell = GameObject.Instantiate(localBell, bindEffects.transform);
+ bindBell.name = BIND_BELL_NAME;
+
+ var follower = bindBell.GetComponent();
+ follower.target = bindEffects.transform;
+ follower.useHero = false;
+
+ var delay = bindBell.AddComponentIfNotPresent();
+ delay.time = 5f;
+ }
+
+ bindBell.SetActive(false);
+ bindBell.SetActive(true);
+ }
+
+ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
+ var bindSilkObj = CreateEffectIfNotExists(bindEffects, "Bind Silk");
+ 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"));
+ }
- yield return new WaitForSeconds(bindTime);
+ private void PlayBeastBindStart(GameObject bindEffects) {
+ var beastAntic = CreateEffectIfNotExists(bindEffects, "Warrior_Bind_antic_silk");
+ if (beastAntic == null) {
+ return;
+ }
+
+ beastAntic.SetActive(true);
- bindSilkMeshRenderer.enabled = false;
-
- // TODO: activate object in state "Heal", last action
-
-
}
///
public override byte[]? GetEffectInfo() {
- // Warding bell
- // Claw mirrors
- return null;
+ byte[] effectInfo = {
+ (byte) (ToolItemManager.IsToolEquipped("Bell Bind") ? 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") ? 1 : 0),
+ (byte) (HeroController.instance.cState.isMaggoted ? 1 : 0),
+ //(byte) (HeroController.instance.onFlatGround ? 0 : 1)
+ };
+
+ 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 +194,37 @@ private PlayMakerFSM GetOrFindBindFsm() {
throw new InvalidOperationException("Could not find Bind FSM on hero");
}
+
+ protected bool CreateObjects(GameObject playerObject, 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;
+ }
+
+ protected GameObject? CreateEffectIfNotExists(GameObject bindEffects, string objectName) {
+ var obj = bindEffects.FindGameObjectInChildren(objectName);
+ if (obj == null) {
+ var localObj = _localBindEffects.FindGameObjectInChildren(objectName);
+ if (localObj == null) {
+ Logger.Warn($"Could not find local {objectName} object, cannot play bind");
+ return null;
+ }
+
+ obj = Object.Instantiate(localObj, bindEffects.transform);
+ obj.name = objectName;
+ }
+
+ return obj;
+ }
}
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
new file mode 100644
index 0000000..ad8f9f5
--- /dev/null
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using HutongGames.PlayMaker.Actions;
+using SSMP.Internals;
+using SSMP.Util;
+using UnityEngine;
+using Logger = SSMP.Logging.Logger;
+using Object = UnityEngine.Object;
+
+namespace SSMP.Animation.Effects;
+
+internal class BindBurst : Bind {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ Flags flags = new Flags(effectInfo);
+ MonoBehaviourUtil.Instance.StartCoroutine(PlayBindBurstEffect(playerObject, crestType, flags));
+ }
+
+ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crestType, Flags flags) {
+ if (!CreateObjects(playerObject, out var bindEffects)) {
+ yield break;
+ }
+
+ if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(bindEffects, flags.UpgradedMirror);
+ if (flags.BindBell) StopBindBell(bindEffects);
+ if (flags.Maggoted) PlayMaggotCleanse(bindEffects, playerObject);
+
+ switch (crestType) {
+ case CrestType.Beast:
+ PlayBeastRage(bindEffects);
+ break;
+ //yield break;
+ default:
+ break;
+ }
+
+ PlayNormalEnd(bindEffects);
+ }
+
+ private GameObject? PrepareMirror(GameObject bindEffects, SetGameObject mirrorSource, string name) {
+ var mirror = bindEffects.FindGameObjectInChildren(name);
+ if (mirror != null) {
+ mirror.SetActive(false);
+ return mirror;
+ }
+
+ if (mirrorSource == null) {
+ Logger.Warn("Unable to find mirror source");
+ return null;
+ }
+
+ mirror = mirrorSource.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ if (mirror == null) {
+ Logger.Warn("Unable to spawn mirror");
+ return null;
+ }
+ mirror.name = name;
+
+ var shaker = mirror.GetComponentInChildren();
+ if (shaker != null) {
+ Component.DestroyImmediate(shaker);
+ }
+ var delay = mirror.AddComponentIfNotPresent();
+ delay.time = 2f;
+ EffectUtils.SafelyRemoveAutoRecycle(mirror);
+ var haze = mirror.FindGameObjectInChildren("haze2");
+ if (haze != null) {
+ GameObject.Destroy(haze);
+ }
+
+ mirror.SetActive(false);
+ return mirror;
+ }
+
+ private void PlayMirror(GameObject bindEffects, bool upgraded) {
+ Logger.Info("Playing Claw Mirror Animation");
+ var regularClaw = GetOrFindBindFsm().GetAction("Dazzle?", 3)!;
+ var upgradedClaw = GetOrFindBindFsm().GetAction("Dazzle?", 4)!;
+
+ GameObject? claw;
+ if (upgraded) claw = PrepareMirror(bindEffects, upgradedClaw, "dazzle_upgraded");
+ else claw = PrepareMirror(bindEffects, regularClaw, "dazzle_regular");
+
+ if (claw == null) {
+ Logger.Warn("Unable to create claw mirror object.");
+ return;
+ }
+
+ claw.SetActive(true);
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ var damagerParent = claw.FindGameObjectInChildren("Trobbio_dazzle_flash");
+ var damager = damagerParent?.FindGameObjectInChildren("hero_dazzle_flash_damager");
+ if (damager != null) {
+ damager.layer = (int) GlobalEnums.PhysLayers.HERO_ATTACK;
+ AddDamageHeroComponent(damager);
+ } else {
+ Logger.Warn("Couldn't find claw mirror damager");
+ }
+ } else {
+ var damager = claw.GetComponentInChildren(true);
+ if (damager != null) {
+ Component.Destroy(damager);
+ }
+ }
+ }
+
+ private void StopBindBell(GameObject bindEffects) {
+ var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
+ if (bindBell != null) {
+ bindBell.SetActive(false);
+ }
+ }
+
+ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
+ Logger.Info("Playing Maggot Animation");
+ var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
+ var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
+ var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
+
+ if (maggotBurst != null) {
+ maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ }
+ if (maggotFlash != null) {
+ maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ }
+ if (maggotAudio != null) {
+ AudioUtil.PlayAudioEventAtPlayerObject(
+ maggotAudio,
+ playerObject
+ );
+ }
+ }
+
+ private void PlayBeastRage(GameObject bindEffects) {
+ var beastRage = CreateEffectIfNotExists(bindEffects, "crest rage_burst_effect(Clone)");
+ beastRage?.SetActive(true);
+ }
+
+ private void PlayNormalEnd(GameObject bindEffects) {
+ Logger.Info("Playing Heal Particles and Anim");
+ var healParticle = CreateEffectIfNotExists(bindEffects, "Pt Heal");
+ if (healParticle != null) {
+ healParticle.GetComponent().Play();
+ }
+
+ var healAnim = CreateEffectIfNotExists(bindEffects, "Heal Anim");
+ if (healAnim != null) {
+ healAnim.SetActive(true);
+ }
+
+ var bindSilkObj = bindEffects.FindGameObjectInChildren("Bind Silk");
+ if (bindSilkObj != null) {
+ var bindSilkMeshRenderer = bindSilkObj.GetComponent();
+ bindSilkMeshRenderer.enabled = false;
+ }
+ }
+}
From 1f6ec4be97c074384b9fb96ebcac3b1a5e10915d Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 00:22:31 -0500
Subject: [PATCH 05/33] Util to prevent recycling
---
SSMP/Animation/AnimationManager.cs | 3 ++-
SSMP/Animation/Effects/EffectUtils.cs | 18 ++++++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)
create mode 100644 SSMP/Animation/Effects/EffectUtils.cs
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index bff6591..95e3561 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -645,7 +645,8 @@ 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.BindBurstGround, new BindBurst() }
};
///
diff --git a/SSMP/Animation/Effects/EffectUtils.cs b/SSMP/Animation/Effects/EffectUtils.cs
new file mode 100644
index 0000000..c4ba757
--- /dev/null
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -0,0 +1,18 @@
+using UnityEngine;
+using Logger = SSMP.Logging.Logger;
+using Object = UnityEngine.Object;
+
+namespace SSMP.Animation.Effects;
+
+internal static class EffectUtils {
+ public static void SafelyRemoveAutoRecycle(GameObject obj) {
+ var recycler = obj.GetComponent();
+ if (recycler != null) {
+ // Stop listeners before destroying
+ recycler.recycleTimerRunning = false;
+ recycler.subbed = false;
+
+ Component.Destroy(recycler);
+ }
+ }
+}
From fdce0320de658e5dad07ccfd5997583247c9ae36 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 00:23:01 -0500
Subject: [PATCH 06/33] Temporary testing component for bind animation
---
SSMP/Game/Client/PlayerManager.cs | 2 +
SSMP/Testing/PlayerAnimation.cs | 106 ++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
create mode 100644 SSMP/Testing/PlayerAnimation.cs
diff --git a/SSMP/Game/Client/PlayerManager.cs b/SSMP/Game/Client/PlayerManager.cs
index a8cdc31..b3dca56 100644
--- a/SSMP/Game/Client/PlayerManager.cs
+++ b/SSMP/Game/Client/PlayerManager.cs
@@ -149,6 +149,8 @@ private void TryCreatePlayerPool() {
// Create a player container prefab, used to spawn players
_playerContainerPrefab = new GameObject(PlayerContainerPrefabName);
+ _playerContainerPrefab.AddComponent();
+
_playerContainerPrefab.AddComponent();
var playerPrefab = new GameObject(
diff --git a/SSMP/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
new file mode 100644
index 0000000..73d593f
--- /dev/null
+++ b/SSMP/Testing/PlayerAnimation.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using GlobalSettings;
+using HarmonyLib;
+using SSMP.Animation;
+using SSMP.Animation.Effects;
+using SSMP.Game.Settings;
+using SSMP.Internals;
+using UnityEngine;
+using static UnityEngine.Rendering.RayTracingAccelerationStructure;
+using Logger = SSMP.Logging.Logger;
+using Object = UnityEngine.Object;
+
+namespace SSMP.Testing;
+
+public class PlayerAnimation : MonoBehaviour {
+
+ CrestType DetermineCrest() {
+ var playerCrest = HeroController.instance.playerData.CurrentCrestID! ?? "";
+ //if (playerCrest == null) return CrestType.Hunter;
+
+ if (playerCrest == Gameplay.HunterCrest.name) return CrestType.Hunter;
+ if (playerCrest == Gameplay.HunterCrest2.name) return CrestType.HunterV2;
+ if (playerCrest == Gameplay.HunterCrest3.name) return CrestType.HunterV3;
+ if (playerCrest == Gameplay.ReaperCrest.name) return CrestType.Reaper;
+
+ if (playerCrest == Gameplay.WandererCrest.name) return CrestType.Wanderer;
+ if (playerCrest == Gameplay.CloaklessCrest.name) return CrestType.Cloakless;
+ if (playerCrest == Gameplay.WarriorCrest.name) return CrestType.Beast;
+ if (playerCrest == Gameplay.ToolmasterCrest.name) return CrestType.Architect;
+ if (playerCrest == Gameplay.CursedCrest.name) return CrestType.Cursed;
+ if (playerCrest == Gameplay.SpellCrest.name) return CrestType.Shaman;
+ if (playerCrest == Gameplay.WitchCrest.name) return CrestType.Witch;
+
+ return CrestType.Hunter;
+ }
+
+ public bool IsEquipped(string toolName) {
+ return ToolItemManager.IsToolEquipped(toolName);
+ }
+
+ public void StartAnimation() {
+ gameObject.SetActive(true);
+
+
+ string clipName = "BindCharge Ground";
+ CrestType crest = DetermineCrest();
+
+ var hornet = HeroController.instance.gameObject;
+ var position = hornet.transform.position;
+ var playerPosition = new Vector3(position.x + 10, position.y, position.z);
+ transform.position = playerPosition;
+
+ var playerObject = transform.GetChild(0).gameObject;
+ var bind = new Bind();
+
+ var info = bind.GetEffectInfo();
+ Logger.Info(string.Join(", ", info));
+
+ //bind.UsingClawMirrors = ToolItemManager.IsToolEquipped("Dazzle Blind");
+ //bind.ClawMirrorsUpgraded = ToolItemManager.IsToolEquipped("Dazzle Blind Upgraded");
+ //bind.UsingBindBell = ToolItemManager.IsToolEquipped("Bell Bind");
+ //bind.UsingMultiBind = ToolItemManager.IsToolEquipped("Multibind");
+ //bind.UsingQuickBind = ToolItemManager.IsToolEquipped("Quickbind");
+ //bind.UsingReserveBind = ToolItemManager.IsToolEquipped("Reserve Bind");
+ //bind.InAir = !HeroController.instance.onFlatGround;
+ //bind.Maggoted = HeroController.instance.cState.isMaggoted;
+
+ SetSettings(bind);
+ bind.Play(playerObject, crest, info);
+
+ var spriteAnimator = playerObject.GetComponent();
+
+ var clip = spriteAnimator.GetClipByName(clipName);
+ spriteAnimator.PlayFromFrame(clip, 0);
+ }
+
+ void SetSettings(AnimationEffect bind) {
+ var plugin = GameObject.FindFirstObjectByType();
+ var traverse = Traverse.Create(plugin).Field("_gameManager").Field("_clientManager").Field("_serverSettings");
+ var settings = traverse.GetValue();
+
+ bind.SetServerSettings(settings);
+ }
+
+ public void StopAnimation() {
+ var clipName = "BindBurst Ground";
+
+ var playerObject = transform.GetChild(0).gameObject;
+
+ var bind = new BindBurst();
+ SetSettings(bind);
+ bind.SetShouldDoDamage(true);
+
+ var info = bind.GetEffectInfo();
+ CrestType crest = DetermineCrest();
+
+ bind.Play(playerObject, crest, info);
+
+ var spriteAnimator = playerObject.GetComponent();
+
+ var clip = spriteAnimator.GetClipByName(clipName);
+ spriteAnimator.PlayFromFrame(clip, 0);
+ }
+}
From 3a42c82b5e18e8aad9975c42623c6582e2298de5 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 01:22:54 -0500
Subject: [PATCH 07/33] add shaman bind
---
SSMP/Animation/AnimationManager.cs | 6 ++-
SSMP/Animation/Effects/Bind.cs | 35 +++++++++++++++++
SSMP/Animation/Effects/BindBurst.cs | 12 +++++-
SSMP/Testing/PlayerAnimation.cs | 61 +++++++++++++++++------------
4 files changed, 87 insertions(+), 27 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 95e3561..3468349 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -646,7 +646,9 @@ internal class AnimationManager {
{ AnimationClip.SlashChargedLoop, new NeedleStrike(true) },
{ AnimationClip.NeedleArtDash, new NeedleStrike(false) },
{ AnimationClip.BindChargeGround, new Bind() },
- { AnimationClip.BindBurstGround, new BindBurst() }
+ { AnimationClip.BindChargeGroundLand, new Bind { ShamanDoneFalling = true } },
+ { AnimationClip.BindBurstGround, new BindBurst() },
+ { AnimationClip.BindChargeHealBurst, new BindBurst() }
};
///
@@ -955,7 +957,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
return;
}
- if (_debugLogAnimations) Logger.Info($" conditions 2: {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Loop}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.LoopSection}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}");
+ //if (_debugLogAnimations) Logger.Info($" conditions 2: {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Loop}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.LoopSection}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}");
// Skip clips that do not have the wrap mode loop, loop-section or once
if (clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Loop &&
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 40c5019..b943cbc 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -17,6 +17,8 @@ internal class Bind : DamageAnimationEffect {
protected string BIND_BELL_NAME = "bind_bell_appear_instance";
+ public bool ShamanDoneFalling = false;
+
protected class Flags {
public bool BindBell = false;
public bool BaseMirror = false;
@@ -79,6 +81,14 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
Logger.Info("Playing Witch Crest Animation");
} else if (crestType == CrestType.Shaman) {
Logger.Info("Playing Shaman Crest Animation");
+ if (ShamanDoneFalling) {
+ PlayShamanFall(bindEffects);
+ yield break;
+ }
+
+ PlayShamanFallEnd(bindEffects);
+ PlayNormalStart(bindEffects, flags);
+ //PlayShamanBindStart(bindEffects);
} else {
Logger.Info("Playing Default Animation");
PlayNormalStart(bindEffects, flags);
@@ -154,8 +164,33 @@ private void PlayBeastBindStart(GameObject bindEffects) {
return;
}
+ beastAntic.SetActive(false);
beastAntic.SetActive(true);
+ }
+
+ private void PlayShamanFall(GameObject bindEffects) {
+ var shamanAntic = CreateEffectIfNotExists(bindEffects, "Shaman_Bind_antic_silk");
+ if (shamanAntic == null) {
+ return;
+ }
+ var delay = shamanAntic.AddComponentIfNotPresent();
+ delay.time = 5;
+
+ shamanAntic.SetActive(false);
+ shamanAntic.SetActive(true);
+ }
+
+ private void PlayShamanFallEnd(GameObject bindEffects) {
+ var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ if (shamanAntic == null) {
+ return;
+ }
+ var animator = shamanAntic.GetComponent();
+ animator.Play("End");
+ //var clip = animator.runtimeAnimatorController.animationClips[2];
+
+ //shamanAntic.SetActive(false);
}
///
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index ad8f9f5..05924e9 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -30,7 +30,9 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
case CrestType.Beast:
PlayBeastRage(bindEffects);
break;
- //yield break;
+ case CrestType.Shaman:
+ PlayShamanEnd(bindEffects);
+ break;
default:
break;
}
@@ -137,6 +139,14 @@ private void PlayBeastRage(GameObject bindEffects) {
beastRage?.SetActive(true);
}
+ private void PlayShamanEnd(GameObject bindEffects) {
+ var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ if (shamanAntic == null) {
+ return;
+ }
+ shamanAntic.SetActive(false);
+ }
+
private void PlayNormalEnd(GameObject bindEffects) {
Logger.Info("Playing Heal Particles and Anim");
var healParticle = CreateEffectIfNotExists(bindEffects, "Pt Heal");
diff --git a/SSMP/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
index 73d593f..eec09c8 100644
--- a/SSMP/Testing/PlayerAnimation.cs
+++ b/SSMP/Testing/PlayerAnimation.cs
@@ -36,52 +36,65 @@ CrestType DetermineCrest() {
return CrestType.Hunter;
}
- public bool IsEquipped(string toolName) {
- return ToolItemManager.IsToolEquipped(toolName);
+ void SetSettings(AnimationEffect bind) {
+ var plugin = GameObject.FindFirstObjectByType();
+ var traverse = Traverse.Create(plugin).Field("_gameManager").Field("_clientManager").Field("_serverSettings");
+ var settings = traverse.GetValue();
+
+ bind.SetServerSettings(settings);
}
- public void StartAnimation() {
+ void Init() {
gameObject.SetActive(true);
-
- string clipName = "BindCharge Ground";
- CrestType crest = DetermineCrest();
-
var hornet = HeroController.instance.gameObject;
var position = hornet.transform.position;
var playerPosition = new Vector3(position.x + 10, position.y, position.z);
transform.position = playerPosition;
+ }
+
+ public void StartPreAnimation() {
+ CrestType crest = DetermineCrest();
+
+ if (crest != CrestType.Shaman) {
+ return;
+ }
+ Init();
+ string clipName = "BindCharge Ground";
var playerObject = transform.GetChild(0).gameObject;
- var bind = new Bind();
+ var bind = new Bind { ShamanDoneFalling = false };
var info = bind.GetEffectInfo();
- Logger.Info(string.Join(", ", info));
-
- //bind.UsingClawMirrors = ToolItemManager.IsToolEquipped("Dazzle Blind");
- //bind.ClawMirrorsUpgraded = ToolItemManager.IsToolEquipped("Dazzle Blind Upgraded");
- //bind.UsingBindBell = ToolItemManager.IsToolEquipped("Bell Bind");
- //bind.UsingMultiBind = ToolItemManager.IsToolEquipped("Multibind");
- //bind.UsingQuickBind = ToolItemManager.IsToolEquipped("Quickbind");
- //bind.UsingReserveBind = ToolItemManager.IsToolEquipped("Reserve Bind");
- //bind.InAir = !HeroController.instance.onFlatGround;
- //bind.Maggoted = HeroController.instance.cState.isMaggoted;
SetSettings(bind);
bind.Play(playerObject, crest, info);
-
+
var spriteAnimator = playerObject.GetComponent();
var clip = spriteAnimator.GetClipByName(clipName);
spriteAnimator.PlayFromFrame(clip, 0);
}
- void SetSettings(AnimationEffect bind) {
- var plugin = GameObject.FindFirstObjectByType();
- var traverse = Traverse.Create(plugin).Field("_gameManager").Field("_clientManager").Field("_serverSettings");
- var settings = traverse.GetValue();
+ public void StartAnimation() {
- bind.SetServerSettings(settings);
+ Init();
+
+ string clipName = "BindCharge Ground";
+ CrestType crest = DetermineCrest();
+
+ var playerObject = transform.GetChild(0).gameObject;
+ var bind = new Bind { ShamanDoneFalling = true };
+
+ var info = bind.GetEffectInfo();
+
+ SetSettings(bind);
+ bind.Play(playerObject, crest, info);
+
+ var spriteAnimator = playerObject.GetComponent();
+
+ var clip = spriteAnimator.GetClipByName(clipName);
+ spriteAnimator.PlayFromFrame(clip, 0);
}
public void StopAnimation() {
From 1fd5ce7c8073e635c5ae0a1cdea5370485e72a5f Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sun, 8 Mar 2026 23:43:30 -0400
Subject: [PATCH 08/33] Finalize claw mirrors and shamans
---
SSMP/Animation/AnimationManager.cs | 4 +-
SSMP/Animation/Effects/Bind.cs | 44 ++++++++++++++------
SSMP/Animation/Effects/BindBurst.cs | 62 +++++++++++++++++++----------
3 files changed, 77 insertions(+), 33 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 3468349..91fb0c9 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -648,7 +648,9 @@ internal class AnimationManager {
{ AnimationClip.BindChargeGround, new Bind() },
{ AnimationClip.BindChargeGroundLand, new Bind { ShamanDoneFalling = true } },
{ AnimationClip.BindBurstGround, new BindBurst() },
- { AnimationClip.BindChargeHealBurst, new BindBurst() }
+ { AnimationClip.BindChargeHealBurst, new BindBurst() },
+ { AnimationClip.BindBurstAir, new BindBurst() },
+ { AnimationClip.RageBindBurst, new BindBurst() },
};
///
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index b943cbc..08f568c 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -68,8 +68,6 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
yield break;
}
- // TODO: if Beast is equipped, activate rage burst object (State: Rage Burst?)
-
Logger.Info("Determining crest animation...");
if (crestType == CrestType.Beast) {
@@ -81,14 +79,14 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
Logger.Info("Playing Witch Crest Animation");
} else if (crestType == CrestType.Shaman) {
Logger.Info("Playing Shaman Crest Animation");
- if (ShamanDoneFalling) {
+ if (!ShamanDoneFalling) {
PlayShamanFall(bindEffects);
+ } else {
+ PlayShamanFallEnd(bindEffects);
+ PlayNormalStart(bindEffects, flags);
yield break;
}
- PlayShamanFallEnd(bindEffects);
- PlayNormalStart(bindEffects, flags);
- //PlayShamanBindStart(bindEffects);
} else {
Logger.Info("Playing Default Animation");
PlayNormalStart(bindEffects, flags);
@@ -101,19 +99,17 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
// TODO: If using reserve bind, use reserve bind animation?
- // TODO: Use air animations if applicable
-
// TODO: Quick Craft animations
Logger.Info("Getting clip name for no reason...");
var playerAnimator = playerObject.GetComponent();
var currentClip = playerAnimator?.currentClip;
Logger.Info($"Player Animator current clip for Bind: {currentClip?.name}");
- // TODO: figure out when animation triggers happen
-
- // TODO: adjust bind time based on crest and tools
}
+ ///
+ /// Creates the bind bell
+ ///
private void StartBindBell(GameObject bindEffects) {
var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
@@ -140,7 +136,9 @@ private void StartBindBell(GameObject bindEffects) {
bindBell.SetActive(false);
bindBell.SetActive(true);
}
-
+ ///
+ /// Plays the normal silk animation
+ ///
private void PlayNormalStart(GameObject bindEffects, Flags flags) {
var bindSilkObj = CreateEffectIfNotExists(bindEffects, "Bind Silk");
if (bindSilkObj == null) {
@@ -158,6 +156,9 @@ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
else bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk"));
}
+ ///
+ /// Plays the Beast Crest specific silk animation
+ ///
private void PlayBeastBindStart(GameObject bindEffects) {
var beastAntic = CreateEffectIfNotExists(bindEffects, "Warrior_Bind_antic_silk");
if (beastAntic == null) {
@@ -168,6 +169,10 @@ private void PlayBeastBindStart(GameObject bindEffects) {
beastAntic.SetActive(true);
}
+ ///
+ /// Plays the Shaman Crest falling silk animation
+ ///
+ ///
private void PlayShamanFall(GameObject bindEffects) {
var shamanAntic = CreateEffectIfNotExists(bindEffects, "Shaman_Bind_antic_silk");
if (shamanAntic == null) {
@@ -181,6 +186,9 @@ private void PlayShamanFall(GameObject bindEffects) {
shamanAntic.SetActive(true);
}
+ ///
+ /// Transitions from the falling silk animation to the normal silk animation
+ ///
private void PlayShamanFallEnd(GameObject bindEffects) {
var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
if (shamanAntic == null) {
@@ -230,6 +238,12 @@ protected 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, out GameObject bindEffects) {
_localBindEffects ??= HeroController.instance.gameObject.FindGameObjectInChildren("Bind Effects");
if (_localBindEffects == null) {
@@ -247,6 +261,12 @@ protected bool CreateObjects(GameObject playerObject, out GameObject bindEffects
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 existing or new effect, or null if the effect cannot be found or created
protected GameObject? CreateEffectIfNotExists(GameObject bindEffects, string objectName) {
var obj = bindEffects.FindGameObjectInChildren(objectName);
if (obj == null) {
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 05924e9..6c1285e 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Text;
using HutongGames.PlayMaker.Actions;
using SSMP.Internals;
@@ -22,9 +23,11 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
yield break;
}
- if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(bindEffects, flags.UpgradedMirror);
- if (flags.BindBell) StopBindBell(bindEffects);
+ if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(playerObject, flags.UpgradedMirror);
if (flags.Maggoted) PlayMaggotCleanse(bindEffects, playerObject);
+
+ // Stop regardless of if its on or not
+ StopBindBell(bindEffects);
switch (crestType) {
case CrestType.Beast:
@@ -40,19 +43,23 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
PlayNormalEnd(bindEffects);
}
- private GameObject? PrepareMirror(GameObject bindEffects, SetGameObject mirrorSource, string name) {
- var mirror = bindEffects.FindGameObjectInChildren(name);
- if (mirror != null) {
- mirror.SetActive(false);
- return mirror;
- }
+ ///
+ /// Creates the appropriate Claw Mirror object.
+ /// The object will be destroyed after it finishes.
+ ///
+ private GameObject? PrepareMirror(GameObject playerObject, SetGameObject mirrorSource, string name) {
if (mirrorSource == null) {
Logger.Warn("Unable to find mirror source");
return null;
}
- mirror = mirrorSource.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ // This is ugly and i hate it, but it works
+ var mirror = GameObject.Instantiate(mirrorSource.gameObject.Value, playerObject.transform);
+ mirror.transform.SetParent(null);
+ mirror.transform.position = playerObject.transform.position;
+ mirror.SetActive(true);
+
if (mirror == null) {
Logger.Warn("Unable to spawn mirror");
return null;
@@ -63,26 +70,31 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
if (shaker != null) {
Component.DestroyImmediate(shaker);
}
- var delay = mirror.AddComponentIfNotPresent();
- delay.time = 2f;
+
EffectUtils.SafelyRemoveAutoRecycle(mirror);
+ mirror.DestroyAfterTime(2f);
+
var haze = mirror.FindGameObjectInChildren("haze2");
if (haze != null) {
GameObject.Destroy(haze);
}
- mirror.SetActive(false);
+ //mirror.SetActive(false);
return mirror;
}
- private void PlayMirror(GameObject bindEffects, bool upgraded) {
+ ///
+ /// Plays the appropriate Claw Mirror animation.
+ /// Adds a damage component if appropriate.
+ ///
+ private void PlayMirror(GameObject playerObject, bool upgraded) {
Logger.Info("Playing Claw Mirror Animation");
var regularClaw = GetOrFindBindFsm().GetAction("Dazzle?", 3)!;
var upgradedClaw = GetOrFindBindFsm().GetAction("Dazzle?", 4)!;
GameObject? claw;
- if (upgraded) claw = PrepareMirror(bindEffects, upgradedClaw, "dazzle_upgraded");
- else claw = PrepareMirror(bindEffects, regularClaw, "dazzle_regular");
+ if (upgraded) claw = PrepareMirror(playerObject, upgradedClaw, "dazzle_upgraded");
+ else claw = PrepareMirror(playerObject, regularClaw, "dazzle_regular");
if (claw == null) {
Logger.Warn("Unable to create claw mirror object.");
@@ -99,14 +111,12 @@ private void PlayMirror(GameObject bindEffects, bool upgraded) {
} else {
Logger.Warn("Couldn't find claw mirror damager");
}
- } else {
- var damager = claw.GetComponentInChildren(true);
- if (damager != null) {
- Component.Destroy(damager);
- }
}
}
+ ///
+ /// Stops the bind bell animation
+ ///
private void StopBindBell(GameObject bindEffects) {
var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
if (bindBell != null) {
@@ -114,6 +124,9 @@ private void StopBindBell(GameObject bindEffects) {
}
}
+ ///
+ /// Plays the maggot cleanse animation
+ ///
private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
Logger.Info("Playing Maggot Animation");
var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
@@ -134,11 +147,17 @@ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject)
}
}
+ ///
+ /// Plays the Beast Crest specific rage animation
+ ///
private void PlayBeastRage(GameObject bindEffects) {
var beastRage = CreateEffectIfNotExists(bindEffects, "crest rage_burst_effect(Clone)");
beastRage?.SetActive(true);
}
+ ///
+ /// Stops the Shaman Crest specific silk animation
+ ///
private void PlayShamanEnd(GameObject bindEffects) {
var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
if (shamanAntic == null) {
@@ -147,6 +166,9 @@ private void PlayShamanEnd(GameObject bindEffects) {
shamanAntic.SetActive(false);
}
+ ///
+ /// Plays particles and shows a flash
+ ///
private void PlayNormalEnd(GameObject bindEffects) {
Logger.Info("Playing Heal Particles and Anim");
var healParticle = CreateEffectIfNotExists(bindEffects, "Pt Heal");
From f20f9ce22045289349a7357553d25370cf77a146 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 00:30:57 -0400
Subject: [PATCH 09/33] Add cursed crest
---
SSMP/Animation/Effects/Bind.cs | 41 +++++++++++++++++++++++++++++++++
SSMP/Testing/PlayerAnimation.cs | 3 ++-
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 08f568c..f94da68 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -75,6 +75,8 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
PlayBeastBindStart(bindEffects);
} else if (crestType == CrestType.Cursed) {
Logger.Info("Playing Cursed Crest Animation");
+ PlayCursedFail(bindEffects);
+ yield break;
} else if (crestType == CrestType.Witch) {
Logger.Info("Playing Witch Crest Animation");
} else if (crestType == CrestType.Shaman) {
@@ -168,6 +170,45 @@ private void PlayBeastBindStart(GameObject bindEffects) {
beastAntic.SetActive(false);
beastAntic.SetActive(true);
}
+
+ private void PlayCursedFail(GameObject bindEffects) {
+ var failAntic = bindEffects.FindGameObjectInChildren("cursed_bind_fail");
+
+ 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("Cursed Bind Hornet");
+ if (localFailAntic == null) {
+ Logger.Warn("Unable to find local cursed bind object");
+ return;
+ }
+
+ failAntic = GameObject.Instantiate(localFailAntic, bindEffects.transform);
+ failAntic.name = "cursed_bind_fail";
+ animator = failAntic.GetComponent();
+ animator.AnimationCompletedEvent += PlayNextCursedPart;
+ } else {
+ animator = failAntic.GetComponent();
+ }
+
+ failAntic.SetActive(false);
+ failAntic.SetActive(true);
+
+ animator.Play("Bind Cursed Start");
+ }
+
+ 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 Shaman Crest falling silk animation
diff --git a/SSMP/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
index eec09c8..e9d1b2f 100644
--- a/SSMP/Testing/PlayerAnimation.cs
+++ b/SSMP/Testing/PlayerAnimation.cs
@@ -46,6 +46,7 @@ void SetSettings(AnimationEffect bind) {
void Init() {
gameObject.SetActive(true);
+ ToolItemManager.SetEquippedCrest(Gameplay.CursedCrest.name);
var hornet = HeroController.instance.gameObject;
var position = hornet.transform.position;
@@ -54,12 +55,12 @@ void Init() {
}
public void StartPreAnimation() {
+ Init();
CrestType crest = DetermineCrest();
if (crest != CrestType.Shaman) {
return;
}
- Init();
string clipName = "BindCharge Ground";
var playerObject = transform.GetChild(0).gameObject;
From 5edff7f3b3f8d93e466bc07a25d37f32a5a50af8 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:14:36 -0400
Subject: [PATCH 10/33] Create method to intercept FSM states
---
SSMP/Fsm/FsmStateActionInjector.cs | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 SSMP/Fsm/FsmStateActionInjector.cs
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
new file mode 100644
index 0000000..15db8ec
--- /dev/null
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HutongGames.PlayMaker;
+
+namespace SSMP.Fsm;
+
+internal class FsmStateActionInjector : FsmStateAction {
+
+ private Action? _onStateEnter;
+ public override void OnEnter() {
+ _onStateEnter?.Invoke(Fsm.FsmComponent);
+ Finish();
+ }
+
+ public static void Inject(FsmState state, Action onEnter) {
+ Inject(state, 0, onEnter);
+ }
+ public static void Inject(FsmState state, int actionIndex, Action onEnter) {
+ var action = new FsmStateActionInjector();
+ action.Fsm = state.Fsm;
+ action._onStateEnter = onEnter;
+
+ var stateActions = state.Actions.ToList();
+ stateActions.Insert(actionIndex, action);
+ state.Actions = stateActions.ToArray();
+ state.SaveActions();
+ }
+}
From e2201b278cb525fa640b6a5940ce621478c8a9cb Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:15:26 -0400
Subject: [PATCH 11/33] Add system for sub-effects
---
SSMP/Animation/AnimationClip.cs | 3 ++
SSMP/Animation/AnimationManager.cs | 65 ++++++++++++++++++++++++++++--
2 files changed, 64 insertions(+), 4 deletions(-)
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index d53f2f1..4884316 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -756,4 +756,7 @@ internal enum AnimationClip {
NailArtChargeEnd,
WallSlideEnd,
HazardDeath,
+
+ // Sub-animation names
+ WitchTentacles,
}
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 91fb0c9..6b26f99 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;
@@ -615,6 +616,8 @@ internal class AnimationManager {
{ "Wound Double Strike", AnimationClip.WoundDoubleStrike },
{ "Wound Zap", AnimationClip.WoundZap },
+ { "Witch Tentacles!", AnimationClip.WitchTentacles }
+
};
///
@@ -653,6 +656,10 @@ internal class AnimationManager {
{ AnimationClip.RageBindBurst, new BindBurst() },
};
+ private static readonly Dictionary SubAnimationEffects = new() {
+ { AnimationClip.WitchTentacles, new BindBurst() }
+ };
+
///
/// The net client for sending animation updates.
///
@@ -732,6 +739,10 @@ public void Initialize(ServerSettings serverSettings) {
foreach (var effect in AnimationEffects.Values) {
effect.SetServerSettings(serverSettings);
}
+
+ foreach (var effect in SubAnimationEffects.Values) {
+ effect.SetServerSettings(serverSettings);
+ }
}
///
@@ -749,6 +760,14 @@ public void RegisterHooks() {
EventHooks.SpriteAnimatorWarpClipToLocalTime += Tk2dSpriteAnimatorOnWarpClipToLocalTime;
EventHooks.SpriteAnimatorProcessEvents += Tk2dSpriteAnimatorOnProcessEvents;
+ // Register FSM hooks for certain bind actions
+ if (HeroController.SilentInstance != null) {
+ CreateWitchTentaclesHook(HeroController.instance);
+ } else {
+ HeroController.OnHeroInstanceSet += CreateWitchTentaclesHook;
+ }
+
+
// Register a callback so we know when the dash has finished
// On.HeroController.CancelDash += HeroControllerOnCancelDash;
@@ -806,7 +825,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");
@@ -854,6 +877,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
@@ -950,7 +978,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
return;
}
- if (_debugLogAnimations) Logger.Info($" conditions 1: {clip.name.Equals(_lastAnimationClip)}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}, {!AllowedLoopAnimations.Contains(clip.name)}");
+ //if (_debugLogAnimations) Logger.Info($" conditions 1: {clip.name.Equals(_lastAnimationClip)}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}, {!AllowedLoopAnimations.Contains(clip.name)}");
// Skip event handling when we already handled this clip, unless it is a clip with wrap mode once
if (clip.name.Equals(_lastAnimationClip)
@@ -997,12 +1025,15 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
if (AnimationEffects.TryGetValue(animationClip, out var effect)) {
var effectInfo = effect.GetEffectInfo();
+ } else if (SubAnimationEffects.TryGetValue(animationClip, out var subEffect)) {
+ var effectInfo = subEffect.GetEffectInfo();
+
_netClient.UpdateManager.UpdatePlayerAnimation(animationClip, 0, effectInfo);
} else {
_netClient.UpdateManager.UpdatePlayerAnimation(animationClip);
}
- if (_debugLogAnimations) Logger.Info($" Sending animation: {animationClip}");
+ //if (_debugLogAnimations) Logger.Info($" Sending animation: {animationClip}");
// Update the last clip name, since it changed
_lastAnimationClip = clip.name;
@@ -1011,6 +1042,31 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
// _animationControllerWasLastSent = false;
}
+ var heroFsms = hc.GetComponents();
+ PlayMakerFSM bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
+ if (bindFsm != null) {
+ // Find witch crest tenticles
+ var tenticles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
+ if (tenticles != null) {
+ FsmStateActionInjector.Inject(tenticles, 4, OnWitchTentacles);
+ } else {
+ Logger.Warn("Unable to find Witch Tentacles! state");
+ }
+ } else {
+ Logger.Warn("Unable to find Bind FSM");
+ }
+ }
+
+ ///
+ /// 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);
+ }
// ///
// /// Callback method on the HeroAnimationController#Play method.
// ///
@@ -1168,7 +1224,7 @@ float time
var frame = clip.frames[index];
if (index == 0 || frame.triggerEvent || AllowedLoopAnimations.Contains(clip.name)) {
- if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnWarpClipToLocalTime: {clip.name}, conditions: {index == 0}, {frame.triggerEvent}, {AllowedLoopAnimations.Contains(clip.name)}");
+ //if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnWarpClipToLocalTime: {clip.name}, conditions: {index == 0}, {frame.triggerEvent}, {AllowedLoopAnimations.Contains(clip.name)}");
OnAnimationEvent(clip);
}
}
@@ -1221,6 +1277,7 @@ int direction
// }
if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnProcessEvents: {self.CurrentClip.name}, conditions: {i}, {frames[i].triggerEvent}");
+ //if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnProcessEvents: {self.CurrentClip.name}, conditions: {i}, {frames[i].triggerEvent}");
// OnAnimationEvent(self.CurrentClip);
}
}
From ea6de8b745e51525bfba9fe4c70e084565c99bb4 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:15:34 -0400
Subject: [PATCH 12/33] Finalize witch crest
---
SSMP/Animation/Effects/Bind.cs | 67 ++++++++++++++++++++++++++---
SSMP/Animation/Effects/BindBurst.cs | 62 ++++++++++++++++----------
2 files changed, 100 insertions(+), 29 deletions(-)
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index f94da68..220cf2e 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -54,11 +54,13 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
var randomClipAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
- AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
- playAudioAction,
- randomClipAction,
- playerObject
- );
+ if (!ShamanDoneFalling) {
+ AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
+ playAudioAction,
+ randomClipAction,
+ playerObject
+ );
+ }
var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
AudioUtil.PlayAudioOneShotSingleAtPlayerObject(oneShotSingleAction, playerObject);
@@ -79,6 +81,15 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
yield break;
} else if (crestType == CrestType.Witch) {
Logger.Info("Playing Witch Crest Animation");
+ if (flags.Maggoted) {
+ PlayWitchMaggoted(bindEffects);
+ PlayMaggotCleanse(bindEffects, playerObject);
+ yield break;
+ } else {
+ PlayWitchAnimationAntic(bindEffects);
+ //PlayWitchAnimation(bindEffects);
+ }
+
} else if (crestType == CrestType.Shaman) {
Logger.Info("Playing Shaman Crest Animation");
if (!ShamanDoneFalling) {
@@ -156,6 +167,9 @@ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
if (flags.QuickBind) bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk Quick"));
else bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk"));
+
+ bindSilkObj.SetActive(false);
+ bindSilkObj.SetActive(true);
}
///
@@ -209,7 +223,48 @@ private void PlayNextCursedPart(tk2dSpriteAnimator animator, tk2dSpriteAnimation
animator.Play("Bind Cursed End");
}
}
-
+
+ private void PlayWitchMaggoted(GameObject bindEffects) {
+ var maggotCleanse = CreateEffectIfNotExists(bindEffects, "Witch Bind Maggot Cleanse");
+ if (maggotCleanse != null) {
+ maggotCleanse.SetActive(false);
+ maggotCleanse.SetActive(true);
+ }
+ }
+
+ private void PlayWitchAnimationAntic(GameObject bindEffects) {
+ var silkAntic = CreateEffectIfNotExists(bindEffects, "Whip_Bind_silk_antic");
+ if (silkAntic == null) {
+ return;
+ }
+
+ silkAntic.SetActive(false);
+ silkAntic.SetActive(true);
+ }
+
+ ///
+ /// Plays the maggot cleanse animation
+ ///
+ protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
+ Logger.Info("Playing Maggot Animation");
+ var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
+ var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
+ var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
+
+ if (maggotBurst != null) {
+ maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ }
+ if (maggotFlash != null) {
+ maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ }
+ if (maggotAudio != null) {
+ AudioUtil.PlayAudioEventAtPlayerObject(
+ maggotAudio,
+ playerObject
+ );
+ }
+ }
+
///
/// Plays the Shaman Crest falling silk animation
///
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 6c1285e..6d05ad1 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -33,6 +33,9 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
case CrestType.Beast:
PlayBeastRage(bindEffects);
break;
+ case CrestType.Witch:
+ PlayWitchEnd(bindEffects);
+ break;
case CrestType.Shaman:
PlayShamanEnd(bindEffects);
break;
@@ -124,29 +127,6 @@ private void StopBindBell(GameObject bindEffects) {
}
}
- ///
- /// Plays the maggot cleanse animation
- ///
- private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
- Logger.Info("Playing Maggot Animation");
- var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
- var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
- var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
-
- if (maggotBurst != null) {
- maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- }
- if (maggotFlash != null) {
- maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- }
- if (maggotAudio != null) {
- AudioUtil.PlayAudioEventAtPlayerObject(
- maggotAudio,
- playerObject
- );
- }
- }
-
///
/// Plays the Beast Crest specific rage animation
///
@@ -155,6 +135,42 @@ private void PlayBeastRage(GameObject bindEffects) {
beastRage?.SetActive(true);
}
+ private void PlayWitchEnd(GameObject bindEffects) {
+ var witchBind = bindEffects.FindGameObjectInChildren("Witch Bind");
+ if (witchBind == null) {
+ var localWitchBind = _localBindEffects.FindGameObjectInChildren("Witch Bind");
+ if (localWitchBind == null) {
+ Logger.Warn("Unable to find local Witch Bind object");
+ return;
+ }
+
+ witchBind = GameObject.Instantiate(localWitchBind, bindEffects.transform);
+
+ var shaker = witchBind.GetComponent();
+ if (shaker != null) {
+ Component.DestroyImmediate(shaker);
+ }
+
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ SetWitchDamagers(witchBind);
+ }
+ }
+
+ witchBind.SetActive(false);
+ witchBind.SetActive(true);
+ }
+
+ private void SetWitchDamagers(GameObject witchBind) {
+ for (int i = 0; i < witchBind.transform.childCount; i++) {
+ var child = witchBind.transform.GetChild(i);
+ if (!child.name.StartsWith("Damager")) {
+ continue;
+ }
+
+ AddDamageHeroComponent(child.gameObject);
+ }
+ }
+
///
/// Stops the Shaman Crest specific silk animation
///
From 2567a379bc5bc1685fcfcb74dfd9d1f6b0eb7dc1 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:37:40 -0400
Subject: [PATCH 13/33] Accidentally committed log removals
---
SSMP/Animation/AnimationManager.cs | 11 ++++++-----
SSMP/Testing/PlayerAnimation.cs | 2 +-
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 6b26f99..38582da 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -978,7 +978,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
return;
}
- //if (_debugLogAnimations) Logger.Info($" conditions 1: {clip.name.Equals(_lastAnimationClip)}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}, {!AllowedLoopAnimations.Contains(clip.name)}");
+ if (_debugLogAnimations) Logger.Info($" conditions 1: {clip.name.Equals(_lastAnimationClip)}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}, {!AllowedLoopAnimations.Contains(clip.name)}");
// Skip event handling when we already handled this clip, unless it is a clip with wrap mode once
if (clip.name.Equals(_lastAnimationClip)
@@ -987,7 +987,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
return;
}
- //if (_debugLogAnimations) Logger.Info($" conditions 2: {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Loop}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.LoopSection}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}");
+ if (_debugLogAnimations) Logger.Info($" conditions 2: {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Loop}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.LoopSection}, {clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Once}");
// Skip clips that do not have the wrap mode loop, loop-section or once
if (clip.wrapMode != tk2dSpriteAnimationClip.WrapMode.Loop &&
@@ -1025,6 +1025,7 @@ 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();
@@ -1033,7 +1034,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
_netClient.UpdateManager.UpdatePlayerAnimation(animationClip);
}
- //if (_debugLogAnimations) Logger.Info($" Sending animation: {animationClip}");
+ if (_debugLogAnimations) Logger.Info($" Sending animation: {animationClip}");
// Update the last clip name, since it changed
_lastAnimationClip = clip.name;
@@ -1042,6 +1043,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
// _animationControllerWasLastSent = false;
}
+ private void CreateWitchTentaclesHook(HeroController hc) {
var heroFsms = hc.GetComponents();
PlayMakerFSM bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
if (bindFsm != null) {
@@ -1224,7 +1226,7 @@ float time
var frame = clip.frames[index];
if (index == 0 || frame.triggerEvent || AllowedLoopAnimations.Contains(clip.name)) {
- //if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnWarpClipToLocalTime: {clip.name}, conditions: {index == 0}, {frame.triggerEvent}, {AllowedLoopAnimations.Contains(clip.name)}");
+ if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnWarpClipToLocalTime: {clip.name}, conditions: {index == 0}, {frame.triggerEvent}, {AllowedLoopAnimations.Contains(clip.name)}");
OnAnimationEvent(clip);
}
}
@@ -1277,7 +1279,6 @@ int direction
// }
if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnProcessEvents: {self.CurrentClip.name}, conditions: {i}, {frames[i].triggerEvent}");
- //if (_debugLogAnimations) Logger.Info($"OnAnimationEvent from tk2dSpriteAnimatorOnProcessEvents: {self.CurrentClip.name}, conditions: {i}, {frames[i].triggerEvent}");
// OnAnimationEvent(self.CurrentClip);
}
}
diff --git a/SSMP/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
index e9d1b2f..dd738f9 100644
--- a/SSMP/Testing/PlayerAnimation.cs
+++ b/SSMP/Testing/PlayerAnimation.cs
@@ -46,7 +46,7 @@ void SetSettings(AnimationEffect bind) {
void Init() {
gameObject.SetActive(true);
- ToolItemManager.SetEquippedCrest(Gameplay.CursedCrest.name);
+ //ToolItemManager.SetEquippedCrest(Gameplay.CursedCrest.name);
var hornet = HeroController.instance.gameObject;
var position = hornet.transform.position;
From b335f556e60648e7274d9b7531c8c4aab3e23ec0 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 21:08:36 -0400
Subject: [PATCH 14/33] Fix architect charged downspike
---
SSMP/Animation/AnimationClip.cs | 4 ++++
SSMP/Animation/AnimationManager.cs | 2 ++
2 files changed, 6 insertions(+)
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index 4884316..a9c8677 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -415,6 +415,10 @@ internal enum AnimationClip {
///
DownSpikeCharge,
///
+ /// Fully charged down slash with Architect crest
+ ///
+ DownSpikeCharged,
+ ///
/// Slash while sprinting/dashing with Architect crest
///
DashAttackCharge,
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 38582da..8bff850 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -344,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 },
@@ -629,6 +630,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) },
From 0bb6f5c92710c9b6960667f974fe04b6a2a42b98 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 21:13:12 -0400
Subject: [PATCH 15/33] use static instance of bindburst
---
SSMP/Animation/AnimationManager.cs | 10 +++++-----
SSMP/Animation/Effects/Bind.cs | 2 +-
SSMP/Animation/Effects/BindBurst.cs | 6 ++++++
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 8bff850..af39e0e 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -652,14 +652,14 @@ internal class AnimationManager {
{ AnimationClip.NeedleArtDash, new NeedleStrike(false) },
{ AnimationClip.BindChargeGround, new Bind() },
{ AnimationClip.BindChargeGroundLand, new Bind { ShamanDoneFalling = true } },
- { AnimationClip.BindBurstGround, new BindBurst() },
- { AnimationClip.BindChargeHealBurst, new BindBurst() },
- { AnimationClip.BindBurstAir, new BindBurst() },
- { AnimationClip.RageBindBurst, new BindBurst() },
+ { AnimationClip.BindBurstGround, BindBurst.Instance },
+ { AnimationClip.BindChargeHealBurst, BindBurst.Instance },
+ { AnimationClip.BindBurstAir, BindBurst.Instance },
+ { AnimationClip.RageBindBurst, BindBurst.Instance },
};
private static readonly Dictionary SubAnimationEffects = new() {
- { AnimationClip.WitchTentacles, new BindBurst() }
+ { AnimationClip.WitchTentacles, BindBurst.Instance }
};
///
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 220cf2e..88f4c7a 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -15,7 +15,7 @@ namespace SSMP.Animation.Effects;
///
internal class Bind : DamageAnimationEffect {
- protected string BIND_BELL_NAME = "bind_bell_appear_instance";
+ protected const string BIND_BELL_NAME = "bind_bell_appear_instance";
public bool ShamanDoneFalling = false;
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 6d05ad1..e7401cc 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -13,6 +13,12 @@
namespace SSMP.Animation.Effects;
internal class BindBurst : Bind {
+ ///
+ /// Static instance for access by multiple animation clips in .
+ ///
+ private static BindBurst? _instance;
+ ///
+ public new static BindBurst Instance => _instance ??= new BindBurst();
public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
MonoBehaviourUtil.Instance.StartCoroutine(PlayBindBurstEffect(playerObject, crestType, flags));
From 3c8e537ec412909c75a1558128d2449a9d0fdbe0 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Mon, 9 Mar 2026 22:59:54 -0400
Subject: [PATCH 16/33] finish shaman, fix maggot effect
---
SSMP/Animation/AnimationClip.cs | 1 +
SSMP/Animation/AnimationEffect.cs | 2 +-
SSMP/Animation/AnimationManager.cs | 59 ++++++++++-----
SSMP/Animation/DamageAnimationEffect.cs | 2 +-
SSMP/Animation/Effects/Bind.cs | 91 +++++++++++------------
SSMP/Animation/Effects/BindBurst.cs | 52 +++++++++++--
SSMP/Animation/Effects/DashSlash.cs | 4 +-
SSMP/Animation/Effects/DashSlashAntic.cs | 4 +-
SSMP/Animation/Effects/DashSlashReaper.cs | 4 +-
SSMP/Animation/Effects/DownSpike.cs | 2 +-
SSMP/Animation/Effects/EffectUtils.cs | 13 ++++
SSMP/Animation/Effects/NeedleStrike.cs | 4 +-
SSMP/Animation/Effects/Slash.cs | 4 +-
SSMP/Animation/Effects/SlashBase.cs | 2 +-
SSMP/Animation/IAnimationEffect.cs | 5 +-
SSMP/Testing/PlayerAnimation.cs | 39 +++++++---
16 files changed, 194 insertions(+), 94 deletions(-)
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index a9c8677..cb1bd97 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -763,4 +763,5 @@ internal enum AnimationClip {
// Sub-animation names
WitchTentacles,
+ ShamanCancel
}
diff --git a/SSMP/Animation/AnimationEffect.cs b/SSMP/Animation/AnimationEffect.cs
index 56e6960..ed09520 100644
--- a/SSMP/Animation/AnimationEffect.cs
+++ b/SSMP/Animation/AnimationEffect.cs
@@ -14,7 +14,7 @@ internal abstract class AnimationEffect : IAnimationEffect {
protected ServerSettings ServerSettings = null!;
///
- public abstract void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
+ public abstract void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
///
public abstract byte[]? GetEffectInfo();
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index af39e0e..30ceb4e 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -617,7 +617,8 @@ internal class AnimationManager {
{ "Wound Double Strike", AnimationClip.WoundDoubleStrike },
{ "Wound Zap", AnimationClip.WoundZap },
- { "Witch Tentacles!", AnimationClip.WitchTentacles }
+ { "Witch Tentacles!", AnimationClip.WitchTentacles },
+ { "Shaman Cancel", AnimationClip.ShamanCancel },
};
@@ -651,15 +652,16 @@ internal class AnimationManager {
{ AnimationClip.SlashChargedLoop, new NeedleStrike(true) },
{ AnimationClip.NeedleArtDash, new NeedleStrike(false) },
{ AnimationClip.BindChargeGround, new Bind() },
- { AnimationClip.BindChargeGroundLand, new Bind { ShamanDoneFalling = true } },
+ { AnimationClip.BindChargeGroundLand, new Bind { BindState = Bind.State.ShamanDoneFalling } },
{ AnimationClip.BindBurstGround, BindBurst.Instance },
{ AnimationClip.BindChargeHealBurst, BindBurst.Instance },
{ AnimationClip.BindBurstAir, BindBurst.Instance },
- { AnimationClip.RageBindBurst, BindBurst.Instance },
+ { AnimationClip.RageBindBurst, BindBurst.Instance }
};
private static readonly Dictionary SubAnimationEffects = new() {
- { AnimationClip.WitchTentacles, BindBurst.Instance }
+ { AnimationClip.WitchTentacles, BindBurst.Instance },
+ { AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } }
};
///
@@ -764,9 +766,9 @@ public void RegisterHooks() {
// Register FSM hooks for certain bind actions
if (HeroController.SilentInstance != null) {
- CreateWitchTentaclesHook(HeroController.instance);
+ CreateBindHooks(HeroController.instance);
} else {
- HeroController.OnHeroInstanceSet += CreateWitchTentaclesHook;
+ HeroController.OnHeroInstanceSet += CreateBindHooks;
}
@@ -856,6 +858,7 @@ public void OnPlayerAnimationUpdate(ushort id, int clipId, int frame, byte[]? ef
animationEffect.Play(
playerObject,
crestType,
+ id,
effectInfo
);
}
@@ -1045,32 +1048,54 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
// _animationControllerWasLastSent = false;
}
- private void CreateWitchTentaclesHook(HeroController hc) {
+ ///
+ /// Creates hooks for the Witch Tentacles and Shaman Cancel states in
+ /// the Bind fsm once the HeroController is ready.
+ ///
+ private void CreateBindHooks(HeroController hc) {
var heroFsms = hc.GetComponents();
PlayMakerFSM bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
- if (bindFsm != null) {
- // Find witch crest tenticles
- var tenticles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
- if (tenticles != null) {
- FsmStateActionInjector.Inject(tenticles, 4, OnWitchTentacles);
- } else {
- Logger.Warn("Unable to find Witch Tentacles! state");
- }
+ if (bindFsm == null) {
+ Logger.Warn("Unable to find Bind FSM to hook.");
+ return;
+ }
+
+ // Find witch crest tenticles
+ var tenticles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
+ if (tenticles != null) {
+ FsmStateActionInjector.Inject(tenticles, 4, OnWitchTentacles);
+ } else {
+ Logger.Warn("Unable to find Witch Tentacles! state");
+ }
+
+ var shamanCancel = bindFsm.GetState("Shaman Air Cancel");
+ if (shamanCancel != null) {
+ FsmStateActionInjector.Inject(shamanCancel, OnShamanCancel);
} else {
- Logger.Warn("Unable to find Bind FSM");
+ Logger.Warn("Unable to find Shaman Air Cancel state");
}
}
///
/// 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);
+ }
+
// ///
// /// Callback method on the HeroAnimationController#Play method.
// ///
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index 527eac7..f22cd04 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -14,7 +14,7 @@ internal abstract class DamageAnimationEffect : AnimationEffect {
protected bool ShouldDoDamage;
///
- public abstract override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
+ public abstract override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
///
public abstract override byte[]? GetEffectInfo();
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 88f4c7a..cf46e68 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -15,9 +15,15 @@ namespace SSMP.Animation.Effects;
///
internal class Bind : DamageAnimationEffect {
+ public enum State {
+ Normal,
+ ShamanCancel,
+ ShamanDoneFalling
+ }
+
protected const string BIND_BELL_NAME = "bind_bell_appear_instance";
- public bool ShamanDoneFalling = false;
+ public State BindState = State.Normal;
protected class Flags {
public bool BindBell = false;
@@ -45,8 +51,17 @@ public Flags(byte[]? info) {
protected static GameObject? _localBindEffects;
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
+
+ // The maggot state is cleared by the time the bind burst is sent.
+ // This keeps track of it, although at a slight possible loss of consistancy
+ if (flags.Maggoted && !(crestType == CrestType.Shaman && BindState == State.ShamanCancel)) {
+ BindBurst.MaggotedPlayers.Add(playerId);
+ } else {
+ BindBurst.MaggotedPlayers.Remove(playerId);
+ }
+
MonoBehaviourUtil.Instance.StartCoroutine(PlayBindEffect(playerObject, crestType, flags));
}
@@ -54,7 +69,7 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
var randomClipAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
- if (!ShamanDoneFalling) {
+ if (BindState == State.Normal) {
AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
playAudioAction,
randomClipAction,
@@ -81,19 +96,14 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
yield break;
} else if (crestType == CrestType.Witch) {
Logger.Info("Playing Witch Crest Animation");
- if (flags.Maggoted) {
- PlayWitchMaggoted(bindEffects);
- PlayMaggotCleanse(bindEffects, playerObject);
- yield break;
- } else {
- PlayWitchAnimationAntic(bindEffects);
- //PlayWitchAnimation(bindEffects);
- }
-
+ PlayWitchAnimationAntic(bindEffects);
} else if (crestType == CrestType.Shaman) {
Logger.Info("Playing Shaman Crest Animation");
- if (!ShamanDoneFalling) {
+ if (BindState == State.Normal) {
PlayShamanFall(bindEffects);
+ } else if (BindState == State.ShamanCancel) {
+ PlayShamanCancel(playerObject, bindEffects);
+ yield break;
} else {
PlayShamanFallEnd(bindEffects);
PlayNormalStart(bindEffects, flags);
@@ -149,6 +159,7 @@ private void StartBindBell(GameObject bindEffects) {
bindBell.SetActive(false);
bindBell.SetActive(true);
}
+
///
/// Plays the normal silk animation
///
@@ -224,14 +235,6 @@ private void PlayNextCursedPart(tk2dSpriteAnimator animator, tk2dSpriteAnimation
}
}
- private void PlayWitchMaggoted(GameObject bindEffects) {
- var maggotCleanse = CreateEffectIfNotExists(bindEffects, "Witch Bind Maggot Cleanse");
- if (maggotCleanse != null) {
- maggotCleanse.SetActive(false);
- maggotCleanse.SetActive(true);
- }
- }
-
private void PlayWitchAnimationAntic(GameObject bindEffects) {
var silkAntic = CreateEffectIfNotExists(bindEffects, "Whip_Bind_silk_antic");
if (silkAntic == null) {
@@ -242,29 +245,6 @@ private void PlayWitchAnimationAntic(GameObject bindEffects) {
silkAntic.SetActive(true);
}
- ///
- /// Plays the maggot cleanse animation
- ///
- protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
- Logger.Info("Playing Maggot Animation");
- var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
- var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
- var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
-
- if (maggotBurst != null) {
- maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- }
- if (maggotFlash != null) {
- maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- }
- if (maggotAudio != null) {
- AudioUtil.PlayAudioEventAtPlayerObject(
- maggotAudio,
- playerObject
- );
- }
- }
-
///
/// Plays the Shaman Crest falling silk animation
///
@@ -282,6 +262,24 @@ private void PlayShamanFall(GameObject bindEffects) {
shamanAntic.SetActive(true);
}
+ private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
+ var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ if (shamanAntic != null) {
+ shamanAntic.SetActive(false);
+ }
+
+ var silkPuffSpawner = GetOrFindBindFsm().GetFirstAction("Shaman Air Cancel");
+ if (silkPuffSpawner == null) {
+ Logger.Warn("Unable to find FSM action for Shaman Air Cancel");
+ return;
+ }
+
+ var globalSilkPuff = silkPuffSpawner.gameObject.Value;
+ EffectUtils.SpawnGlobalPoolObject(globalSilkPuff, playerObject.transform, false);
+
+ BindBurst.Instance.StopBindBell(bindEffects);
+ }
+
///
/// Transitions from the falling silk animation to the normal silk animation
///
@@ -305,10 +303,9 @@ private void PlayShamanFallEnd(GameObject bindEffects) {
(byte) (ToolItemManager.IsToolEquipped("Dazzle Bind Upgraded") ? 1 : 0),
(byte) (ToolItemManager.IsToolEquipped("Quickbind") ? 1 : 0),
(byte) (ToolItemManager.IsToolEquipped("Reserve Bind") ? 1 : 0),
- (byte) (HeroController.instance.cState.isMaggoted ? 1 : 0),
- //(byte) (HeroController.instance.onFlatGround ? 0 : 1)
+ (byte) (HeroController.instance.cState.isMaggoted ? 1 : 0)
};
-
+ Logger.Info(HeroController.instance.cState.isMaggoted ? "MAGGOTS" : "NO MAGGOTS");
return effectInfo;
}
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index e7401cc..41c09e2 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -18,9 +18,14 @@ internal class BindBurst : Bind {
///
private static BindBurst? _instance;
///
- public new static BindBurst Instance => _instance ??= new BindBurst();
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public static BindBurst Instance => _instance ??= new BindBurst();
+ public static HashSet MaggotedPlayers = new HashSet();
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
+ if (MaggotedPlayers.Contains(playerId)) {
+ flags.Maggoted = true;
+ MaggotedPlayers.Remove(playerId);
+ }
MonoBehaviourUtil.Instance.StartCoroutine(PlayBindBurstEffect(playerObject, crestType, flags));
}
@@ -37,10 +42,16 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
switch (crestType) {
case CrestType.Beast:
- PlayBeastRage(bindEffects);
+ if (!flags.Maggoted) {
+ PlayBeastRage(bindEffects);
+ }
break;
case CrestType.Witch:
- PlayWitchEnd(bindEffects);
+ if (!flags.Maggoted) {
+ PlayWitchEnd(bindEffects);
+ } else {
+ PlayWitchMaggoted(bindEffects);
+ }
break;
case CrestType.Shaman:
PlayShamanEnd(bindEffects);
@@ -52,6 +63,29 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
PlayNormalEnd(bindEffects);
}
+ ///
+ /// Plays the maggot cleanse animation
+ ///
+ protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
+ Logger.Info("Playing Maggot Animation");
+ var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
+ var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
+ var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
+
+ if (maggotBurst != null) {
+ maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ }
+ if (maggotFlash != null) {
+ maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ }
+ if (maggotAudio != null) {
+ AudioUtil.PlayAudioEventAtPlayerObject(
+ maggotAudio,
+ playerObject
+ );
+ }
+ }
+
///
/// Creates the appropriate Claw Mirror object.
/// The object will be destroyed after it finishes.
@@ -126,7 +160,7 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
///
/// Stops the bind bell animation
///
- private void StopBindBell(GameObject bindEffects) {
+ public void StopBindBell(GameObject bindEffects) {
var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
if (bindBell != null) {
bindBell.SetActive(false);
@@ -141,6 +175,14 @@ private void PlayBeastRage(GameObject bindEffects) {
beastRage?.SetActive(true);
}
+ private void PlayWitchMaggoted(GameObject bindEffects) {
+ var maggotCleanse = CreateEffectIfNotExists(bindEffects, "Witch Bind Maggot Cleanse");
+ if (maggotCleanse != null) {
+ maggotCleanse.SetActive(false);
+ maggotCleanse.SetActive(true);
+ }
+ }
+
private void PlayWitchEnd(GameObject bindEffects) {
var witchBind = bindEffects.FindGameObjectInChildren("Witch Bind");
if (witchBind == null) {
diff --git a/SSMP/Animation/Effects/DashSlash.cs b/SSMP/Animation/Effects/DashSlash.cs
index 43b3f08..14edeb0 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;
@@ -25,7 +25,7 @@ public DashSlash(DashSlashType dashSlashType) {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
if (effectInfo == null || effectInfo.Length < 1) {
Logger.Error("Could not get null or empty effect info for DashAttack");
return;
diff --git a/SSMP/Animation/Effects/DashSlashAntic.cs b/SSMP/Animation/Effects/DashSlashAntic.cs
index 0e23af9..78c4845 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;
@@ -23,7 +23,7 @@ private DashSlashAntic() {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
var sprintFsm = HeroController.instance.sprintFSM;
PlayAudioEvent playAudioAction;
switch (crestType) {
diff --git a/SSMP/Animation/Effects/DashSlashReaper.cs b/SSMP/Animation/Effects/DashSlashReaper.cs
index 1444fae..e095316 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;
@@ -9,7 +9,7 @@ namespace SSMP.Animation.Effects;
///
internal class DashSlashReaper : SlashBase {
///
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
// Call the base function with the correct parameters
Play(playerObject, crestType, effectInfo, SlashType.Dash);
diff --git a/SSMP/Animation/Effects/DownSpike.cs b/SSMP/Animation/Effects/DownSpike.cs
index 88e7110..4b174a3 100644
--- a/SSMP/Animation/Effects/DownSpike.cs
+++ b/SSMP/Animation/Effects/DownSpike.cs
@@ -11,7 +11,7 @@ namespace SSMP.Animation.Effects;
///
internal class DownSpike : SlashBase {
///
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
if (effectInfo == null || effectInfo.Length < 1) {
Logger.Error("Could not get null or empty effect info for SlashBase");
return;
diff --git a/SSMP/Animation/Effects/EffectUtils.cs b/SSMP/Animation/Effects/EffectUtils.cs
index c4ba757..6a029ff 100644
--- a/SSMP/Animation/Effects/EffectUtils.cs
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -15,4 +15,17 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
Component.Destroy(recycler);
}
}
+
+ public static GameObject SpawnGlobalPoolObject(GameObject globalObj, Transform spawnLocation, bool keepParent = false) {
+ var newObj = GameObject.Instantiate(globalObj, spawnLocation);
+
+ if (!keepParent) {
+ newObj.transform.SetParent(null);
+ newObj.transform.position = spawnLocation.position;
+ }
+ newObj.SetActive(true);
+
+ SafelyRemoveAutoRecycle(newObj);
+ return newObj;
+ }
}
diff --git a/SSMP/Animation/Effects/NeedleStrike.cs b/SSMP/Animation/Effects/NeedleStrike.cs
index 72b580f..a50d76f 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;
@@ -102,7 +102,7 @@ public NeedleStrike(bool witchLoop) {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
if (effectInfo == null || effectInfo.Length < 1) {
Logger.Error("Could not get null or empty effect info for SlashBase");
return;
diff --git a/SSMP/Animation/Effects/Slash.cs b/SSMP/Animation/Effects/Slash.cs
index 93ea975..c4350c5 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;
@@ -21,7 +21,7 @@ public Slash(SlashType slashType) {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
// Call the base function with the correct parameters
Play(playerObject, crestType, effectInfo, _slashType);
}
diff --git a/SSMP/Animation/Effects/SlashBase.cs b/SSMP/Animation/Effects/SlashBase.cs
index cff731b..e789410 100644
--- a/SSMP/Animation/Effects/SlashBase.cs
+++ b/SSMP/Animation/Effects/SlashBase.cs
@@ -16,7 +16,7 @@ namespace SSMP.Animation.Effects;
///
internal abstract class SlashBase : ParryableEffect {
///
- public abstract override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
+ public abstract override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
///
public override byte[] GetEffectInfo() {
diff --git a/SSMP/Animation/IAnimationEffect.cs b/SSMP/Animation/IAnimationEffect.cs
index 1999f4e..f23ed45 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;
@@ -13,8 +13,9 @@ internal interface IAnimationEffect {
///
/// The GameObject representing the player.
/// The type of crest the player is using.
+ /// The ID of the player sending the animation
/// A byte array containing effect info.
- void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
+ void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
///
/// Get the effect info corresponding to this effect.
diff --git a/SSMP/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
index dd738f9..446b15f 100644
--- a/SSMP/Testing/PlayerAnimation.cs
+++ b/SSMP/Testing/PlayerAnimation.cs
@@ -54,7 +54,7 @@ void Init() {
transform.position = playerPosition;
}
- public void StartPreAnimation() {
+ public void StartAnimation1() {
Init();
CrestType crest = DetermineCrest();
@@ -64,12 +64,12 @@ public void StartPreAnimation() {
string clipName = "BindCharge Ground";
var playerObject = transform.GetChild(0).gameObject;
- var bind = new Bind { ShamanDoneFalling = false };
+ var bind = new Bind { BindState = Bind.State.Normal };
var info = bind.GetEffectInfo();
SetSettings(bind);
- bind.Play(playerObject, crest, info);
+ bind.Play(playerObject, crest, 1, info);
var spriteAnimator = playerObject.GetComponent();
@@ -77,7 +77,28 @@ public void StartPreAnimation() {
spriteAnimator.PlayFromFrame(clip, 0);
}
- public void StartAnimation() {
+ public void StartAnimation2() {
+ var clipName = "BindBurst Ground";
+
+ var playerObject = transform.GetChild(0).gameObject;
+
+ var bind = new BindBurst();
+ SetSettings(bind);
+ bind.SetShouldDoDamage(true);
+
+ var info = bind.GetEffectInfo();
+ CrestType crest = DetermineCrest();
+
+ bind.Play(playerObject, crest, 1, info);
+
+ var spriteAnimator = playerObject.GetComponent();
+
+ var clip = spriteAnimator.GetClipByName(clipName);
+ spriteAnimator.PlayFromFrame(clip, 0);
+ }
+
+
+ public void StartAnimation3() {
Init();
@@ -85,12 +106,12 @@ public void StartAnimation() {
CrestType crest = DetermineCrest();
var playerObject = transform.GetChild(0).gameObject;
- var bind = new Bind { ShamanDoneFalling = true };
+ var bind = new Bind { BindState = Bind.State.ShamanDoneFalling };
var info = bind.GetEffectInfo();
SetSettings(bind);
- bind.Play(playerObject, crest, info);
+ bind.Play(playerObject, crest, 1, info);
var spriteAnimator = playerObject.GetComponent();
@@ -98,19 +119,19 @@ public void StartAnimation() {
spriteAnimator.PlayFromFrame(clip, 0);
}
- public void StopAnimation() {
+ public void StartAnimation4() {
var clipName = "BindBurst Ground";
var playerObject = transform.GetChild(0).gameObject;
- var bind = new BindBurst();
+ var bind = new Bind { BindState = Bind.State.ShamanCancel };
SetSettings(bind);
bind.SetShouldDoDamage(true);
var info = bind.GetEffectInfo();
CrestType crest = DetermineCrest();
- bind.Play(playerObject, crest, info);
+ bind.Play(playerObject, crest, 1, info);
var spriteAnimator = playerObject.GetComponent();
From 902bc1dd0d48afc044274ea4e64d5f8e4e94d848 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Tue, 10 Mar 2026 00:46:01 -0400
Subject: [PATCH 17/33] clean up, add docs
---
SSMP/Animation/Effects/Bind.cs | 114 ++++++++++++++++++++--------
SSMP/Animation/Effects/BindBurst.cs | 10 +++
2 files changed, 92 insertions(+), 32 deletions(-)
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index cf46e68..2268695 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -55,7 +55,7 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
Flags flags = new Flags(effectInfo);
// The maggot state is cleared by the time the bind burst is sent.
- // This keeps track of it, although at a slight possible loss of consistancy
+ // This method keeps track of it, although at a slight possible loss of consistancy
if (flags.Maggoted && !(crestType == CrestType.Shaman && BindState == State.ShamanCancel)) {
BindBurst.MaggotedPlayers.Add(playerId);
} else {
@@ -87,32 +87,25 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
Logger.Info("Determining crest animation...");
- if (crestType == CrestType.Beast) {
- Logger.Info("Playing Beast Crest Animation");
- PlayBeastBindStart(bindEffects);
- } else if (crestType == CrestType.Cursed) {
- Logger.Info("Playing Cursed Crest Animation");
- PlayCursedFail(bindEffects);
- yield break;
- } else if (crestType == CrestType.Witch) {
- Logger.Info("Playing Witch Crest Animation");
- PlayWitchAnimationAntic(bindEffects);
- } else if (crestType == CrestType.Shaman) {
- Logger.Info("Playing Shaman Crest Animation");
- if (BindState == State.Normal) {
- PlayShamanFall(bindEffects);
- } else if (BindState == State.ShamanCancel) {
- PlayShamanCancel(playerObject, bindEffects);
+ switch(crestType) {
+ case CrestType.Beast:
+ PlayBeastBindStart(bindEffects);
+ break;
+ case CrestType.Cursed:
+ PlayCursedFail(bindEffects);
yield break;
- } else {
- PlayShamanFallEnd(bindEffects);
+ case CrestType.Witch:
+ PlayWitchAnimationAntic(bindEffects);
+ break;
+ case CrestType.Shaman:
+ var shouldContinue = PickShamanAnimation(playerObject, bindEffects, flags);
+ if (!shouldContinue) {
+ yield break;
+ }
+ break;
+ default:
PlayNormalStart(bindEffects, flags);
- yield break;
- }
-
- } else {
- Logger.Info("Playing Default Animation");
- PlayNormalStart(bindEffects, flags);
+ break;
}
// If bind bell, do effects in state "Bind Bell?" and "Bind Bell Disappear?"
@@ -124,7 +117,6 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
// TODO: Quick Craft animations
- Logger.Info("Getting clip name for no reason...");
var playerAnimator = playerObject.GetComponent();
var currentClip = playerAnimator?.currentClip;
Logger.Info($"Player Animator current clip for Bind: {currentClip?.name}");
@@ -134,6 +126,7 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
/// Creates the bind bell
///
private void StartBindBell(GameObject bindEffects) {
+ Logger.Debug("Starting bind bell");
var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
if (bindBell == null) {
@@ -164,6 +157,7 @@ private void StartBindBell(GameObject bindEffects) {
/// Plays the normal silk animation
///
private void PlayNormalStart(GameObject bindEffects, Flags flags) {
+ Logger.Debug("Playing normal bind start animation");
var bindSilkObj = CreateEffectIfNotExists(bindEffects, "Bind Silk");
if (bindSilkObj == null) {
return;
@@ -187,6 +181,7 @@ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
/// Plays the Beast Crest specific silk animation
///
private void PlayBeastBindStart(GameObject bindEffects) {
+ Logger.Debug("Playing Beast Crest start antic");
var beastAntic = CreateEffectIfNotExists(bindEffects, "Warrior_Bind_antic_silk");
if (beastAntic == null) {
return;
@@ -196,7 +191,11 @@ private void PlayBeastBindStart(GameObject bindEffects) {
beastAntic.SetActive(true);
}
+ ///
+ /// Starts the Cursed Crest bind animation
+ ///
private void PlayCursedFail(GameObject bindEffects) {
+ Logger.Debug("Playing Cursed Crest bind fail animation");
var failAntic = bindEffects.FindGameObjectInChildren("cursed_bind_fail");
tk2dSpriteAnimator animator;
@@ -227,6 +226,9 @@ private void PlayCursedFail(GameObject bindEffects) {
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");
@@ -235,7 +237,11 @@ private void PlayNextCursedPart(tk2dSpriteAnimator animator, tk2dSpriteAnimation
}
}
+ ///
+ /// Plays the Witch Crest silk bind animation
+ ///
private void PlayWitchAnimationAntic(GameObject bindEffects) {
+ Logger.Debug("Playing Witch Crest bind antic");
var silkAntic = CreateEffectIfNotExists(bindEffects, "Whip_Bind_silk_antic");
if (silkAntic == null) {
return;
@@ -245,11 +251,27 @@ private void PlayWitchAnimationAntic(GameObject bindEffects) {
silkAntic.SetActive(true);
}
+
+ 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;
+ } else {
+ 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, "Shaman_Bind_antic_silk");
if (shamanAntic == null) {
return;
@@ -263,6 +285,7 @@ private void PlayShamanFall(GameObject bindEffects) {
}
private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
+ Logger.Debug("Playing Shaman Crest bind cancel/fail animation");
var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
if (shamanAntic != null) {
shamanAntic.SetActive(false);
@@ -284,25 +307,23 @@ private void PlayShamanCancel(GameObject playerObject, GameObject 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("Shaman_Bind_antic_silk");
if (shamanAntic == null) {
return;
}
var animator = shamanAntic.GetComponent();
animator.Play("End");
- //var clip = animator.runtimeAnimatorController.animationClips[2];
-
- //shamanAntic.SetActive(false);
}
///
public override byte[]? GetEffectInfo() {
byte[] effectInfo = {
- (byte) (ToolItemManager.IsToolEquipped("Bell Bind") ? 1 : 0),
+ (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") ? 1 : 0),
+ (byte) ((ToolItemManager.IsToolEquipped("Reserve Bind") && !ToolItemManager.GetToolByName("Reserve Bind").IsEmpty) ? 1 : 0),
(byte) (HeroController.instance.cState.isMaggoted ? 1 : 0)
};
Logger.Info(HeroController.instance.cState.isMaggoted ? "MAGGOTS" : "NO MAGGOTS");
@@ -375,4 +396,33 @@ protected bool CreateObjects(GameObject playerObject, out GameObject bindEffects
return obj;
}
+
+ public static void ForceStopAllEffects(GameObject bindEffects) {
+ BindBurst.Instance.StopBindBell(bindEffects);
+
+ var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ if (shamanAntic != null) {
+ shamanAntic.SetActive(false);
+ }
+
+ var silkAntic = bindEffects.FindGameObjectInChildren("Whip_Bind_silk_antic");
+ if (silkAntic != null) {
+ silkAntic.SetActive(false);
+ }
+
+ var cursedFailAntic = bindEffects.FindGameObjectInChildren("cursed_bind_fail");
+ if (cursedFailAntic != null) {
+ cursedFailAntic.SetActive(false);
+ }
+
+ var beastAntic = bindEffects.FindGameObjectInChildren("Warrior_Bind_antic_silk");
+ if (beastAntic != null) {
+ beastAntic.SetActive(false);
+ }
+
+ var bindSilkObj = bindEffects.FindGameObjectInChildren("Bind Silk");
+ if (bindSilkObj != null) {
+ bindSilkObj.SetActive(false);
+ }
+ }
}
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 41c09e2..a9adf56 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -175,6 +175,9 @@ private void PlayBeastRage(GameObject bindEffects) {
beastRage?.SetActive(true);
}
+ ///
+ /// Plays the witch maggot cleanse animation
+ ///
private void PlayWitchMaggoted(GameObject bindEffects) {
var maggotCleanse = CreateEffectIfNotExists(bindEffects, "Witch Bind Maggot Cleanse");
if (maggotCleanse != null) {
@@ -183,6 +186,10 @@ private void PlayWitchMaggoted(GameObject bindEffects) {
}
}
+ ///
+ /// Plays the Witch Crest tentancles animation
+ /// Yes it's called Tentancles internally. Thanks TC.
+ ///
private void PlayWitchEnd(GameObject bindEffects) {
var witchBind = bindEffects.FindGameObjectInChildren("Witch Bind");
if (witchBind == null) {
@@ -208,6 +215,9 @@ private void PlayWitchEnd(GameObject bindEffects) {
witchBind.SetActive(true);
}
+ ///
+ /// Adds hero damage components to Witch Crest bind if PVP is on
+ ///
private void SetWitchDamagers(GameObject witchBind) {
for (int i = 0; i < witchBind.transform.childCount; i++) {
var child = witchBind.transform.GetChild(i);
From d80d43b86931e6766ab79b7dad53d5f43fa70ddd Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Tue, 10 Mar 2026 00:46:26 -0400
Subject: [PATCH 18/33] add bind fail effect plus exploding warding bell
---
SSMP/Animation/AnimationClip.cs | 3 +-
SSMP/Animation/AnimationManager.cs | 19 ++++-
SSMP/Animation/Effects/BindFail.cs | 110 +++++++++++++++++++++++++++++
3 files changed, 129 insertions(+), 3 deletions(-)
create mode 100644 SSMP/Animation/Effects/BindFail.cs
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index cb1bd97..4b0764b 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -763,5 +763,6 @@ internal enum AnimationClip {
// Sub-animation names
WitchTentacles,
- ShamanCancel
+ ShamanCancel,
+ BindFailBurst,
}
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 30ceb4e..efc69cd 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -619,7 +619,7 @@ internal class AnimationManager {
{ "Witch Tentacles!", AnimationClip.WitchTentacles },
{ "Shaman Cancel", AnimationClip.ShamanCancel },
-
+ { "Bind Fail Burst", AnimationClip.BindFailBurst }
};
///
@@ -661,7 +661,8 @@ internal class AnimationManager {
private static readonly Dictionary SubAnimationEffects = new() {
{ AnimationClip.WitchTentacles, BindBurst.Instance },
- { AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } }
+ { AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } },
+ { AnimationClip.BindFailBurst, BindFail.Instance }
};
///
@@ -1053,6 +1054,7 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
/// the Bind fsm once the HeroController is ready.
///
private void CreateBindHooks(HeroController hc) {
+ HeroController.instance.bellBindFSM.Init();
var heroFsms = hc.GetComponents();
PlayMakerFSM bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
if (bindFsm == null) {
@@ -1074,6 +1076,11 @@ private void CreateBindHooks(HeroController hc) {
} else {
Logger.Warn("Unable to find Shaman Air Cancel state");
}
+
+ var bindFail = bindFsm.GetState("Remove Silk?");
+ if (bindFsm != null) {
+ FsmStateActionInjector.Inject(bindFail, 2, OnBindFail);
+ }
}
///
@@ -1096,6 +1103,14 @@ private void OnShamanCancel(PlayMakerFSM fsm) {
OnAnimationEvent(dummyClip);
}
+ private void OnBindFail(PlayMakerFSM fsm) {
+ Logger.Warn("PLAYING BIND FAIL");
+ 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/Effects/BindFail.cs b/SSMP/Animation/Effects/BindFail.cs
new file mode 100644
index 0000000..19ac915
--- /dev/null
+++ b/SSMP/Animation/Effects/BindFail.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using HutongGames.PlayMaker.Actions;
+using SSMP.Internals;
+using SSMP.Util;
+using UnityEngine;
+using static UnityEngine.ParticleSystem;
+using Logger = SSMP.Logging.Logger;
+
+namespace SSMP.Animation.Effects;
+
+internal class BindFail : Bind {
+ ///
+ /// Static instance for access by multiple animation clips in .
+ ///
+ private static BindFail? _instance;
+ ///
+ public static BindFail Instance => _instance ??= new BindFail();
+ public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ Flags flags = new Flags(effectInfo);
+
+ if (!CreateObjects(playerObject, out var bindEffects)) {
+ return;
+ }
+
+ ForceStopAllEffects(bindEffects);
+
+ if (flags.BindBell) {
+ PlayBellBurst(bindEffects);
+ } else {
+ PlayBindBurst(bindEffects);
+ }
+ }
+
+ private void PlayBindBurst(GameObject bindEffects) {
+ var bellBurstSpawner = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
+ if (bellBurstSpawner == null) {
+ Logger.Warn("Unable to find Bell Burst spawner");
+ return;
+ }
+
+ var globalBellBurst = bellBurstSpawner.gameObject.Value;
+ var burst = EffectUtils.SpawnGlobalPoolObject(globalBellBurst, bindEffects.transform);
+
+ if (burst == null) {
+ Logger.Warn("Unable to create Bell Burst");
+ return;
+ }
+
+ burst.DestroyAfterTime(5f);
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ AddDamageHeroComponent(burst);
+ }
+
+ var haze = burst.FindGameObjectInChildren("haze2");
+ if (haze != null) {
+ GameObject.Destroy(haze);
+ }
+
+ var shaker = burst.GetComponentInChildren();
+ if (shaker != null) {
+ Component.DestroyImmediate(shaker);
+ }
+ }
+
+ private void PlayBellBurst(GameObject bindEffects) {
+ Logger.Info("Playing Bell Burst");
+
+ var bellFsm = HeroController.instance.bellBindFSM;
+ if (!bellFsm.fsm.initialized) {
+ HeroController.instance.bellBindFSM.Init();
+ }
+
+ if (bellFsm == null) {
+ Logger.Warn("Unable to find bind bell fsm");
+ return;
+ }
+
+ var audio = bellFsm.GetFirstAction("Burst");
+ var spawner = bellFsm.GetFirstAction("Burst");
+
+ if (spawner == null) {
+ Logger.Warn("Unable to find bind bell spawner");
+ return;
+ }
+
+ var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner.gameObject.Value, bindEffects.transform);
+ var shaker = bindBell.GetComponentInChildren();
+ if (shaker != null) {
+ Component.DestroyImmediate(shaker);
+ }
+
+ var haze = bindBell.FindGameObjectInChildren("haze2 (1)");
+ if (haze != null) {
+ GameObject.Destroy(haze);
+ }
+
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ var damager = bindBell.FindGameObjectInChildren("damager");
+ if (damager != null) {
+ AddDamageHeroComponent(damager);
+ } else {
+ Logger.Warn("Unable to add damager to bind bell burst");
+ }
+ }
+ }
+}
From ce83546e421a1f9d5f699f159ea2f23ed07d6702 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 11 Mar 2026 00:23:28 -0400
Subject: [PATCH 19/33] add more sounds
---
SSMP/Animation/AnimationEffect.cs | 18 ++++++
SSMP/Animation/DamageAnimationEffect.cs | 23 ++++++-
SSMP/Animation/Effects/Bind.cs | 20 +++---
SSMP/Animation/Effects/BindBurst.cs | 85 ++++++++++++-------------
SSMP/Animation/Effects/BindFail.cs | 16 +++--
SSMP/Animation/Effects/EffectUtils.cs | 31 ++++++++-
SSMP/Testing/PlayerAnimation.cs | 4 ++
7 files changed, 135 insertions(+), 62 deletions(-)
diff --git a/SSMP/Animation/AnimationEffect.cs b/SSMP/Animation/AnimationEffect.cs
index ed09520..b05d89e 100644
--- a/SSMP/Animation/AnimationEffect.cs
+++ b/SSMP/Animation/AnimationEffect.cs
@@ -1,5 +1,7 @@
+using HutongGames.PlayMaker.Actions;
using SSMP.Game.Settings;
using SSMP.Internals;
+using SSMP.Util;
using UnityEngine;
namespace SSMP.Animation;
@@ -40,4 +42,20 @@ protected static void ChangeAttackDirection(GameObject targetObject, float direc
var directionVar = damageFsm.FsmVariables.GetFsmFloat("direction");
directionVar.Value = direction;
}
+
+ protected static void PlaySound(GameObject source, AudioPlayerOneShotSingle audio) {
+ AudioUtil.PlayAudioOneShotSingleAtPlayerObject(audio, source);
+ }
+
+ protected static void PlaySound(GameObject source, PlayAudioEvent audio) {
+ AudioUtil.PlayAudioEventAtPlayerObject(audio, source);
+ }
+
+ protected static void PlaySound(GameObject source, GetRandomAudioClipFromTable getAction, PlayAudioEvent playAction) {
+ AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
+ playAction,
+ getAction,
+ source
+ );
+ }
}
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index f22cd04..d93f4ca 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -1,6 +1,7 @@
using SSMP.Internals;
using UnityEngine;
using UnityEngine.Events;
+using Logger = SSMP.Logging.Logger;
namespace SSMP.Animation;
@@ -33,9 +34,29 @@ 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) {
+ protected static DamageHero AddDamageHeroComponent(GameObject target, int damage = 1) {
var damageHero = target.AddComponentIfNotPresent();
damageHero.damageDealt = damage;
damageHero.OnDamagedHero = new UnityEvent();
+
+ return damageHero;
+ }
+
+ protected static void RemoveDamageHeroComponent(GameObject target) {
+ var damageHero = target.GetComponent();
+ if (damageHero == null) {
+ return;
+ }
+
+ Component.DestroyImmediate(damageHero);
+ }
+
+ protected DamageHero? SetDamageHeroState(GameObject target, int damage = 1) {
+ if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
+ return AddDamageHeroComponent(target, damage);
+ } else {
+ RemoveDamageHeroComponent(target);
+ return null;
+ }
}
}
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 2268695..f509884 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -70,15 +70,11 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
if (BindState == State.Normal) {
- AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
- playAudioAction,
- randomClipAction,
- playerObject
- );
+ PlaySound(playerObject, randomClipAction, playAudioAction);
}
var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
- AudioUtil.PlayAudioOneShotSingleAtPlayerObject(oneShotSingleAction, playerObject);
+ PlaySound(playerObject, oneShotSingleAction);
var created = CreateObjects(playerObject, out var bindEffects);
if (!created) {
@@ -214,6 +210,8 @@ private void PlayCursedFail(GameObject bindEffects) {
failAntic = GameObject.Instantiate(localFailAntic, bindEffects.transform);
failAntic.name = "cursed_bind_fail";
+ failAntic.transform.SetLocalPositionZ(failAntic.transform.localPosition.z - 0.25f);
+
animator = failAntic.GetComponent();
animator.AnimationCompletedEvent += PlayNextCursedPart;
} else {
@@ -297,8 +295,12 @@ private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
return;
}
- var globalSilkPuff = silkPuffSpawner.gameObject.Value;
- EffectUtils.SpawnGlobalPoolObject(globalSilkPuff, playerObject.transform, false);
+ var audio = GetOrFindBindFsm().GetFirstAction("Shaman Air Cancel");
+ if (audio != null) {
+ PlaySound(playerObject, audio);
+ }
+
+ EffectUtils.SpawnGlobalPoolObject(silkPuffSpawner, playerObject.transform, 5f);
BindBurst.Instance.StopBindBell(bindEffects);
}
@@ -316,6 +318,8 @@ private void PlayShamanFallEnd(GameObject bindEffects) {
animator.Play("End");
}
+
+
///
public override byte[]? GetEffectInfo() {
byte[] effectInfo = {
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index a9adf56..dd45347 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -21,22 +21,20 @@ internal class BindBurst : Bind {
public static BindBurst Instance => _instance ??= new BindBurst();
public static HashSet MaggotedPlayers = new HashSet();
public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ // Set maggot info
Flags flags = new Flags(effectInfo);
if (MaggotedPlayers.Contains(playerId)) {
flags.Maggoted = true;
MaggotedPlayers.Remove(playerId);
}
- MonoBehaviourUtil.Instance.StartCoroutine(PlayBindBurstEffect(playerObject, crestType, flags));
- }
- private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crestType, Flags flags) {
if (!CreateObjects(playerObject, out var bindEffects)) {
- yield break;
+ return;
}
if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(playerObject, flags.UpgradedMirror);
if (flags.Maggoted) PlayMaggotCleanse(bindEffects, playerObject);
-
+
// Stop regardless of if its on or not
StopBindBell(bindEffects);
@@ -52,7 +50,7 @@ private IEnumerator PlayBindBurstEffect(GameObject playerObject, CrestType crest
} else {
PlayWitchMaggoted(bindEffects);
}
- break;
+ return;
case CrestType.Shaman:
PlayShamanEnd(bindEffects);
break;
@@ -72,17 +70,16 @@ protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject
var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
- if (maggotBurst != null) {
- maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- }
- if (maggotFlash != null) {
- maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- }
+ EffectUtils.SpawnGlobalPoolObject(maggotBurst, bindEffects.transform, 5f);
+ //if (maggotBurst != null) {
+ //maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ //}
+ EffectUtils.SpawnGlobalPoolObject(maggotFlash, bindEffects.transform, 5f);
+ //if (maggotFlash != null) {
+ //maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
+ //}
if (maggotAudio != null) {
- AudioUtil.PlayAudioEventAtPlayerObject(
- maggotAudio,
- playerObject
- );
+ PlaySound(playerObject, maggotAudio);
}
}
@@ -90,39 +87,24 @@ protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject
/// Creates the appropriate Claw Mirror object.
/// The object will be destroyed after it finishes.
///
- private GameObject? PrepareMirror(GameObject playerObject, SetGameObject mirrorSource, string name) {
-
- if (mirrorSource == null) {
- Logger.Warn("Unable to find mirror source");
- return null;
- }
+ private GameObject? PrepareMirror(GameObject playerObject, SetGameObject mirrorSource) {
- // This is ugly and i hate it, but it works
- var mirror = GameObject.Instantiate(mirrorSource.gameObject.Value, playerObject.transform);
- mirror.transform.SetParent(null);
- mirror.transform.position = playerObject.transform.position;
- mirror.SetActive(true);
+ var mirror = EffectUtils.SpawnGlobalPoolObject(mirrorSource.gameObject.Value, playerObject.transform, 3f);
if (mirror == null) {
- Logger.Warn("Unable to spawn mirror");
return null;
}
- mirror.name = name;
var shaker = mirror.GetComponentInChildren();
if (shaker != null) {
Component.DestroyImmediate(shaker);
}
- EffectUtils.SafelyRemoveAutoRecycle(mirror);
- mirror.DestroyAfterTime(2f);
-
var haze = mirror.FindGameObjectInChildren("haze2");
if (haze != null) {
GameObject.Destroy(haze);
}
- //mirror.SetActive(false);
return mirror;
}
@@ -136,24 +118,27 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
var upgradedClaw = GetOrFindBindFsm().GetAction("Dazzle?", 4)!;
GameObject? claw;
- if (upgraded) claw = PrepareMirror(playerObject, upgradedClaw, "dazzle_upgraded");
- else claw = PrepareMirror(playerObject, regularClaw, "dazzle_regular");
+ if (upgraded) claw = PrepareMirror(playerObject, upgradedClaw);
+ else claw = PrepareMirror(playerObject, regularClaw);
if (claw == null) {
- Logger.Warn("Unable to create claw mirror object.");
return;
}
claw.SetActive(true);
+
if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
var damagerParent = claw.FindGameObjectInChildren("Trobbio_dazzle_flash");
var damager = damagerParent?.FindGameObjectInChildren("hero_dazzle_flash_damager");
- if (damager != null) {
- damager.layer = (int) GlobalEnums.PhysLayers.HERO_ATTACK;
- AddDamageHeroComponent(damager);
- } else {
+ 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;
}
}
@@ -206,9 +191,12 @@ private void PlayWitchEnd(GameObject bindEffects) {
Component.DestroyImmediate(shaker);
}
- if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
- SetWitchDamagers(witchBind);
- }
+ SetWitchDamagers(witchBind);
+ }
+
+ var audio = GetOrFindBindFsm().GetFirstAction("Witch Tentancles!");
+ if (audio != null) {
+ PlaySound(bindEffects.transform.parent.gameObject, audio);
}
witchBind.SetActive(false);
@@ -219,13 +207,13 @@ private void PlayWitchEnd(GameObject bindEffects) {
/// Adds hero damage components to Witch Crest bind if PVP is on
///
private void SetWitchDamagers(GameObject witchBind) {
- for (int i = 0; i < witchBind.transform.childCount; i++) {
+ for (var i = 0; i < witchBind.transform.childCount; i++) {
var child = witchBind.transform.GetChild(i);
if (!child.name.StartsWith("Damager")) {
continue;
}
- AddDamageHeroComponent(child.gameObject);
+ SetDamageHeroState(child.gameObject);
}
}
@@ -260,5 +248,12 @@ private void PlayNormalEnd(GameObject bindEffects) {
var bindSilkMeshRenderer = bindSilkObj.GetComponent();
bindSilkMeshRenderer.enabled = false;
}
+
+ var audio = GetOrFindBindFsm().GetFirstAction("Bind Burst");
+ if (audio != null) {
+ PlaySound(bindEffects.transform.parent.gameObject, audio);
+ }
+
+
}
}
diff --git a/SSMP/Animation/Effects/BindFail.cs b/SSMP/Animation/Effects/BindFail.cs
index 19ac915..27b9af9 100644
--- a/SSMP/Animation/Effects/BindFail.cs
+++ b/SSMP/Animation/Effects/BindFail.cs
@@ -38,19 +38,21 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
private void PlayBindBurst(GameObject bindEffects) {
var bellBurstSpawner = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
if (bellBurstSpawner == null) {
- Logger.Warn("Unable to find Bell Burst spawner");
+ Logger.Warn("Unable to find bind burst effect spawner");
return;
}
- var globalBellBurst = bellBurstSpawner.gameObject.Value;
- var burst = EffectUtils.SpawnGlobalPoolObject(globalBellBurst, bindEffects.transform);
+ var audio = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
+
+ var burst = EffectUtils.SpawnGlobalPoolObject(bellBurstSpawner, bindEffects.transform, 5f);
if (burst == null) {
- Logger.Warn("Unable to create Bell Burst");
+ Logger.Warn("Unable to create bind burst effect");
return;
}
- burst.DestroyAfterTime(5f);
+ PlaySound(bindEffects.transform.parent.gameObject, audio);
+
if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
AddDamageHeroComponent(burst);
}
@@ -80,6 +82,8 @@ private void PlayBellBurst(GameObject bindEffects) {
}
var audio = bellFsm.GetFirstAction("Burst");
+ PlaySound(bindEffects.transform.parent.gameObject, audio);
+
var spawner = bellFsm.GetFirstAction("Burst");
if (spawner == null) {
@@ -87,7 +91,7 @@ private void PlayBellBurst(GameObject bindEffects) {
return;
}
- var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner.gameObject.Value, bindEffects.transform);
+ var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner, bindEffects.transform, 5f);
var shaker = bindBell.GetComponentInChildren();
if (shaker != null) {
Component.DestroyImmediate(shaker);
diff --git a/SSMP/Animation/Effects/EffectUtils.cs b/SSMP/Animation/Effects/EffectUtils.cs
index 6a029ff..1c17ed6 100644
--- a/SSMP/Animation/Effects/EffectUtils.cs
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -1,3 +1,5 @@
+using HutongGames.PlayMaker.Actions;
+using SSMP.Util;
using UnityEngine;
using Logger = SSMP.Logging.Logger;
using Object = UnityEngine.Object;
@@ -16,16 +18,41 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
}
}
- public static GameObject SpawnGlobalPoolObject(GameObject globalObj, Transform spawnLocation, bool keepParent = false) {
+ 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);
+ }
+
+ 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;
+ }
+
var newObj = GameObject.Instantiate(globalObj, spawnLocation);
-
+ if (newObj == null) {
+ Logger.Warn($"Unable to spawn global pool object {globalObj.name}");
+ return null;
+ }
+
+ // This is ugly and i hate it, but it works
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/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
index 446b15f..ca798ad 100644
--- a/SSMP/Testing/PlayerAnimation.cs
+++ b/SSMP/Testing/PlayerAnimation.cs
@@ -44,6 +44,10 @@ void SetSettings(AnimationEffect bind) {
bind.SetServerSettings(settings);
}
+ void SetCrest(string crest) {
+ ToolItemManager.SetEquippedCrest(crest);
+ }
+
void Init() {
gameObject.SetActive(true);
//ToolItemManager.SetEquippedCrest(Gameplay.CursedCrest.name);
From 10f6a13d004a109e4b0734b8764b465157944e07 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 11 Mar 2026 00:42:05 -0400
Subject: [PATCH 20/33] fix shaman crest double silk sounds
---
SSMP/Animation/Effects/Bind.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index f509884..d3172f5 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -71,10 +71,11 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
if (BindState == State.Normal) {
PlaySound(playerObject, randomClipAction, playAudioAction);
+
+ var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
+ PlaySound(playerObject, oneShotSingleAction);
}
- var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
- PlaySound(playerObject, oneShotSingleAction);
var created = CreateObjects(playerObject, out var bindEffects);
if (!created) {
From e2882077dfcf4055cf382d88b8b98933b76f9df5 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sat, 14 Mar 2026 19:28:58 -0400
Subject: [PATCH 21/33] rename bind fail, prevent fsm injection errors from
interupting flow
---
SSMP/Animation/AnimationClip.cs | 2 +-
SSMP/Animation/AnimationManager.cs | 24 +++++-----
.../Effects/{BindFail.cs => BindInterupt.cs} | 10 ++--
SSMP/Fsm/FsmStateActionInjector.cs | 46 +++++++++++++------
4 files changed, 54 insertions(+), 28 deletions(-)
rename SSMP/Animation/Effects/{BindFail.cs => BindInterupt.cs} (94%)
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index 4b0764b..9f2d568 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -764,5 +764,5 @@ internal enum AnimationClip {
// Sub-animation names
WitchTentacles,
ShamanCancel,
- BindFailBurst,
+ BindInterupt,
}
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index efc69cd..fb1c011 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -619,7 +619,7 @@ internal class AnimationManager {
{ "Witch Tentacles!", AnimationClip.WitchTentacles },
{ "Shaman Cancel", AnimationClip.ShamanCancel },
- { "Bind Fail Burst", AnimationClip.BindFailBurst }
+ { "Bind Fail Burst", AnimationClip.BindInterupt }
};
///
@@ -662,7 +662,7 @@ internal class AnimationManager {
private static readonly Dictionary SubAnimationEffects = new() {
{ AnimationClip.WitchTentacles, BindBurst.Instance },
{ AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } },
- { AnimationClip.BindFailBurst, BindFail.Instance }
+ { AnimationClip.BindInterupt, BindInterupt.Instance }
};
///
@@ -766,10 +766,9 @@ public void RegisterHooks() {
EventHooks.SpriteAnimatorProcessEvents += Tk2dSpriteAnimatorOnProcessEvents;
// Register FSM hooks for certain bind actions
+ HeroController.OnHeroInstanceSet += CreateBindHooks;
if (HeroController.SilentInstance != null) {
CreateBindHooks(HeroController.instance);
- } else {
- HeroController.OnHeroInstanceSet += CreateBindHooks;
}
@@ -797,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;
@@ -1065,7 +1066,7 @@ private void CreateBindHooks(HeroController hc) {
// Find witch crest tenticles
var tenticles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
if (tenticles != null) {
- FsmStateActionInjector.Inject(tenticles, 4, OnWitchTentacles);
+ FsmStateActionInjector.Inject(tenticles, OnWitchTentacles, 4);
} else {
Logger.Warn("Unable to find Witch Tentacles! state");
}
@@ -1077,9 +1078,9 @@ private void CreateBindHooks(HeroController hc) {
Logger.Warn("Unable to find Shaman Air Cancel state");
}
- var bindFail = bindFsm.GetState("Remove Silk?");
- if (bindFsm != null) {
- FsmStateActionInjector.Inject(bindFail, 2, OnBindFail);
+ var bindInterupt = bindFsm.GetState("Remove Silk?");
+ if (bindInterupt != null) {
+ FsmStateActionInjector.Inject(bindInterupt, OnBindInterupt, 2);
}
}
@@ -1102,9 +1103,10 @@ private void OnShamanCancel(PlayMakerFSM fsm) {
dummyClip.wrapMode = tk2dSpriteAnimationClip.WrapMode.Once;
OnAnimationEvent(dummyClip);
}
-
- private void OnBindFail(PlayMakerFSM fsm) {
- Logger.Warn("PLAYING BIND FAIL");
+ ///
+ /// Animation subanimation hook for interupted binds
+ ///
+ private void OnBindInterupt(PlayMakerFSM fsm) {
var dummyClip = new tk2dSpriteAnimationClip();
dummyClip.name = "Bind Fail Burst";
dummyClip.wrapMode = tk2dSpriteAnimationClip.WrapMode.Once;
diff --git a/SSMP/Animation/Effects/BindFail.cs b/SSMP/Animation/Effects/BindInterupt.cs
similarity index 94%
rename from SSMP/Animation/Effects/BindFail.cs
rename to SSMP/Animation/Effects/BindInterupt.cs
index 27b9af9..5887196 100644
--- a/SSMP/Animation/Effects/BindFail.cs
+++ b/SSMP/Animation/Effects/BindInterupt.cs
@@ -12,13 +12,13 @@
namespace SSMP.Animation.Effects;
-internal class BindFail : Bind {
+internal class BindInterupt : Bind {
///
/// Static instance for access by multiple animation clips in .
///
- private static BindFail? _instance;
+ private static BindInterupt? _instance;
///
- public static BindFail Instance => _instance ??= new BindFail();
+ public static BindInterupt Instance => _instance ??= new BindInterupt();
public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
@@ -92,6 +92,10 @@ private void PlayBellBurst(GameObject bindEffects) {
}
var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner, bindEffects.transform, 5f);
+ if (bindBell == null) {
+ return;
+ }
+
var shaker = bindBell.GetComponentInChildren();
if (shaker != null) {
Component.DestroyImmediate(shaker);
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
index 15db8ec..3c50145 100644
--- a/SSMP/Fsm/FsmStateActionInjector.cs
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -3,28 +3,48 @@
using System.Linq;
using System.Text;
using HutongGames.PlayMaker;
+using Steamworks;
namespace SSMP.Fsm;
internal class FsmStateActionInjector : FsmStateAction {
-
+ private static Action? _onUninject;
private Action? _onStateEnter;
+ private FsmStateActionInjector(FsmState state, Action onEnter) {
+ Fsm = state.Fsm;
+ State = state;
+ _onStateEnter = onEnter;
+ _onUninject += Uninject;
+ }
+
+ private void DoInjection(int index) {
+ var stateActions = State.Actions.ToList();
+ stateActions.Insert(index, this);
+ State.Actions = stateActions.ToArray();
+ State.SaveActions();
+ }
+
+ public void Uninject() {
+ var actions = State.Actions.ToList();
+ actions.Remove(this);
+ State.Actions = actions.ToArray();
+ State.SaveActions();
+ }
+
public override void OnEnter() {
- _onStateEnter?.Invoke(Fsm.FsmComponent);
Finish();
+ _onStateEnter?.Invoke(Fsm.FsmComponent);
}
- public static void Inject(FsmState state, Action onEnter) {
- Inject(state, 0, onEnter);
+
+ public static FsmStateActionInjector Inject(FsmState state, Action onEnter, int actionIndex = 0) {
+ var action = new FsmStateActionInjector(state, onEnter);
+ action.DoInjection(actionIndex);
+
+ return action;
}
- public static void Inject(FsmState state, int actionIndex, Action onEnter) {
- var action = new FsmStateActionInjector();
- action.Fsm = state.Fsm;
- action._onStateEnter = onEnter;
-
- var stateActions = state.Actions.ToList();
- stateActions.Insert(actionIndex, action);
- state.Actions = stateActions.ToArray();
- state.SaveActions();
+
+ public static void UninjectAll() {
+ _onUninject?.Invoke();
}
}
From b21f4c16d202cb70d2d0cb33549add8eda5c038d Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:12:03 -0400
Subject: [PATCH 22/33] Catch errors to fully prevent fsm breaks
---
SSMP/Fsm/FsmStateActionInjector.cs | 31 +++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
index 3c50145..ff9742f 100644
--- a/SSMP/Fsm/FsmStateActionInjector.cs
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -3,7 +3,7 @@
using System.Linq;
using System.Text;
using HutongGames.PlayMaker;
-using Steamworks;
+using SSMP.Logging;
namespace SSMP.Fsm;
@@ -11,12 +11,14 @@ internal class FsmStateActionInjector : FsmStateAction {
private static Action? _onUninject;
private Action? _onStateEnter;
private FsmStateActionInjector(FsmState state, Action onEnter) {
- Fsm = state.Fsm;
- State = state;
+ 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);
@@ -24,6 +26,9 @@ private void DoInjection(int index) {
State.SaveActions();
}
+ ///
+ /// Removes the delegate action from the FSM state
+ ///
public void Uninject() {
var actions = State.Actions.ToList();
actions.Remove(this);
@@ -31,12 +36,25 @@ public void Uninject() {
State.SaveActions();
}
+ ///
public override void OnEnter() {
+ if (_onStateEnter != null) {
+ try {
+ _onStateEnter?.Invoke(Fsm.FsmComponent);
+ } catch (Exception e) {
+ Logger.Error(e.ToString());
+ }
+ }
Finish();
- _onStateEnter?.Invoke(Fsm.FsmComponent);
}
-
+ ///
+ /// 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) {
var action = new FsmStateActionInjector(state, onEnter);
action.DoInjection(actionIndex);
@@ -44,6 +62,9 @@ public static FsmStateActionInjector Inject(FsmState state, Action
return action;
}
+ ///
+ /// Removes all injected FSM actions
+ ///
public static void UninjectAll() {
_onUninject?.Invoke();
}
From c56568e1b9218e290bb198bcbd5da70951e7274a Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:12:18 -0400
Subject: [PATCH 23/33] Add documentation
---
SSMP/Animation/AnimationManager.cs | 2 +-
SSMP/Animation/DamageAnimationEffect.cs | 11 ++++-
SSMP/Animation/Effects/Bind.cs | 23 +++++++--
SSMP/Animation/Effects/BindBurst.cs | 33 ++++++++-----
SSMP/Animation/Effects/BindInterupt.cs | 65 +++++++++++++++----------
SSMP/Animation/Effects/EffectUtils.cs | 18 +++++++
6 files changed, 106 insertions(+), 46 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index fb1c011..922f9aa 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -25,7 +25,7 @@ internal class AnimationManager {
/// Whether to log debug messages about animations. For debugging purposes this can be enabled so that all
/// animation events are logged.
///
- private static bool _debugLogAnimations = false;
+ private static bool _debugLogAnimations = true;
///
/// The distance threshold for playing certain effects.
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index d93f4ca..876806c 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -1,7 +1,6 @@
using SSMP.Internals;
using UnityEngine;
using UnityEngine.Events;
-using Logger = SSMP.Logging.Logger;
namespace SSMP.Animation;
@@ -42,6 +41,10 @@ protected static DamageHero AddDamageHeroComponent(GameObject target, int damage
return damageHero;
}
+ ///
+ /// Removes a component from the given game object.
+ ///
+ /// The target game object to detatch the component to.
protected static void RemoveDamageHeroComponent(GameObject target) {
var damageHero = target.GetComponent();
if (damageHero == null) {
@@ -51,6 +54,12 @@ protected static void RemoveDamageHeroComponent(GameObject target) {
Component.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.
protected DamageHero? SetDamageHeroState(GameObject target, int damage = 1) {
if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
return AddDamageHeroComponent(target, damage);
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index d3172f5..b898955 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -25,6 +25,9 @@ public enum State {
public State BindState = State.Normal;
+ ///
+ /// Effect flags sent by the other player. Mostly items they have equipped.
+ ///
protected class Flags {
public bool BindBell = false;
public bool BaseMirror = false;
@@ -49,8 +52,12 @@ public Flags(byte[]? info) {
///
protected static PlayMakerFSM? _bindFsm;
+ ///
+ /// Cached effects object for Hornet's bind ability.
+ ///
protected static GameObject? _localBindEffects;
+ ///
public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
@@ -123,7 +130,7 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
/// Creates the bind bell
///
private void StartBindBell(GameObject bindEffects) {
- Logger.Debug("Starting bind bell");
+ Logger.Debug("Starting warding bell");
var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
if (bindBell == null) {
@@ -131,7 +138,7 @@ private void StartBindBell(GameObject bindEffects) {
var localBell = allObjects.FirstOrDefault(o => o.name == "bind_bell_appear");
if (localBell == null) {
- Logger.Warn("Couldn't find bind bell object");
+ Logger.Warn("Couldn't find warding bell object");
return;
}
@@ -250,7 +257,9 @@ private void PlayWitchAnimationAntic(GameObject bindEffects) {
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);
@@ -283,6 +292,9 @@ private void PlayShamanFall(GameObject bindEffects) {
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("Shaman_Bind_antic_silk");
@@ -401,7 +413,10 @@ protected bool CreateObjects(GameObject playerObject, out GameObject bindEffects
return obj;
}
-
+
+ ///
+ /// Turns off all bind effects
+ ///
public static void ForceStopAllEffects(GameObject bindEffects) {
BindBurst.Instance.StopBindBell(bindEffects);
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index dd45347..5fe134b 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -12,14 +12,24 @@
namespace SSMP.Animation.Effects;
+///
+/// Class for the animation effect of a bind (healing) finishing.
+///
internal class BindBurst : Bind {
///
/// Static instance for access by multiple animation clips in .
///
private static BindBurst? _instance;
+
///
public static BindBurst Instance => _instance ??= new BindBurst();
+
+ ///
+ /// A set of players who recently binded while maggoted
+ ///
public static HashSet MaggotedPlayers = new HashSet();
+
+ ///
public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
// Set maggot info
Flags flags = new Flags(effectInfo);
@@ -38,6 +48,7 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
// Stop regardless of if its on or not
StopBindBell(bindEffects);
+ // Play crest-specific animations
switch (crestType) {
case CrestType.Beast:
if (!flags.Maggoted) {
@@ -71,13 +82,8 @@ protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject
var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
EffectUtils.SpawnGlobalPoolObject(maggotBurst, bindEffects.transform, 5f);
- //if (maggotBurst != null) {
- //maggotBurst.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- //}
EffectUtils.SpawnGlobalPoolObject(maggotFlash, bindEffects.transform, 5f);
- //if (maggotFlash != null) {
- //maggotFlash.gameObject.Value.Spawn(bindEffects.transform, Vector3.zero);
- //}
+
if (maggotAudio != null) {
PlaySound(playerObject, maggotAudio);
}
@@ -113,10 +119,12 @@ protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject
/// Adds a damage component if appropriate.
///
private void PlayMirror(GameObject playerObject, bool upgraded) {
- Logger.Info("Playing Claw Mirror Animation");
+ // 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);
@@ -127,6 +135,7 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
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");
@@ -147,9 +156,7 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
///
public void StopBindBell(GameObject bindEffects) {
var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
- if (bindBell != null) {
- bindBell.SetActive(false);
- }
+ bindBell?.SetActive(false);
}
///
@@ -172,13 +179,13 @@ private void PlayWitchMaggoted(GameObject bindEffects) {
}
///
- /// Plays the Witch Crest tentancles animation
+ /// Plays the Witch Crest tentancles animation.
/// Yes it's called Tentancles internally. Thanks TC.
///
private void PlayWitchEnd(GameObject bindEffects) {
var witchBind = bindEffects.FindGameObjectInChildren("Witch Bind");
if (witchBind == null) {
- var localWitchBind = _localBindEffects.FindGameObjectInChildren("Witch Bind");
+ var localWitchBind = _localBindEffects?.FindGameObjectInChildren("Witch Bind");
if (localWitchBind == null) {
Logger.Warn("Unable to find local Witch Bind object");
return;
@@ -204,7 +211,7 @@ private void PlayWitchEnd(GameObject bindEffects) {
}
///
- /// Adds hero damage components to Witch Crest bind if PVP is on
+ /// Adds or removes hero damage components from Witch Crest bind
///
private void SetWitchDamagers(GameObject witchBind) {
for (var i = 0; i < witchBind.transform.childCount; i++) {
diff --git a/SSMP/Animation/Effects/BindInterupt.cs b/SSMP/Animation/Effects/BindInterupt.cs
index 5887196..843cd0d 100644
--- a/SSMP/Animation/Effects/BindInterupt.cs
+++ b/SSMP/Animation/Effects/BindInterupt.cs
@@ -1,24 +1,24 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using HutongGames.PlayMaker.Actions;
using SSMP.Internals;
using SSMP.Util;
using UnityEngine;
-using static UnityEngine.ParticleSystem;
using Logger = SSMP.Logging.Logger;
namespace SSMP.Animation.Effects;
+///
+/// Class for the animation effect of a bind (healing) being interupted by an attack.
+///
internal class BindInterupt : Bind {
///
/// Static instance for access by multiple animation clips in .
///
private static BindInterupt? _instance;
+
///
public static BindInterupt Instance => _instance ??= new BindInterupt();
+
+ ///
public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
@@ -29,34 +29,35 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
ForceStopAllEffects(bindEffects);
if (flags.BindBell) {
- PlayBellBurst(bindEffects);
+ PlayBellExplode(bindEffects);
} else {
- PlayBindBurst(bindEffects);
+ PlayBindInterupt(bindEffects);
}
}
- private void PlayBindBurst(GameObject bindEffects) {
- var bellBurstSpawner = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
- if (bellBurstSpawner == null) {
+ ///
+ /// Plays the normal bind interput animation
+ ///
+ private void PlayBindInterupt(GameObject bindEffects) {
+ // Find prefab
+ var bindBurstSpawner = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
+ if (bindBurstSpawner == null) {
Logger.Warn("Unable to find bind burst effect spawner");
return;
}
- var audio = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
-
- var burst = EffectUtils.SpawnGlobalPoolObject(bellBurstSpawner, bindEffects.transform, 5f);
-
+ // Spawn in effect
+ var burst = EffectUtils.SpawnGlobalPoolObject(bindBurstSpawner, bindEffects.transform, 5f);
if (burst == null) {
Logger.Warn("Unable to create bind burst effect");
return;
}
+ // Play audio
+ var audio = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
PlaySound(bindEffects.transform.parent.gameObject, audio);
- if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
- AddDamageHeroComponent(burst);
- }
-
+ // Remove haze and camera controls
var haze = burst.FindGameObjectInChildren("haze2");
if (haze != null) {
GameObject.Destroy(haze);
@@ -68,34 +69,43 @@ private void PlayBindBurst(GameObject bindEffects) {
}
}
- private void PlayBellBurst(GameObject bindEffects) {
- Logger.Info("Playing Bell Burst");
+ ///
+ /// Creates a Warding Bell explosion
+ ///
+ private void PlayBellExplode(GameObject bindEffects) {
+ Logger.Debug("Playing Bell Burst");
+ // 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) {
HeroController.instance.bellBindFSM.Init();
}
if (bellFsm == null) {
- Logger.Warn("Unable to find bind bell fsm");
+ Logger.Warn("Unable to find warding bell fsm");
return;
}
- var audio = bellFsm.GetFirstAction("Burst");
- PlaySound(bindEffects.transform.parent.gameObject, audio);
-
var spawner = bellFsm.GetFirstAction("Burst");
if (spawner == null) {
- Logger.Warn("Unable to find bind bell spawner");
+ Logger.Warn("Unable to find warding bell spawner");
return;
}
+ // Spawn warding bell
var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner, bindEffects.transform, 5f);
if (bindBell == null) {
return;
}
+ // Play sound
+ var audio = bellFsm.GetFirstAction("Burst");
+ PlaySound(bindBell, audio);
+
+
+ // Remove camera control and haze
var shaker = bindBell.GetComponentInChildren();
if (shaker != null) {
Component.DestroyImmediate(shaker);
@@ -106,12 +116,13 @@ private void PlayBellBurst(GameObject bindEffects) {
GameObject.Destroy(haze);
}
+ // 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 bind bell burst");
+ Logger.Warn("Unable to add damager to warding bell burst");
}
}
}
diff --git a/SSMP/Animation/Effects/EffectUtils.cs b/SSMP/Animation/Effects/EffectUtils.cs
index 1c17ed6..f38dcff 100644
--- a/SSMP/Animation/Effects/EffectUtils.cs
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -7,6 +7,11 @@
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) {
@@ -18,6 +23,14 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
}
}
+ ///
+ /// 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");
@@ -27,6 +40,11 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
return SpawnGlobalPoolObject(spawner.gameObject.Value, spawnLocation, destroyAfterDelay, keepParent);
}
+ ///
+ /// The GameObject to spawn
+ ///
+ ///
+ ///
public static GameObject? SpawnGlobalPoolObject(GameObject? globalObj, Transform spawnLocation, float destroyAfterDelay, bool keepParent = false) {
if (globalObj == null) {
Logger.Warn("Unable to find global pool object");
From 32a885f2fd64038fcac4b2d8f2b374625b976051 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:14:00 -0400
Subject: [PATCH 24/33] remove testing components
---
SSMP/Game/Client/PlayerManager.cs | 2 -
SSMP/Testing/PlayerAnimation.cs | 145 ------------------------------
2 files changed, 147 deletions(-)
delete mode 100644 SSMP/Testing/PlayerAnimation.cs
diff --git a/SSMP/Game/Client/PlayerManager.cs b/SSMP/Game/Client/PlayerManager.cs
index b3dca56..a8cdc31 100644
--- a/SSMP/Game/Client/PlayerManager.cs
+++ b/SSMP/Game/Client/PlayerManager.cs
@@ -149,8 +149,6 @@ private void TryCreatePlayerPool() {
// Create a player container prefab, used to spawn players
_playerContainerPrefab = new GameObject(PlayerContainerPrefabName);
- _playerContainerPrefab.AddComponent();
-
_playerContainerPrefab.AddComponent();
var playerPrefab = new GameObject(
diff --git a/SSMP/Testing/PlayerAnimation.cs b/SSMP/Testing/PlayerAnimation.cs
deleted file mode 100644
index ca798ad..0000000
--- a/SSMP/Testing/PlayerAnimation.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using GlobalSettings;
-using HarmonyLib;
-using SSMP.Animation;
-using SSMP.Animation.Effects;
-using SSMP.Game.Settings;
-using SSMP.Internals;
-using UnityEngine;
-using static UnityEngine.Rendering.RayTracingAccelerationStructure;
-using Logger = SSMP.Logging.Logger;
-using Object = UnityEngine.Object;
-
-namespace SSMP.Testing;
-
-public class PlayerAnimation : MonoBehaviour {
-
- CrestType DetermineCrest() {
- var playerCrest = HeroController.instance.playerData.CurrentCrestID! ?? "";
- //if (playerCrest == null) return CrestType.Hunter;
-
- if (playerCrest == Gameplay.HunterCrest.name) return CrestType.Hunter;
- if (playerCrest == Gameplay.HunterCrest2.name) return CrestType.HunterV2;
- if (playerCrest == Gameplay.HunterCrest3.name) return CrestType.HunterV3;
- if (playerCrest == Gameplay.ReaperCrest.name) return CrestType.Reaper;
-
- if (playerCrest == Gameplay.WandererCrest.name) return CrestType.Wanderer;
- if (playerCrest == Gameplay.CloaklessCrest.name) return CrestType.Cloakless;
- if (playerCrest == Gameplay.WarriorCrest.name) return CrestType.Beast;
- if (playerCrest == Gameplay.ToolmasterCrest.name) return CrestType.Architect;
- if (playerCrest == Gameplay.CursedCrest.name) return CrestType.Cursed;
- if (playerCrest == Gameplay.SpellCrest.name) return CrestType.Shaman;
- if (playerCrest == Gameplay.WitchCrest.name) return CrestType.Witch;
-
- return CrestType.Hunter;
- }
-
- void SetSettings(AnimationEffect bind) {
- var plugin = GameObject.FindFirstObjectByType();
- var traverse = Traverse.Create(plugin).Field("_gameManager").Field("_clientManager").Field("_serverSettings");
- var settings = traverse.GetValue();
-
- bind.SetServerSettings(settings);
- }
-
- void SetCrest(string crest) {
- ToolItemManager.SetEquippedCrest(crest);
- }
-
- void Init() {
- gameObject.SetActive(true);
- //ToolItemManager.SetEquippedCrest(Gameplay.CursedCrest.name);
-
- var hornet = HeroController.instance.gameObject;
- var position = hornet.transform.position;
- var playerPosition = new Vector3(position.x + 10, position.y, position.z);
- transform.position = playerPosition;
- }
-
- public void StartAnimation1() {
- Init();
- CrestType crest = DetermineCrest();
-
- if (crest != CrestType.Shaman) {
- return;
- }
- string clipName = "BindCharge Ground";
-
- var playerObject = transform.GetChild(0).gameObject;
- var bind = new Bind { BindState = Bind.State.Normal };
-
- var info = bind.GetEffectInfo();
-
- SetSettings(bind);
- bind.Play(playerObject, crest, 1, info);
-
- var spriteAnimator = playerObject.GetComponent();
-
- var clip = spriteAnimator.GetClipByName(clipName);
- spriteAnimator.PlayFromFrame(clip, 0);
- }
-
- public void StartAnimation2() {
- var clipName = "BindBurst Ground";
-
- var playerObject = transform.GetChild(0).gameObject;
-
- var bind = new BindBurst();
- SetSettings(bind);
- bind.SetShouldDoDamage(true);
-
- var info = bind.GetEffectInfo();
- CrestType crest = DetermineCrest();
-
- bind.Play(playerObject, crest, 1, info);
-
- var spriteAnimator = playerObject.GetComponent();
-
- var clip = spriteAnimator.GetClipByName(clipName);
- spriteAnimator.PlayFromFrame(clip, 0);
- }
-
-
- public void StartAnimation3() {
-
- Init();
-
- string clipName = "BindCharge Ground";
- CrestType crest = DetermineCrest();
-
- var playerObject = transform.GetChild(0).gameObject;
- var bind = new Bind { BindState = Bind.State.ShamanDoneFalling };
-
- var info = bind.GetEffectInfo();
-
- SetSettings(bind);
- bind.Play(playerObject, crest, 1, info);
-
- var spriteAnimator = playerObject.GetComponent();
-
- var clip = spriteAnimator.GetClipByName(clipName);
- spriteAnimator.PlayFromFrame(clip, 0);
- }
-
- public void StartAnimation4() {
- var clipName = "BindBurst Ground";
-
- var playerObject = transform.GetChild(0).gameObject;
-
- var bind = new Bind { BindState = Bind.State.ShamanCancel };
- SetSettings(bind);
- bind.SetShouldDoDamage(true);
-
- var info = bind.GetEffectInfo();
- CrestType crest = DetermineCrest();
-
- bind.Play(playerObject, crest, 1, info);
-
- var spriteAnimator = playerObject.GetComponent();
-
- var clip = spriteAnimator.GetClipByName(clipName);
- spriteAnimator.PlayFromFrame(clip, 0);
- }
-}
From 9d9f434d1b8c2c9dc0794cf6276644e858799826 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 17:10:36 -0400
Subject: [PATCH 25/33] documentation, remove accidental debugs
---
SSMP/Animation/AnimationEffect.cs | 17 +++++++++++++++++
SSMP/Animation/AnimationManager.cs | 2 +-
SSMP/Animation/Effects/Bind.cs | 1 -
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/SSMP/Animation/AnimationEffect.cs b/SSMP/Animation/AnimationEffect.cs
index b05d89e..b1ffd94 100644
--- a/SSMP/Animation/AnimationEffect.cs
+++ b/SSMP/Animation/AnimationEffect.cs
@@ -43,14 +43,31 @@ protected static void ChangeAttackDirection(GameObject targetObject, float direc
directionVar.Value = direction;
}
+
+ ///
+ /// Plays a one-shot audio clip at the specified GameObject source.
+ ///
+ /// The object to play the sound at.
+ /// The audio clip to be played.
protected static void PlaySound(GameObject source, AudioPlayerOneShotSingle audio) {
AudioUtil.PlayAudioOneShotSingleAtPlayerObject(audio, source);
}
+ ///
+ /// Plays a specified audio event at the specified GameObject source.
+ ///
+ /// The object to play the sound at.
+ /// The FSM action with the audio clip to be played.
protected static void PlaySound(GameObject source, PlayAudioEvent audio) {
AudioUtil.PlayAudioEventAtPlayerObject(audio, source);
}
+ ///
+ /// Plays a random sound effect at the specified GameObject source
+ ///
+ /// The object to play the sound at.
+ /// The FSM action with the audio table.
+ /// The FSM action with the audio playing function.
protected static void PlaySound(GameObject source, GetRandomAudioClipFromTable getAction, PlayAudioEvent playAction) {
AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
playAction,
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 922f9aa..fb1c011 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -25,7 +25,7 @@ internal class AnimationManager {
/// Whether to log debug messages about animations. For debugging purposes this can be enabled so that all
/// animation events are logged.
///
- private static bool _debugLogAnimations = true;
+ private static bool _debugLogAnimations = false;
///
/// The distance threshold for playing certain effects.
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index b898955..13b6930 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -343,7 +343,6 @@ private void PlayShamanFallEnd(GameObject bindEffects) {
(byte) ((ToolItemManager.IsToolEquipped("Reserve Bind") && !ToolItemManager.GetToolByName("Reserve Bind").IsEmpty) ? 1 : 0),
(byte) (HeroController.instance.cState.isMaggoted ? 1 : 0)
};
- Logger.Info(HeroController.instance.cState.isMaggoted ? "MAGGOTS" : "NO MAGGOTS");
return effectInfo;
}
From 37b4038e588963a9d7bf5fdc20751a399ae8ed81 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 17:30:32 -0400
Subject: [PATCH 26/33] requested style changes
---
SSMP/Animation/DamageAnimationEffect.cs | 6 +-
SSMP/Animation/Effects/Bind.cs | 133 ++++++++++++------------
SSMP/Animation/Effects/BindBurst.cs | 9 +-
SSMP/Fsm/FsmStateActionInjector.cs | 5 +-
4 files changed, 72 insertions(+), 81 deletions(-)
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index 876806c..c8fde05 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -63,9 +63,9 @@ protected static void RemoveDamageHeroComponent(GameObject target) {
protected DamageHero? SetDamageHeroState(GameObject target, int damage = 1) {
if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
return AddDamageHeroComponent(target, damage);
- } else {
- RemoveDamageHeroComponent(target);
- return null;
}
+
+ RemoveDamageHeroComponent(target);
+ return null;
}
}
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 13b6930..5eac9ea 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;
@@ -15,51 +16,23 @@ namespace SSMP.Animation.Effects;
///
internal class Bind : DamageAnimationEffect {
- public enum State {
- Normal,
- ShamanCancel,
- ShamanDoneFalling
- }
-
- protected const string BIND_BELL_NAME = "bind_bell_appear_instance";
+ protected const string BindBellName = "bind_bell_appear_instance";
public State BindState = State.Normal;
- ///
- /// Effect flags sent by the other player. Mostly items they have equipped.
- ///
- protected class Flags {
- public bool BindBell = false;
- public bool BaseMirror = false;
- public bool UpgradedMirror = false;
- public bool QuickBind = false;
- public bool ReserveBind = false;
- public bool Maggoted = false;
-
- 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;
- }
- }
-
///
/// Cached FSM for Hornet's bind ability.
///
- protected static PlayMakerFSM? _bindFsm;
+ protected static PlayMakerFSM? BindFsm;
///
/// Cached effects object for Hornet's bind ability.
///
- protected static GameObject? _localBindEffects;
+ protected static GameObject? LocalBindEffects;
///
public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
- Flags flags = new Flags(effectInfo);
+ 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 consistancy
@@ -69,42 +42,35 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
BindBurst.MaggotedPlayers.Remove(playerId);
}
- MonoBehaviourUtil.Instance.StartCoroutine(PlayBindEffect(playerObject, crestType, flags));
- }
-
- private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType, Flags flags) {
var randomClipAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
if (BindState == State.Normal) {
PlaySound(playerObject, randomClipAction, playAudioAction);
-
+
var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
PlaySound(playerObject, oneShotSingleAction);
}
- var created = CreateObjects(playerObject, out var bindEffects);
- if (!created) {
- yield break;
+ if (!CreateObjects(playerObject, out var bindEffects)) {
+ return;
}
- Logger.Info("Determining crest animation...");
-
- switch(crestType) {
+ switch (crestType) {
case CrestType.Beast:
PlayBeastBindStart(bindEffects);
break;
case CrestType.Cursed:
PlayCursedFail(bindEffects);
- yield break;
+ return;
case CrestType.Witch:
PlayWitchAnimationAntic(bindEffects);
break;
case CrestType.Shaman:
var shouldContinue = PickShamanAnimation(playerObject, bindEffects, flags);
if (!shouldContinue) {
- yield break;
+ return;
}
break;
default:
@@ -120,10 +86,6 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
// TODO: If using reserve bind, use reserve bind animation?
// TODO: Quick Craft animations
-
- var playerAnimator = playerObject.GetComponent();
- var currentClip = playerAnimator?.currentClip;
- Logger.Info($"Player Animator current clip for Bind: {currentClip?.name}");
}
///
@@ -131,7 +93,7 @@ private IEnumerator PlayBindEffect(GameObject playerObject, CrestType crestType,
///
private void StartBindBell(GameObject bindEffects) {
Logger.Debug("Starting warding bell");
- var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
+ var bindBell = bindEffects.FindGameObjectInChildren(BindBellName);
if (bindBell == null) {
var allObjects = Resources.FindObjectsOfTypeAll();
@@ -142,8 +104,8 @@ private void StartBindBell(GameObject bindEffects) {
return;
}
- bindBell = GameObject.Instantiate(localBell, bindEffects.transform);
- bindBell.name = BIND_BELL_NAME;
+ bindBell = Object.Instantiate(localBell, bindEffects.transform);
+ bindBell.name = BindBellName;
var follower = bindBell.GetComponent();
follower.target = bindEffects.transform;
@@ -174,9 +136,11 @@ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
Logger.Info("Playing Bind Silk animation");
- if (flags.QuickBind) bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk Quick"));
- else bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk"));
-
+ if (flags.QuickBind) {
+ bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk Quick"));
+ } else {
+ bindSilkAnimator.Play(bindSilkAnimator.GetClipByName("Bind Silk"));
+ }
bindSilkObj.SetActive(false);
bindSilkObj.SetActive(true);
}
@@ -216,7 +180,7 @@ private void PlayCursedFail(GameObject bindEffects) {
return;
}
- failAntic = GameObject.Instantiate(localFailAntic, bindEffects.transform);
+ failAntic = Object.Instantiate(localFailAntic, bindEffects.transform);
failAntic.name = "cursed_bind_fail";
failAntic.transform.SetLocalPositionZ(failAntic.transform.localPosition.z - 0.25f);
@@ -267,12 +231,11 @@ private bool PickShamanAnimation(GameObject playerObject, GameObject bindEffects
} else if (BindState == State.ShamanCancel) {
PlayShamanCancel(playerObject, bindEffects);
return false;
- } else {
- PlayShamanFallEnd(bindEffects);
- PlayNormalStart(bindEffects, flags);
- return false;
}
+ PlayShamanFallEnd(bindEffects);
+ PlayNormalStart(bindEffects, flags);
+ return false;
}
///
@@ -347,21 +310,21 @@ private void PlayShamanFallEnd(GameObject bindEffects) {
}
///
- /// Get or find the Bind FSM on the hero object. Will be cached to .
+ /// 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.
///
protected PlayMakerFSM GetOrFindBindFsm() {
- if (_bindFsm != null) {
- return _bindFsm;
+ if (BindFsm != null) {
+ return BindFsm;
}
var heroFsms = HeroController.instance.GetComponents();
foreach (var heroFsm in heroFsms) {
if (heroFsm.FsmName == "Bind") {
- _bindFsm = heroFsm;
- return _bindFsm;
+ BindFsm = heroFsm;
+ return BindFsm;
}
}
@@ -374,9 +337,9 @@ protected PlayMakerFSM GetOrFindBindFsm() {
/// 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, out GameObject bindEffects) {
- _localBindEffects ??= HeroController.instance.gameObject.FindGameObjectInChildren("Bind Effects");
- if (_localBindEffects == null) {
+ 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;
@@ -400,7 +363,7 @@ protected bool CreateObjects(GameObject playerObject, out GameObject bindEffects
protected GameObject? CreateEffectIfNotExists(GameObject bindEffects, string objectName) {
var obj = bindEffects.FindGameObjectInChildren(objectName);
if (obj == null) {
- var localObj = _localBindEffects.FindGameObjectInChildren(objectName);
+ var localObj = LocalBindEffects!.FindGameObjectInChildren(objectName);
if (localObj == null) {
Logger.Warn($"Could not find local {objectName} object, cannot play bind");
return null;
@@ -444,4 +407,36 @@ public static void ForceStopAllEffects(GameObject bindEffects) {
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 bool BindBell = false;
+ public bool BaseMirror = false;
+ public bool UpgradedMirror = false;
+ public bool QuickBind = false;
+ public bool ReserveBind = false;
+ public bool Maggoted = false;
+
+ 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
index 5fe134b..49cc46c 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -1,14 +1,9 @@
-using System;
-using System.Collections;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using HutongGames.PlayMaker.Actions;
using SSMP.Internals;
using SSMP.Util;
using UnityEngine;
using Logger = SSMP.Logging.Logger;
-using Object = UnityEngine.Object;
namespace SSMP.Animation.Effects;
@@ -155,7 +150,7 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
/// Stops the bind bell animation
///
public void StopBindBell(GameObject bindEffects) {
- var bindBell = bindEffects.FindGameObjectInChildren(BIND_BELL_NAME);
+ var bindBell = bindEffects.FindGameObjectInChildren(BindBellName);
bindBell?.SetActive(false);
}
@@ -185,7 +180,7 @@ private void PlayWitchMaggoted(GameObject bindEffects) {
private void PlayWitchEnd(GameObject bindEffects) {
var witchBind = bindEffects.FindGameObjectInChildren("Witch Bind");
if (witchBind == null) {
- var localWitchBind = _localBindEffects?.FindGameObjectInChildren("Witch Bind");
+ var localWitchBind = LocalBindEffects?.FindGameObjectInChildren("Witch Bind");
if (localWitchBind == null) {
Logger.Warn("Unable to find local Witch Bind object");
return;
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
index ff9742f..2270597 100644
--- a/SSMP/Fsm/FsmStateActionInjector.cs
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -1,7 +1,5 @@
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
using HutongGames.PlayMaker;
using SSMP.Logging;
@@ -34,6 +32,9 @@ public void Uninject() {
actions.Remove(this);
State.Actions = actions.ToArray();
State.SaveActions();
+
+ _onStateEnter = null;
+ _onUninject -= Uninject;
}
///
From ec01d3cd7683d993ad69208af9cda6633db8d6bd Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 17:36:00 -0400
Subject: [PATCH 27/33] remove player id from animations, use object instance
id instead
---
SSMP/Animation/AnimationEffect.cs | 2 +-
SSMP/Animation/AnimationManager.cs | 1 -
SSMP/Animation/DamageAnimationEffect.cs | 2 +-
SSMP/Animation/Effects/Bind.cs | 7 ++++---
SSMP/Animation/Effects/BindBurst.cs | 18 +++++++++---------
SSMP/Animation/Effects/BindInterupt.cs | 2 +-
SSMP/Animation/Effects/DashSlash.cs | 2 +-
SSMP/Animation/Effects/DashSlashAntic.cs | 2 +-
SSMP/Animation/Effects/DashSlashReaper.cs | 2 +-
SSMP/Animation/Effects/DownSpike.cs | 2 +-
SSMP/Animation/Effects/NeedleStrike.cs | 2 +-
SSMP/Animation/Effects/Slash.cs | 2 +-
SSMP/Animation/Effects/SlashBase.cs | 2 +-
SSMP/Animation/IAnimationEffect.cs | 3 +--
SSMP/Fsm/FsmStateActionInjector.cs | 2 +-
15 files changed, 25 insertions(+), 26 deletions(-)
diff --git a/SSMP/Animation/AnimationEffect.cs b/SSMP/Animation/AnimationEffect.cs
index b1ffd94..fdce536 100644
--- a/SSMP/Animation/AnimationEffect.cs
+++ b/SSMP/Animation/AnimationEffect.cs
@@ -16,7 +16,7 @@ internal abstract class AnimationEffect : IAnimationEffect {
protected ServerSettings ServerSettings = null!;
///
- public abstract void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
+ public abstract void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
///
public abstract byte[]? GetEffectInfo();
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index fb1c011..69e49b6 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -860,7 +860,6 @@ public void OnPlayerAnimationUpdate(ushort id, int clipId, int frame, byte[]? ef
animationEffect.Play(
playerObject,
crestType,
- id,
effectInfo
);
}
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index c8fde05..be6fab6 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -14,7 +14,7 @@ internal abstract class DamageAnimationEffect : AnimationEffect {
protected bool ShouldDoDamage;
///
- public abstract override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
+ public abstract override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
///
public abstract override byte[]? GetEffectInfo();
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 5eac9ea..1ffa21a 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -31,15 +31,16 @@ internal class Bind : DamageAnimationEffect {
protected static GameObject? LocalBindEffects;
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
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 consistancy
+ var playerObjectIdentifier = playerObject.GetInstanceID();
if (flags.Maggoted && !(crestType == CrestType.Shaman && BindState == State.ShamanCancel)) {
- BindBurst.MaggotedPlayers.Add(playerId);
+ BindBurst.MaggotedPlayers.Add(playerObjectIdentifier);
} else {
- BindBurst.MaggotedPlayers.Remove(playerId);
+ BindBurst.MaggotedPlayers.Remove(playerObjectIdentifier);
}
var randomClipAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 49cc46c..11d3a11 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -22,15 +22,17 @@ internal class BindBurst : Bind {
///
/// A set of players who recently binded while maggoted
///
- public static HashSet MaggotedPlayers = new HashSet();
+ public static readonly HashSet MaggotedPlayers = new();
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
// Set maggot info
- Flags flags = new Flags(effectInfo);
- if (MaggotedPlayers.Contains(playerId)) {
+ var flags = new Flags(effectInfo);
+
+ var playerObjectIdentifier = playerObject.GetInstanceID();
+ if (MaggotedPlayers.Contains(playerObjectIdentifier)) {
flags.Maggoted = true;
- MaggotedPlayers.Remove(playerId);
+ MaggotedPlayers.Remove(playerObjectIdentifier);
}
if (!CreateObjects(playerObject, out var bindEffects)) {
@@ -40,7 +42,7 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(playerObject, flags.UpgradedMirror);
if (flags.Maggoted) PlayMaggotCleanse(bindEffects, playerObject);
- // Stop regardless of if its on or not
+ // Stop regardless of if it's on or not
StopBindBell(bindEffects);
// Play crest-specific animations
@@ -60,8 +62,6 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
case CrestType.Shaman:
PlayShamanEnd(bindEffects);
break;
- default:
- break;
}
PlayNormalEnd(bindEffects);
@@ -70,7 +70,7 @@ public override void Play(GameObject playerObject, CrestType crestType, ushort p
///
/// Plays the maggot cleanse animation
///
- protected void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
+ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
Logger.Info("Playing Maggot Animation");
var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
diff --git a/SSMP/Animation/Effects/BindInterupt.cs b/SSMP/Animation/Effects/BindInterupt.cs
index 843cd0d..eff3a7b 100644
--- a/SSMP/Animation/Effects/BindInterupt.cs
+++ b/SSMP/Animation/Effects/BindInterupt.cs
@@ -19,7 +19,7 @@ internal class BindInterupt : Bind {
public static BindInterupt Instance => _instance ??= new BindInterupt();
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
Flags flags = new Flags(effectInfo);
if (!CreateObjects(playerObject, out var bindEffects)) {
diff --git a/SSMP/Animation/Effects/DashSlash.cs b/SSMP/Animation/Effects/DashSlash.cs
index 14edeb0..8eac3f5 100644
--- a/SSMP/Animation/Effects/DashSlash.cs
+++ b/SSMP/Animation/Effects/DashSlash.cs
@@ -25,7 +25,7 @@ public DashSlash(DashSlashType dashSlashType) {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
if (effectInfo == null || effectInfo.Length < 1) {
Logger.Error("Could not get null or empty effect info for DashAttack");
return;
diff --git a/SSMP/Animation/Effects/DashSlashAntic.cs b/SSMP/Animation/Effects/DashSlashAntic.cs
index 78c4845..57e0add 100644
--- a/SSMP/Animation/Effects/DashSlashAntic.cs
+++ b/SSMP/Animation/Effects/DashSlashAntic.cs
@@ -23,7 +23,7 @@ private DashSlashAntic() {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
var sprintFsm = HeroController.instance.sprintFSM;
PlayAudioEvent playAudioAction;
switch (crestType) {
diff --git a/SSMP/Animation/Effects/DashSlashReaper.cs b/SSMP/Animation/Effects/DashSlashReaper.cs
index e095316..72c4190 100644
--- a/SSMP/Animation/Effects/DashSlashReaper.cs
+++ b/SSMP/Animation/Effects/DashSlashReaper.cs
@@ -9,7 +9,7 @@ namespace SSMP.Animation.Effects;
///
internal class DashSlashReaper : SlashBase {
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
// Call the base function with the correct parameters
Play(playerObject, crestType, effectInfo, SlashType.Dash);
diff --git a/SSMP/Animation/Effects/DownSpike.cs b/SSMP/Animation/Effects/DownSpike.cs
index 4b174a3..88e7110 100644
--- a/SSMP/Animation/Effects/DownSpike.cs
+++ b/SSMP/Animation/Effects/DownSpike.cs
@@ -11,7 +11,7 @@ namespace SSMP.Animation.Effects;
///
internal class DownSpike : SlashBase {
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
if (effectInfo == null || effectInfo.Length < 1) {
Logger.Error("Could not get null or empty effect info for SlashBase");
return;
diff --git a/SSMP/Animation/Effects/NeedleStrike.cs b/SSMP/Animation/Effects/NeedleStrike.cs
index a50d76f..e6e03f1 100644
--- a/SSMP/Animation/Effects/NeedleStrike.cs
+++ b/SSMP/Animation/Effects/NeedleStrike.cs
@@ -102,7 +102,7 @@ public NeedleStrike(bool witchLoop) {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
if (effectInfo == null || effectInfo.Length < 1) {
Logger.Error("Could not get null or empty effect info for SlashBase");
return;
diff --git a/SSMP/Animation/Effects/Slash.cs b/SSMP/Animation/Effects/Slash.cs
index c4350c5..18636cf 100644
--- a/SSMP/Animation/Effects/Slash.cs
+++ b/SSMP/Animation/Effects/Slash.cs
@@ -21,7 +21,7 @@ public Slash(SlashType slashType) {
}
///
- public override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo) {
+ public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
// Call the base function with the correct parameters
Play(playerObject, crestType, effectInfo, _slashType);
}
diff --git a/SSMP/Animation/Effects/SlashBase.cs b/SSMP/Animation/Effects/SlashBase.cs
index e789410..cff731b 100644
--- a/SSMP/Animation/Effects/SlashBase.cs
+++ b/SSMP/Animation/Effects/SlashBase.cs
@@ -16,7 +16,7 @@ namespace SSMP.Animation.Effects;
///
internal abstract class SlashBase : ParryableEffect {
///
- public abstract override void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
+ public abstract override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
///
public override byte[] GetEffectInfo() {
diff --git a/SSMP/Animation/IAnimationEffect.cs b/SSMP/Animation/IAnimationEffect.cs
index f23ed45..d8a9709 100644
--- a/SSMP/Animation/IAnimationEffect.cs
+++ b/SSMP/Animation/IAnimationEffect.cs
@@ -13,9 +13,8 @@ internal interface IAnimationEffect {
///
/// The GameObject representing the player.
/// The type of crest the player is using.
- /// The ID of the player sending the animation
/// A byte array containing effect info.
- void Play(GameObject playerObject, CrestType crestType, ushort playerId, byte[]? effectInfo);
+ void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo);
///
/// Get the effect info corresponding to this effect.
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
index 2270597..cca9c56 100644
--- a/SSMP/Fsm/FsmStateActionInjector.cs
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -5,7 +5,7 @@
namespace SSMP.Fsm;
-internal class FsmStateActionInjector : FsmStateAction {
+internal sealed class FsmStateActionInjector : FsmStateAction {
private static Action? _onUninject;
private Action? _onStateEnter;
private FsmStateActionInjector(FsmState state, Action onEnter) {
From a9418d93bcfea36dea90102e0d3eafa032172ab9 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 17:59:54 -0400
Subject: [PATCH 28/33] fix typos and more style changes
---
SSMP/Animation/AnimationManager.cs | 21 +++++++++---------
SSMP/Animation/DamageAnimationEffect.cs | 4 +++-
SSMP/Animation/Effects/Bind.cs | 18 +++++++--------
SSMP/Animation/Effects/BindBurst.cs | 22 ++++++++++---------
.../{BindInterupt.cs => BindInterrupt.cs} | 16 +++++++-------
SSMP/Animation/Effects/EffectUtils.cs | 15 ++++++++-----
6 files changed, 52 insertions(+), 44 deletions(-)
rename SSMP/Animation/Effects/{BindInterupt.cs => BindInterrupt.cs} (91%)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 69e49b6..d557977 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -662,7 +662,7 @@ internal class AnimationManager {
private static readonly Dictionary SubAnimationEffects = new() {
{ AnimationClip.WitchTentacles, BindBurst.Instance },
{ AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } },
- { AnimationClip.BindInterupt, BindInterupt.Instance }
+ { AnimationClip.BindInterupt, BindInterrupt.Instance }
};
///
@@ -1056,16 +1056,17 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
private void CreateBindHooks(HeroController hc) {
HeroController.instance.bellBindFSM.Init();
var heroFsms = hc.GetComponents();
- PlayMakerFSM bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
+
+ var bindFsm = heroFsms.FirstOrDefault(fsm => fsm.FsmName == "Bind");
if (bindFsm == null) {
Logger.Warn("Unable to find Bind FSM to hook.");
return;
}
- // Find witch crest tenticles
- var tenticles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
- if (tenticles != null) {
- FsmStateActionInjector.Inject(tenticles, OnWitchTentacles, 4);
+ // Find witch crest tentacles
+ var tentacles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
+ if (tentacles != null) {
+ FsmStateActionInjector.Inject(tentacles, OnWitchTentacles, 4);
} else {
Logger.Warn("Unable to find Witch Tentacles! state");
}
@@ -1077,9 +1078,9 @@ private void CreateBindHooks(HeroController hc) {
Logger.Warn("Unable to find Shaman Air Cancel state");
}
- var bindInterupt = bindFsm.GetState("Remove Silk?");
- if (bindInterupt != null) {
- FsmStateActionInjector.Inject(bindInterupt, OnBindInterupt, 2);
+ var bindInterrupt = bindFsm.GetState("Remove Silk?");
+ if (bindInterrupt != null) {
+ FsmStateActionInjector.Inject(bindInterrupt, OnBindInterrupt, 2);
}
}
@@ -1105,7 +1106,7 @@ private void OnShamanCancel(PlayMakerFSM fsm) {
///
/// Animation subanimation hook for interupted binds
///
- private void OnBindInterupt(PlayMakerFSM fsm) {
+ private void OnBindInterrupt(PlayMakerFSM fsm) {
var dummyClip = new tk2dSpriteAnimationClip();
dummyClip.name = "Bind Fail Burst";
dummyClip.wrapMode = tk2dSpriteAnimationClip.WrapMode.Once;
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index be6fab6..e7707cc 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -33,6 +33,7 @@ public void SetShouldDoDamage(bool shouldDoDamage) {
///
/// The target game object to attach the component to.
/// The number of mask of damage it should deal.
+ /// 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;
@@ -51,7 +52,7 @@ protected static void RemoveDamageHeroComponent(GameObject target) {
return;
}
- Component.DestroyImmediate(damageHero);
+ Object.DestroyImmediate(damageHero);
}
///
@@ -60,6 +61,7 @@ protected static void RemoveDamageHeroComponent(GameObject target) {
///
/// 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);
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 1ffa21a..4c54f3f 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -35,7 +35,7 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
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 consistancy
+ // 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);
@@ -279,7 +279,7 @@ private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
EffectUtils.SpawnGlobalPoolObject(silkPuffSpawner, playerObject.transform, 5f);
- BindBurst.Instance.StopBindBell(bindEffects);
+ BindBurst.StopBindBell(bindEffects);
}
///
@@ -381,7 +381,7 @@ protected bool CreateObjects(GameObject playerObject, [MaybeNullWhen(false)] out
/// Turns off all bind effects
///
public static void ForceStopAllEffects(GameObject bindEffects) {
- BindBurst.Instance.StopBindBell(bindEffects);
+ BindBurst.StopBindBell(bindEffects);
var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
if (shamanAntic != null) {
@@ -423,12 +423,12 @@ public enum State {
/// Effect flags sent by the other player. Mostly items they have equipped.
///
protected class Flags {
- public bool BindBell = false;
- public bool BaseMirror = false;
- public bool UpgradedMirror = false;
- public bool QuickBind = false;
- public bool ReserveBind = false;
- public bool Maggoted = false;
+ public readonly bool BindBell = false;
+ public readonly bool BaseMirror = false;
+ public readonly bool UpgradedMirror = false;
+ public readonly bool QuickBind = false;
+ public readonly bool ReserveBind = false;
+ public bool Maggoted = false; // Needs to be writable by BindBurst
public Flags(byte[]? info) {
if (info == null) return;
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 11d3a11..ae7472b 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -88,8 +88,7 @@ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject)
/// Creates the appropriate Claw Mirror object.
/// The object will be destroyed after it finishes.
///
- private GameObject? PrepareMirror(GameObject playerObject, SetGameObject mirrorSource) {
-
+ private static GameObject? PrepareMirror(GameObject playerObject, SetGameObject mirrorSource) {
var mirror = EffectUtils.SpawnGlobalPoolObject(mirrorSource.gameObject.Value, playerObject.transform, 3f);
if (mirror == null) {
@@ -98,12 +97,12 @@ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject)
var shaker = mirror.GetComponentInChildren();
if (shaker != null) {
- Component.DestroyImmediate(shaker);
+ Object.DestroyImmediate(shaker);
}
var haze = mirror.FindGameObjectInChildren("haze2");
if (haze != null) {
- GameObject.Destroy(haze);
+ Object.Destroy(haze);
}
return mirror;
@@ -121,8 +120,11 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
// Create the claw
GameObject? claw;
- if (upgraded) claw = PrepareMirror(playerObject, upgradedClaw);
- else claw = PrepareMirror(playerObject, regularClaw);
+ if (upgraded) {
+ claw = PrepareMirror(playerObject, upgradedClaw);
+ } else {
+ claw = PrepareMirror(playerObject, regularClaw);
+ }
if (claw == null) {
return;
@@ -149,7 +151,7 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
///
/// Stops the bind bell animation
///
- public void StopBindBell(GameObject bindEffects) {
+ public static void StopBindBell(GameObject bindEffects) {
var bindBell = bindEffects.FindGameObjectInChildren(BindBellName);
bindBell?.SetActive(false);
}
@@ -186,11 +188,11 @@ private void PlayWitchEnd(GameObject bindEffects) {
return;
}
- witchBind = GameObject.Instantiate(localWitchBind, bindEffects.transform);
+ witchBind = Object.Instantiate(localWitchBind, bindEffects.transform);
var shaker = witchBind.GetComponent();
if (shaker != null) {
- Component.DestroyImmediate(shaker);
+ Object.DestroyImmediate(shaker);
}
SetWitchDamagers(witchBind);
@@ -222,7 +224,7 @@ private void SetWitchDamagers(GameObject witchBind) {
///
/// Stops the Shaman Crest specific silk animation
///
- private void PlayShamanEnd(GameObject bindEffects) {
+ private static void PlayShamanEnd(GameObject bindEffects) {
var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
if (shamanAntic == null) {
return;
diff --git a/SSMP/Animation/Effects/BindInterupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
similarity index 91%
rename from SSMP/Animation/Effects/BindInterupt.cs
rename to SSMP/Animation/Effects/BindInterrupt.cs
index eff3a7b..dc36b04 100644
--- a/SSMP/Animation/Effects/BindInterupt.cs
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -7,20 +7,20 @@
namespace SSMP.Animation.Effects;
///
-/// Class for the animation effect of a bind (healing) being interupted by an attack.
+/// Class for the animation effect of a bind (healing) being interrupted by an attack.
///
-internal class BindInterupt : Bind {
+internal class BindInterrupt : Bind {
///
/// Static instance for access by multiple animation clips in .
///
- private static BindInterupt? _instance;
+ private static BindInterrupt? _instance;
///
- public static BindInterupt Instance => _instance ??= new BindInterupt();
+ public static BindInterrupt Instance => _instance ??= new BindInterrupt();
///
public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
- Flags flags = new Flags(effectInfo);
+ var flags = new Flags(effectInfo);
if (!CreateObjects(playerObject, out var bindEffects)) {
return;
@@ -60,12 +60,12 @@ private void PlayBindInterupt(GameObject bindEffects) {
// Remove haze and camera controls
var haze = burst.FindGameObjectInChildren("haze2");
if (haze != null) {
- GameObject.Destroy(haze);
+ Object.Destroy(haze);
}
var shaker = burst.GetComponentInChildren();
if (shaker != null) {
- Component.DestroyImmediate(shaker);
+ Object.DestroyImmediate(shaker);
}
}
@@ -113,7 +113,7 @@ private void PlayBellExplode(GameObject bindEffects) {
var haze = bindBell.FindGameObjectInChildren("haze2 (1)");
if (haze != null) {
- GameObject.Destroy(haze);
+ Object.Destroy(haze);
}
// Add hitbox if appropriate
diff --git a/SSMP/Animation/Effects/EffectUtils.cs b/SSMP/Animation/Effects/EffectUtils.cs
index f38dcff..940899a 100644
--- a/SSMP/Animation/Effects/EffectUtils.cs
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -19,7 +19,7 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
recycler.recycleTimerRunning = false;
recycler.subbed = false;
- Component.Destroy(recycler);
+ Object.Destroy(recycler);
}
}
@@ -42,22 +42,25 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
///
/// 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;
}
- var newObj = GameObject.Instantiate(globalObj, spawnLocation);
+ // 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;
}
- // This is ugly and i hate it, but it works
+ // Now it can be orphaned if needed.
if (!keepParent) {
newObj.transform.SetParent(null);
newObj.transform.position = spawnLocation.position;
From 3c1c65509f4590956d159410ff527e9b94d5186a Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 18:01:22 -0400
Subject: [PATCH 29/33] missed a component destroy
---
SSMP/Animation/Effects/BindInterrupt.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SSMP/Animation/Effects/BindInterrupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
index dc36b04..e697106 100644
--- a/SSMP/Animation/Effects/BindInterrupt.cs
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -108,7 +108,7 @@ private void PlayBellExplode(GameObject bindEffects) {
// Remove camera control and haze
var shaker = bindBell.GetComponentInChildren();
if (shaker != null) {
- Component.DestroyImmediate(shaker);
+ Object.DestroyImmediate(shaker);
}
var haze = bindBell.FindGameObjectInChildren("haze2 (1)");
From acd47b3988339fa7c5da7aa70fcc1b32a8d74afe Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 18:06:46 -0400
Subject: [PATCH 30/33] i'm bad at splelling apparently
---
SSMP/Animation/AnimationClip.cs | 2 +-
SSMP/Animation/AnimationManager.cs | 6 +++---
SSMP/Animation/Effects/BindInterrupt.cs | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/SSMP/Animation/AnimationClip.cs b/SSMP/Animation/AnimationClip.cs
index 9f2d568..c7e54e4 100644
--- a/SSMP/Animation/AnimationClip.cs
+++ b/SSMP/Animation/AnimationClip.cs
@@ -764,5 +764,5 @@ internal enum AnimationClip {
// Sub-animation names
WitchTentacles,
ShamanCancel,
- BindInterupt,
+ BindInterrupt,
}
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index d557977..850f55c 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -619,7 +619,7 @@ internal class AnimationManager {
{ "Witch Tentacles!", AnimationClip.WitchTentacles },
{ "Shaman Cancel", AnimationClip.ShamanCancel },
- { "Bind Fail Burst", AnimationClip.BindInterupt }
+ { "Bind Fail Burst", AnimationClip.BindInterrupt }
};
///
@@ -662,7 +662,7 @@ internal class AnimationManager {
private static readonly Dictionary SubAnimationEffects = new() {
{ AnimationClip.WitchTentacles, BindBurst.Instance },
{ AnimationClip.ShamanCancel, new Bind { BindState = Bind.State.ShamanCancel } },
- { AnimationClip.BindInterupt, BindInterrupt.Instance }
+ { AnimationClip.BindInterrupt, BindInterrupt.Instance }
};
///
@@ -1104,7 +1104,7 @@ private void OnShamanCancel(PlayMakerFSM fsm) {
OnAnimationEvent(dummyClip);
}
///
- /// Animation subanimation hook for interupted binds
+ /// Animation subanimation hook for interrupted binds
///
private void OnBindInterrupt(PlayMakerFSM fsm) {
var dummyClip = new tk2dSpriteAnimationClip();
diff --git a/SSMP/Animation/Effects/BindInterrupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
index e697106..7c4e538 100644
--- a/SSMP/Animation/Effects/BindInterrupt.cs
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -31,14 +31,14 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
if (flags.BindBell) {
PlayBellExplode(bindEffects);
} else {
- PlayBindInterupt(bindEffects);
+ PlayBindInterrupt(bindEffects);
}
}
///
/// Plays the normal bind interput animation
///
- private void PlayBindInterupt(GameObject bindEffects) {
+ private void PlayBindInterrupt(GameObject bindEffects) {
// Find prefab
var bindBurstSpawner = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
if (bindBurstSpawner == null) {
From 868cdeba1211985c27151cb709f6650bbb3073b8 Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 23:01:30 -0400
Subject: [PATCH 31/33] remove redundant checks, use vars for more strings
---
SSMP/Animation/AnimationManager.cs | 29 ++++----
SSMP/Animation/Effects/Bind.cs | 98 ++++++++++++++-----------
SSMP/Animation/Effects/BindBurst.cs | 83 +++++++++++++--------
SSMP/Animation/Effects/BindInterrupt.cs | 37 ++++------
SSMP/Fsm/FsmStateActionInjector.cs | 6 +-
5 files changed, 140 insertions(+), 113 deletions(-)
diff --git a/SSMP/Animation/AnimationManager.cs b/SSMP/Animation/AnimationManager.cs
index 850f55c..6d300ee 100644
--- a/SSMP/Animation/AnimationManager.cs
+++ b/SSMP/Animation/AnimationManager.cs
@@ -1054,7 +1054,14 @@ private void OnAnimationEvent(tk2dSpriteAnimationClip clip) {
/// the Bind fsm once the HeroController is ready.
///
private void CreateBindHooks(HeroController hc) {
- HeroController.instance.bellBindFSM.Init();
+ // 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");
@@ -1063,25 +1070,15 @@ private void CreateBindHooks(HeroController hc) {
return;
}
- // Find witch crest tentacles
+ // Find FSM states to inject
var tentacles = bindFsm.GetState("Witch Tentancles!"); // no that's not a typo... at least on my end
- if (tentacles != null) {
- FsmStateActionInjector.Inject(tentacles, OnWitchTentacles, 4);
- } else {
- Logger.Warn("Unable to find Witch Tentacles! state");
- }
-
+ FsmStateActionInjector.Inject(tentacles, OnWitchTentacles, 4);
+
var shamanCancel = bindFsm.GetState("Shaman Air Cancel");
- if (shamanCancel != null) {
- FsmStateActionInjector.Inject(shamanCancel, OnShamanCancel);
- } else {
- Logger.Warn("Unable to find Shaman Air Cancel state");
- }
+ FsmStateActionInjector.Inject(shamanCancel, OnShamanCancel);
var bindInterrupt = bindFsm.GetState("Remove Silk?");
- if (bindInterrupt != null) {
- FsmStateActionInjector.Inject(bindInterrupt, OnBindInterrupt, 2);
- }
+ FsmStateActionInjector.Inject(bindInterrupt, OnBindInterrupt, 2);
}
///
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 4c54f3f..70e0421 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -15,9 +15,17 @@ namespace SSMP.Animation.Effects;
/// Class for the animation effect of bind (healing).
///
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";
- protected const string BindBellName = "bind_bell_appear_instance";
-
+ ///
+ /// Keeps track of special bind scenarios, mostly to do with Shaman Crest.
+ ///
public State BindState = State.Normal;
///
@@ -94,7 +102,7 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
///
private void StartBindBell(GameObject bindEffects) {
Logger.Debug("Starting warding bell");
- var bindBell = bindEffects.FindGameObjectInChildren(BindBellName);
+ var bindBell = bindEffects.FindGameObjectInChildren(BindBellObjectName);
if (bindBell == null) {
var allObjects = Resources.FindObjectsOfTypeAll();
@@ -106,7 +114,7 @@ private void StartBindBell(GameObject bindEffects) {
}
bindBell = Object.Instantiate(localBell, bindEffects.transform);
- bindBell.name = BindBellName;
+ bindBell.name = BindBellObjectName;
var follower = bindBell.GetComponent();
follower.target = bindEffects.transform;
@@ -125,7 +133,7 @@ private void StartBindBell(GameObject bindEffects) {
///
private void PlayNormalStart(GameObject bindEffects, Flags flags) {
Logger.Debug("Playing normal bind start animation");
- var bindSilkObj = CreateEffectIfNotExists(bindEffects, "Bind Silk");
+ var bindSilkObj = CreateEffectIfNotExists(bindEffects, BindSilkObjectName);
if (bindSilkObj == null) {
return;
}
@@ -151,7 +159,7 @@ private void PlayNormalStart(GameObject bindEffects, Flags flags) {
///
private void PlayBeastBindStart(GameObject bindEffects) {
Logger.Debug("Playing Beast Crest start antic");
- var beastAntic = CreateEffectIfNotExists(bindEffects, "Warrior_Bind_antic_silk");
+ var beastAntic = CreateEffectIfNotExists(bindEffects, BeastCrestAnticObjectName);
if (beastAntic == null) {
return;
}
@@ -165,7 +173,7 @@ private void PlayBeastBindStart(GameObject bindEffects) {
///
private void PlayCursedFail(GameObject bindEffects) {
Logger.Debug("Playing Cursed Crest bind fail animation");
- var failAntic = bindEffects.FindGameObjectInChildren("cursed_bind_fail");
+ var failAntic = bindEffects.FindGameObjectInChildren(CursedBindFailObjectName);
tk2dSpriteAnimator animator;
if (failAntic == null) {
@@ -175,14 +183,14 @@ private void PlayCursedFail(GameObject bindEffects) {
return;
}
- var localFailAntic = effects.FindGameObjectInChildren("Cursed Bind Hornet");
+ 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 = "cursed_bind_fail";
+ failAntic.name = CursedBindFailObjectName;
failAntic.transform.SetLocalPositionZ(failAntic.transform.localPosition.z - 0.25f);
animator = failAntic.GetComponent();
@@ -213,7 +221,7 @@ private void PlayNextCursedPart(tk2dSpriteAnimator animator, tk2dSpriteAnimation
///
private void PlayWitchAnimationAntic(GameObject bindEffects) {
Logger.Debug("Playing Witch Crest bind antic");
- var silkAntic = CreateEffectIfNotExists(bindEffects, "Whip_Bind_silk_antic");
+ var silkAntic = CreateEffectIfNotExists(bindEffects, WitchBindObjectName);
if (silkAntic == null) {
return;
}
@@ -244,7 +252,7 @@ private bool PickShamanAnimation(GameObject playerObject, GameObject bindEffects
///
private void PlayShamanFall(GameObject bindEffects) {
Logger.Debug("Playing Shaman Crest bind fall animation");
- var shamanAntic = CreateEffectIfNotExists(bindEffects, "Shaman_Bind_antic_silk");
+ var shamanAntic = CreateEffectIfNotExists(bindEffects, ShamanFallAnticObjectName);
if (shamanAntic == null) {
return;
}
@@ -261,22 +269,17 @@ private void PlayShamanFall(GameObject bindEffects) {
///
private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
Logger.Debug("Playing Shaman Crest bind cancel/fail animation");
- var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
if (shamanAntic != null) {
shamanAntic.SetActive(false);
}
- var silkPuffSpawner = GetOrFindBindFsm().GetFirstAction("Shaman Air Cancel");
- if (silkPuffSpawner == null) {
- Logger.Warn("Unable to find FSM action for Shaman Air Cancel");
- return;
- }
-
- var audio = GetOrFindBindFsm().GetFirstAction("Shaman Air Cancel");
- if (audio != null) {
- PlaySound(playerObject, audio);
- }
+ var cancelStateName = "Shaman Air Cancel";
+ var silkPuffSpawner = GetOrFindBindFsm().GetFirstAction(cancelStateName);
+ var audio = GetOrFindBindFsm().GetFirstAction(cancelStateName);
+ PlaySound(playerObject, audio);
+
EffectUtils.SpawnGlobalPoolObject(silkPuffSpawner, playerObject.transform, 5f);
BindBurst.StopBindBell(bindEffects);
@@ -287,7 +290,7 @@ private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
///
private void PlayShamanFallEnd(GameObject bindEffects) {
Logger.Debug("Playing Shaman Crest bind fall finished transition animation");
- var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
if (shamanAntic == null) {
return;
}
@@ -360,50 +363,63 @@ protected bool CreateObjects(GameObject playerObject, [MaybeNullWhen(false)] out
///
/// The player's Bind Effects object.
/// The name of the effect to find or create.
- /// The existing or new effect, or null if the effect cannot be found or created
- protected GameObject? CreateEffectIfNotExists(GameObject bindEffects, string objectName) {
- var obj = bindEffects.FindGameObjectInChildren(objectName);
- if (obj == null) {
- var localObj = LocalBindEffects!.FindGameObjectInChildren(objectName);
- if (localObj == null) {
- Logger.Warn($"Could not find local {objectName} object, cannot play bind");
- return null;
- }
+ /// 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;
+ }
- obj = Object.Instantiate(localObj, bindEffects.transform);
- obj.name = objectName;
+ var localObj = LocalBindEffects!.FindGameObjectInChildren(objectName);
+ if (localObj == null) {
+ Logger.Warn($"Could not find local {objectName} object, cannot play bind effect");
+ return false;
}
- return obj;
+ 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("Shaman_Bind_antic_silk");
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
if (shamanAntic != null) {
shamanAntic.SetActive(false);
}
- var silkAntic = bindEffects.FindGameObjectInChildren("Whip_Bind_silk_antic");
+ var silkAntic = bindEffects.FindGameObjectInChildren(WitchBindObjectName);
if (silkAntic != null) {
silkAntic.SetActive(false);
}
- var cursedFailAntic = bindEffects.FindGameObjectInChildren("cursed_bind_fail");
+ var cursedFailAntic = bindEffects.FindGameObjectInChildren(CursedBindFailObjectName);
if (cursedFailAntic != null) {
cursedFailAntic.SetActive(false);
}
- var beastAntic = bindEffects.FindGameObjectInChildren("Warrior_Bind_antic_silk");
+ var beastAntic = bindEffects.FindGameObjectInChildren(BeastCrestAnticObjectName);
if (beastAntic != null) {
beastAntic.SetActive(false);
}
- var bindSilkObj = bindEffects.FindGameObjectInChildren("Bind Silk");
+ var bindSilkObj = bindEffects.FindGameObjectInChildren(BindSilkObjectName);
if (bindSilkObj != null) {
bindSilkObj.SetActive(false);
}
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index ae7472b..b13bab3 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -11,16 +11,20 @@ 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 .
///
- private static BindBurst? _instance;
-
- ///
- public static BindBurst Instance => _instance ??= new BindBurst();
+ public static readonly BindBurst Instance = new();
///
- /// A set of players who recently binded while maggoted
+ /// A set of players who recently bound while maggoted
///
public static readonly HashSet MaggotedPlayers = new();
@@ -39,6 +43,7 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
return;
}
+ // Play flag-specific effects
if (flags.BaseMirror || flags.UpgradedMirror) PlayMirror(playerObject, flags.UpgradedMirror);
if (flags.Maggoted) PlayMaggotCleanse(bindEffects, playerObject);
@@ -71,14 +76,17 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
/// Plays the maggot cleanse animation
///
private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject) {
- Logger.Info("Playing Maggot Animation");
- var maggotBurst = GetOrFindBindFsm().GetAction("Maggoted?", 2);
- var maggotFlash = GetOrFindBindFsm().GetAction("Maggoted?", 4);
- var maggotAudio = GetOrFindBindFsm().GetFirstAction("Maggoted?");
+ // 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
if (maggotAudio != null) {
PlaySound(playerObject, maggotAudio);
}
@@ -89,12 +97,14 @@ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject)
/// 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);
@@ -152,7 +162,8 @@ private void PlayMirror(GameObject playerObject, bool upgraded) {
/// Stops the bind bell animation
///
public static void StopBindBell(GameObject bindEffects) {
- var bindBell = bindEffects.FindGameObjectInChildren(BindBellName);
+ // Only bother turning it off if it's on
+ var bindBell = bindEffects.FindGameObjectInChildren(BindBellObjectName);
bindBell?.SetActive(false);
}
@@ -160,15 +171,22 @@ public static void StopBindBell(GameObject bindEffects) {
/// Plays the Beast Crest specific rage animation
///
private void PlayBeastRage(GameObject bindEffects) {
- var beastRage = CreateEffectIfNotExists(bindEffects, "crest rage_burst_effect(Clone)");
- beastRage?.SetActive(true);
+ // 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) {
- var maggotCleanse = CreateEffectIfNotExists(bindEffects, "Witch Bind Maggot Cleanse");
+ // Spawn in the special tentacles
+ var maggotCleanse = CreateEffectIfNotExists(bindEffects, WitchBindCleanseObjectName);
+
if (maggotCleanse != null) {
maggotCleanse.SetActive(false);
maggotCleanse.SetActive(true);
@@ -180,24 +198,24 @@ private void PlayWitchMaggoted(GameObject bindEffects) {
/// Yes it's called Tentancles internally. Thanks TC.
///
private void PlayWitchEnd(GameObject bindEffects) {
- var witchBind = bindEffects.FindGameObjectInChildren("Witch Bind");
+ // Get bind effect
+ var effectWasCreated = CreateEffectIfNotExists(bindEffects, WitchBindObjectName, out var witchBind);
if (witchBind == null) {
- var localWitchBind = LocalBindEffects?.FindGameObjectInChildren("Witch Bind");
- if (localWitchBind == null) {
- Logger.Warn("Unable to find local Witch Bind object");
- return;
- }
-
- witchBind = Object.Instantiate(localWitchBind, bindEffects.transform);
+ return;
+ }
+ // Remove camera controls if object was created
+ if (effectWasCreated) {
var shaker = witchBind.GetComponent();
if (shaker != null) {
Object.DestroyImmediate(shaker);
}
-
- SetWitchDamagers(witchBind);
}
+ // Toggle damage depending on if PVP is on or not
+ SetWitchDamagers(witchBind);
+
+ // Play tentacles audio
var audio = GetOrFindBindFsm().GetFirstAction("Witch Tentancles!");
if (audio != null) {
PlaySound(bindEffects.transform.parent.gameObject, audio);
@@ -211,12 +229,14 @@ private void PlayWitchEnd(GameObject bindEffects) {
/// 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);
}
}
@@ -225,7 +245,7 @@ private void SetWitchDamagers(GameObject witchBind) {
/// Stops the Shaman Crest specific silk animation
///
private static void PlayShamanEnd(GameObject bindEffects) {
- var shamanAntic = bindEffects.FindGameObjectInChildren("Shaman_Bind_antic_silk");
+ var shamanAntic = bindEffects.FindGameObjectInChildren(ShamanFallAnticObjectName);
if (shamanAntic == null) {
return;
}
@@ -233,31 +253,32 @@ private static void PlayShamanEnd(GameObject bindEffects) {
}
///
- /// Plays particles and shows a flash
+ /// Plays particles and shows a flash. Happens at the end of a normal bind.
///
private void PlayNormalEnd(GameObject bindEffects) {
- Logger.Info("Playing Heal Particles and Anim");
- var healParticle = CreateEffectIfNotExists(bindEffects, "Pt Heal");
+ // Play particle effect
+ CreateEffectIfNotExists(bindEffects, HealParticleObjectName, out var healParticle);
if (healParticle != null) {
healParticle.GetComponent().Play();
}
- var healAnim = CreateEffectIfNotExists(bindEffects, "Heal Anim");
+ // Play silk animation
+ var healAnim = CreateEffectIfNotExists(bindEffects, HealAnimObjectName);
if (healAnim != null) {
healAnim.SetActive(true);
}
- var bindSilkObj = bindEffects.FindGameObjectInChildren("Bind Silk");
+ // 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");
if (audio != null) {
PlaySound(bindEffects.transform.parent.gameObject, audio);
}
-
-
}
}
diff --git a/SSMP/Animation/Effects/BindInterrupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
index 7c4e538..5dc9c6e 100644
--- a/SSMP/Animation/Effects/BindInterrupt.cs
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -13,21 +13,21 @@ internal class BindInterrupt : Bind {
///
/// Static instance for access by multiple animation clips in .
///
- private static BindInterrupt? _instance;
-
- ///
- public static BindInterrupt Instance => _instance ??= new BindInterrupt();
+ 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 {
@@ -36,25 +36,22 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
}
///
- /// Plays the normal bind interput animation
+ /// Plays the normal bind interrupt animation
///
private void PlayBindInterrupt(GameObject bindEffects) {
// Find prefab
- var bindBurstSpawner = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
- if (bindBurstSpawner == null) {
- Logger.Warn("Unable to find bind burst effect spawner");
- return;
- }
+ 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 burst effect");
+ Logger.Warn("Unable to create bind interrupt effect");
return;
}
// Play audio
- var audio = GetOrFindBindFsm().GetFirstAction("Remove Silk?");
+ var audio = GetOrFindBindFsm().GetFirstAction(stateName);
PlaySound(bindEffects.transform.parent.gameObject, audio);
// Remove haze and camera controls
@@ -75,24 +72,16 @@ private void PlayBindInterrupt(GameObject bindEffects) {
private void PlayBellExplode(GameObject bindEffects) {
Logger.Debug("Playing Bell Burst");
- // Initialize warding bell FSM if it isn't already.
- // This fills it in with the template
+ // Locate warding bell FSM
var bellFsm = HeroController.instance.bellBindFSM;
- if (!bellFsm.fsm.initialized) {
- HeroController.instance.bellBindFSM.Init();
- }
if (bellFsm == null) {
Logger.Warn("Unable to find warding bell fsm");
return;
}
- var spawner = bellFsm.GetFirstAction("Burst");
-
- if (spawner == null) {
- Logger.Warn("Unable to find warding bell spawner");
- return;
- }
+ var stateName = "Burst";
+ var spawner = bellFsm.GetFirstAction(stateName);
// Spawn warding bell
var bindBell = EffectUtils.SpawnGlobalPoolObject(spawner, bindEffects.transform, 5f);
@@ -101,7 +90,7 @@ private void PlayBellExplode(GameObject bindEffects) {
}
// Play sound
- var audio = bellFsm.GetFirstAction("Burst");
+ var audio = bellFsm.GetFirstAction(stateName);
PlaySound(bindBell, audio);
diff --git a/SSMP/Fsm/FsmStateActionInjector.cs b/SSMP/Fsm/FsmStateActionInjector.cs
index cca9c56..034ffb9 100644
--- a/SSMP/Fsm/FsmStateActionInjector.cs
+++ b/SSMP/Fsm/FsmStateActionInjector.cs
@@ -41,7 +41,7 @@ public void Uninject() {
public override void OnEnter() {
if (_onStateEnter != null) {
try {
- _onStateEnter?.Invoke(Fsm.FsmComponent);
+ _onStateEnter.Invoke(Fsm.FsmComponent);
} catch (Exception e) {
Logger.Error(e.ToString());
}
@@ -57,6 +57,10 @@ public override void OnEnter() {
/// 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);
From 97dddc71bd0cf7a6070ee0874fc02a74b515738a Mon Sep 17 00:00:00 2001
From: BobbyTheCatfish <46359040+BobbyTheCatfish@users.noreply.github.com>
Date: Wed, 18 Mar 2026 23:07:39 -0400
Subject: [PATCH 32/33] add util to remove child gameobjects
---
SSMP/Animation/Effects/BindBurst.cs | 5 +----
SSMP/Animation/Effects/BindInterrupt.cs | 10 ++--------
SSMP/Util/GameObjectUtil.cs | 20 ++++++++++++++++++++
3 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index b13bab3..367c3de 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -110,10 +110,7 @@ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject)
Object.DestroyImmediate(shaker);
}
- var haze = mirror.FindGameObjectInChildren("haze2");
- if (haze != null) {
- Object.Destroy(haze);
- }
+ mirror.DestroyGameObjectInChildren("haze2");
return mirror;
}
diff --git a/SSMP/Animation/Effects/BindInterrupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
index 5dc9c6e..d2b103b 100644
--- a/SSMP/Animation/Effects/BindInterrupt.cs
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -55,10 +55,7 @@ private void PlayBindInterrupt(GameObject bindEffects) {
PlaySound(bindEffects.transform.parent.gameObject, audio);
// Remove haze and camera controls
- var haze = burst.FindGameObjectInChildren("haze2");
- if (haze != null) {
- Object.Destroy(haze);
- }
+ burst.DestroyGameObjectInChildren("haze2");
var shaker = burst.GetComponentInChildren();
if (shaker != null) {
@@ -100,10 +97,7 @@ private void PlayBellExplode(GameObject bindEffects) {
Object.DestroyImmediate(shaker);
}
- var haze = bindBell.FindGameObjectInChildren("haze2 (1)");
- if (haze != null) {
- Object.Destroy(haze);
- }
+ bindBell.DestroyGameObjectInChildren("haze2 (1)");
// Add hitbox if appropriate
if (ServerSettings.IsPvpEnabled && ShouldDoDamage) {
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.
///
From 5726cbdf0b092bac15692e034565d3802dc0d27d Mon Sep 17 00:00:00 2001
From: Extremelyd1 <10898310+Extremelyd1@users.noreply.github.com>
Date: Sat, 4 Apr 2026 11:54:44 +0200
Subject: [PATCH 33/33] Final cleanup - Rename audio util methods - Remove
unused imports - Change unneeded accessibility of variables - Various style
formatting changes - Fix doc comment cref
---
SSMP/Animation/AnimationEffect.cs | 35 ------------------
SSMP/Animation/DamageAnimationEffect.cs | 2 +-
SSMP/Animation/Effects/Bind.cs | 45 +++++++++++------------
SSMP/Animation/Effects/BindBurst.cs | 14 ++-----
SSMP/Animation/Effects/BindInterrupt.cs | 4 +-
SSMP/Animation/Effects/DashSlash.cs | 2 +-
SSMP/Animation/Effects/DashSlashAntic.cs | 10 ++---
SSMP/Animation/Effects/DashSlashReaper.cs | 2 +-
SSMP/Animation/Effects/EffectUtils.cs | 2 +-
SSMP/Animation/Effects/NeedleStrike.cs | 12 +++---
SSMP/Util/AudioUtil.cs | 8 ++--
11 files changed, 47 insertions(+), 89 deletions(-)
diff --git a/SSMP/Animation/AnimationEffect.cs b/SSMP/Animation/AnimationEffect.cs
index fdce536..56e6960 100644
--- a/SSMP/Animation/AnimationEffect.cs
+++ b/SSMP/Animation/AnimationEffect.cs
@@ -1,7 +1,5 @@
-using HutongGames.PlayMaker.Actions;
using SSMP.Game.Settings;
using SSMP.Internals;
-using SSMP.Util;
using UnityEngine;
namespace SSMP.Animation;
@@ -42,37 +40,4 @@ protected static void ChangeAttackDirection(GameObject targetObject, float direc
var directionVar = damageFsm.FsmVariables.GetFsmFloat("direction");
directionVar.Value = direction;
}
-
-
- ///
- /// Plays a one-shot audio clip at the specified GameObject source.
- ///
- /// The object to play the sound at.
- /// The audio clip to be played.
- protected static void PlaySound(GameObject source, AudioPlayerOneShotSingle audio) {
- AudioUtil.PlayAudioOneShotSingleAtPlayerObject(audio, source);
- }
-
- ///
- /// Plays a specified audio event at the specified GameObject source.
- ///
- /// The object to play the sound at.
- /// The FSM action with the audio clip to be played.
- protected static void PlaySound(GameObject source, PlayAudioEvent audio) {
- AudioUtil.PlayAudioEventAtPlayerObject(audio, source);
- }
-
- ///
- /// Plays a random sound effect at the specified GameObject source
- ///
- /// The object to play the sound at.
- /// The FSM action with the audio table.
- /// The FSM action with the audio playing function.
- protected static void PlaySound(GameObject source, GetRandomAudioClipFromTable getAction, PlayAudioEvent playAction) {
- AudioUtil.PlayAudioEventWithRandomAudioClipFromTableAtPlayerObject(
- playAction,
- getAction,
- source
- );
- }
}
diff --git a/SSMP/Animation/DamageAnimationEffect.cs b/SSMP/Animation/DamageAnimationEffect.cs
index e7707cc..aedc240 100644
--- a/SSMP/Animation/DamageAnimationEffect.cs
+++ b/SSMP/Animation/DamageAnimationEffect.cs
@@ -45,7 +45,7 @@ protected static DamageHero AddDamageHeroComponent(GameObject target, int damage
///
/// Removes a component from the given game object.
///
- /// The target game object to detatch the component to.
+ /// The target game object to detach the component from.
protected static void RemoveDamageHeroComponent(GameObject target) {
var damageHero = target.GetComponent();
if (damageHero == null) {
diff --git a/SSMP/Animation/Effects/Bind.cs b/SSMP/Animation/Effects/Bind.cs
index 70e0421..d3d9174 100644
--- a/SSMP/Animation/Effects/Bind.cs
+++ b/SSMP/Animation/Effects/Bind.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using HutongGames.PlayMaker.Actions;
@@ -31,12 +30,12 @@ internal class Bind : DamageAnimationEffect {
///
/// Cached FSM for Hornet's bind ability.
///
- protected static PlayMakerFSM? BindFsm;
+ private static PlayMakerFSM? _bindFsm;
///
/// Cached effects object for Hornet's bind ability.
///
- protected static GameObject? LocalBindEffects;
+ private static GameObject? _localBindEffects;
///
public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
@@ -55,10 +54,10 @@ public override void Play(GameObject playerObject, CrestType crestType, byte[]?
var playAudioAction = GetOrFindBindFsm().GetFirstAction("Bind Start");
if (BindState == State.Normal) {
- PlaySound(playerObject, randomClipAction, playAudioAction);
+ AudioUtil.PlayAudio(playAudioAction, randomClipAction, playerObject);
var oneShotSingleAction = GetOrFindBindFsm().GetFirstAction("Check Grounded");
- PlaySound(playerObject, oneShotSingleAction);
+ AudioUtil.PlayAudio(oneShotSingleAction, playerObject);
}
@@ -278,7 +277,7 @@ private void PlayShamanCancel(GameObject playerObject, GameObject bindEffects) {
var silkPuffSpawner = GetOrFindBindFsm().GetFirstAction(cancelStateName);
var audio = GetOrFindBindFsm().GetFirstAction(cancelStateName);
- PlaySound(playerObject, audio);
+ AudioUtil.PlayAudio(audio, playerObject);
EffectUtils.SpawnGlobalPoolObject(silkPuffSpawner, playerObject.transform, 5f);
@@ -301,34 +300,34 @@ private void PlayShamanFallEnd(GameObject bindEffects) {
///
- public override byte[]? GetEffectInfo() {
- byte[] effectInfo = {
+ 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 .
+ /// 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.
///
protected PlayMakerFSM GetOrFindBindFsm() {
- if (BindFsm != null) {
- return BindFsm;
+ if (_bindFsm != null) {
+ return _bindFsm;
}
var heroFsms = HeroController.instance.GetComponents();
foreach (var heroFsm in heroFsms) {
if (heroFsm.FsmName == "Bind") {
- BindFsm = heroFsm;
- return BindFsm;
+ _bindFsm = heroFsm;
+ return _bindFsm;
}
}
@@ -342,8 +341,8 @@ protected PlayMakerFSM GetOrFindBindFsm() {
/// 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) {
+ _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;
@@ -371,7 +370,7 @@ protected bool CreateEffectIfNotExists(GameObject bindEffects, string objectName
return false;
}
- var localObj = LocalBindEffects!.FindGameObjectInChildren(objectName);
+ var localObj = _localBindEffects!.FindGameObjectInChildren(objectName);
if (localObj == null) {
Logger.Warn($"Could not find local {objectName} object, cannot play bind effect");
return false;
@@ -439,12 +438,12 @@ public enum State {
/// Effect flags sent by the other player. Mostly items they have equipped.
///
protected class Flags {
- public readonly bool BindBell = false;
- public readonly bool BaseMirror = false;
- public readonly bool UpgradedMirror = false;
- public readonly bool QuickBind = false;
- public readonly bool ReserveBind = false;
- public bool Maggoted = false; // Needs to be writable by BindBurst
+ 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;
diff --git a/SSMP/Animation/Effects/BindBurst.cs b/SSMP/Animation/Effects/BindBurst.cs
index 367c3de..193caa5 100644
--- a/SSMP/Animation/Effects/BindBurst.cs
+++ b/SSMP/Animation/Effects/BindBurst.cs
@@ -26,7 +26,7 @@ internal class BindBurst : Bind {
///
/// A set of players who recently bound while maggoted
///
- public static readonly HashSet MaggotedPlayers = new();
+ public static readonly HashSet MaggotedPlayers = [];
///
public override void Play(GameObject playerObject, CrestType crestType, byte[]? effectInfo) {
@@ -87,9 +87,7 @@ private void PlayMaggotCleanse(GameObject bindEffects, GameObject playerObject)
EffectUtils.SpawnGlobalPoolObject(maggotFlash, bindEffects.transform, 5f);
// Play audio
- if (maggotAudio != null) {
- PlaySound(playerObject, maggotAudio);
- }
+ AudioUtil.PlayAudio(maggotAudio, playerObject);
}
///
@@ -214,9 +212,7 @@ private void PlayWitchEnd(GameObject bindEffects) {
// Play tentacles audio
var audio = GetOrFindBindFsm().GetFirstAction("Witch Tentancles!");
- if (audio != null) {
- PlaySound(bindEffects.transform.parent.gameObject, audio);
- }
+ AudioUtil.PlayAudio(audio, bindEffects.transform.parent.gameObject);
witchBind.SetActive(false);
witchBind.SetActive(true);
@@ -274,8 +270,6 @@ private void PlayNormalEnd(GameObject bindEffects) {
// Play audio
var audio = GetOrFindBindFsm().GetFirstAction("Bind Burst");
- if (audio != null) {
- PlaySound(bindEffects.transform.parent.gameObject, audio);
- }
+ AudioUtil.PlayAudio(audio, bindEffects.transform.parent.gameObject);
}
}
diff --git a/SSMP/Animation/Effects/BindInterrupt.cs b/SSMP/Animation/Effects/BindInterrupt.cs
index d2b103b..fe8b979 100644
--- a/SSMP/Animation/Effects/BindInterrupt.cs
+++ b/SSMP/Animation/Effects/BindInterrupt.cs
@@ -52,7 +52,7 @@ private void PlayBindInterrupt(GameObject bindEffects) {
// Play audio
var audio = GetOrFindBindFsm().GetFirstAction(stateName);
- PlaySound(bindEffects.transform.parent.gameObject, audio);
+ AudioUtil.PlayAudio(audio, bindEffects.transform.parent.gameObject);
// Remove haze and camera controls
burst.DestroyGameObjectInChildren("haze2");
@@ -88,7 +88,7 @@ private void PlayBellExplode(GameObject bindEffects) {
// Play sound
var audio = bellFsm.GetFirstAction(stateName);
- PlaySound(bindBell, audio);
+ AudioUtil.PlayAudio(audio, bindBell);
// Remove camera control and haze
diff --git a/SSMP/Animation/Effects/DashSlash.cs b/SSMP/Animation/Effects/DashSlash.cs
index 8eac3f5..38ba946 100644
--- a/SSMP/Animation/Effects/DashSlash.cs
+++ b/SSMP/Animation/Effects/DashSlash.cs
@@ -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 57e0add..adf5511 100644
--- a/SSMP/Animation/Effects/DashSlashAntic.cs
+++ b/SSMP/Animation/Effects/DashSlashAntic.cs
@@ -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 72c4190..8795dc3 100644
--- a/SSMP/Animation/Effects/DashSlashReaper.cs
+++ b/SSMP/Animation/Effects/DashSlashReaper.cs
@@ -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
index 940899a..8abd61f 100644
--- a/SSMP/Animation/Effects/EffectUtils.cs
+++ b/SSMP/Animation/Effects/EffectUtils.cs
@@ -40,7 +40,7 @@ public static void SafelyRemoveAutoRecycle(GameObject obj) {
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.
diff --git a/SSMP/Animation/Effects/NeedleStrike.cs b/SSMP/Animation/Effects/NeedleStrike.cs
index e6e03f1..7dd5ca4 100644
--- a/SSMP/Animation/Effects/NeedleStrike.cs
+++ b/SSMP/Animation/Effects/NeedleStrike.cs
@@ -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/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
) {