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