Gameplay - 獨立游戲Celeste的Player源碼

TGA2018最佳獨立游戲《蔚藍》的一些公開代碼;

主要是Player部分的代碼:

using System;
using System.Collections;
using System.Collections.Generic;
using Monocle;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;namespace Celeste
{[Tracked]public class Player : Actor{#region Constantspublic static ParticleType P_DashA;public static ParticleType P_DashB;public static ParticleType P_CassetteFly;public static ParticleType P_Split;public static ParticleType P_SummitLandA;public static ParticleType P_SummitLandB;public static ParticleType P_SummitLandC;public const float MaxFall = 160f;private const float Gravity = 900f;private const float HalfGravThreshold = 40f;private const float FastMaxFall = 240f;private const float FastMaxAccel = 300f;public const float MaxRun = 90f;public const float RunAccel = 1000f;private const float RunReduce = 400f;private const float AirMult = .65f;private const float HoldingMaxRun = 70f;private const float HoldMinTime = .35f;private const float BounceAutoJumpTime = .1f;private const float DuckFriction = 500f;private const int DuckCorrectCheck = 4;private const float DuckCorrectSlide = 50f;private const float DodgeSlideSpeedMult = 1.2f;private const float DuckSuperJumpXMult = 1.25f;private const float DuckSuperJumpYMult = .5f;private const float JumpGraceTime = 0.1f;private const float JumpSpeed = -105f;private const float JumpHBoost = 40f;private const float VarJumpTime = .2f;private const float CeilingVarJumpGrace = .05f;private const int UpwardCornerCorrection = 4;private const float WallSpeedRetentionTime = .06f;private const int WallJumpCheckDist = 3;private const float WallJumpForceTime = .16f;private const float WallJumpHSpeed = MaxRun + JumpHBoost;public const float WallSlideStartMax = 20f;private const float WallSlideTime = 1.2f;private const float BounceVarJumpTime = .2f;private const float BounceSpeed = -140f;private const float SuperBounceVarJumpTime = .2f;private const float SuperBounceSpeed = -185f;private const float SuperJumpSpeed = JumpSpeed;private const float SuperJumpH = 260f;private const float SuperWallJumpSpeed = -160f;private const float SuperWallJumpVarTime = .25f;private const float SuperWallJumpForceTime = .2f;private const float SuperWallJumpH = MaxRun + JumpHBoost * 2;private const float DashSpeed = 240f;private const float EndDashSpeed = 160f;private const float EndDashUpMult = .75f;private const float DashTime = .15f;private const float DashCooldown = .2f;private const float DashRefillCooldown = .1f;private const int DashHJumpThruNudge = 6;private const int DashCornerCorrection = 4;private const int DashVFloorSnapDist = 3;private const float DashAttackTime = .3f;private const float BoostMoveSpeed = 80f;public const float BoostTime = .25f;private const float DuckWindMult = 0f;private const int WindWallDistance = 3;private const float ReboundSpeedX = 120f;private const float ReboundSpeedY = -120f;private const float ReboundVarJumpTime = .15f;private const float ReflectBoundSpeed = 220f;private const float DreamDashSpeed = DashSpeed;private const int DreamDashEndWiggle = 5;private const float DreamDashMinTime = .1f;public const float ClimbMaxStamina = 110;private const float ClimbUpCost = 100 / 2.2f;private const float ClimbStillCost = 100 / 10f;private const float ClimbJumpCost = 110 / 4f;private const int ClimbCheckDist = 2;private const int ClimbUpCheckDist = 2;private const float ClimbNoMoveTime = .1f;public const float ClimbTiredThreshold = 20f;private const float ClimbUpSpeed = -45f;private const float ClimbDownSpeed = 80f;private const float ClimbSlipSpeed = 30f;private const float ClimbAccel = 900f;private const float ClimbGrabYMult = .2f;private const float ClimbHopY = -120f;private const float ClimbHopX = 100f;private const float ClimbHopForceTime = .2f;private const float ClimbJumpBoostTime = .2f;private const float ClimbHopNoWindTime = .3f;private const float LaunchSpeed = 280f;private const float LaunchCancelThreshold = 220f;private const float LiftYCap = -130f;private const float LiftXCap = 250f;private const float JumpThruAssistSpeed = -40f;private const float InfiniteDashesTime = 2f;private const float InfiniteDashesFirstTime = .5f;private const float FlyPowerFlashTime = .5f;private const float ThrowRecoil = 80f;private static readonly Vector2 CarryOffsetTarget = new Vector2(0, -12);private const float ChaserStateMaxTime = 4f;public const float WalkSpeed = 64f;public const int StNormal = 0;public const int StClimb = 1;public const int StDash = 2;public const int StSwim = 3;public const int StBoost = 4;public const int StRedDash = 5;public const int StHitSquash = 6;public const int StLaunch = 7;public const int StPickup = 8;public const int StDreamDash = 9;public const int StSummitLaunch = 10;public const int StDummy = 11;public const int StIntroWalk = 12;public const int StIntroJump = 13;public const int StIntroRespawn = 14;public const int StIntroWakeUp = 15;public const int StBirdDashTutorial = 16;public const int StFrozen = 17;public const int StReflectionFall = 18;public const int StStarFly = 19;public const int StTempleFall = 20;public const int StCassetteFly = 21;public const int StAttract = 22;public const string TalkSfx = "player_talk";#endregion#region Varspublic Vector2 Speed;public Facings Facing;public PlayerSprite Sprite;public PlayerHair Hair;public StateMachine StateMachine;public Vector2 CameraAnchor;public bool CameraAnchorIgnoreX;public bool CameraAnchorIgnoreY;public Vector2 CameraAnchorLerp;public bool ForceCameraUpdate;public Leader Leader;public VertexLight Light;public int Dashes;public float Stamina = ClimbMaxStamina;public bool StrawberriesBlocked;public Vector2 PreviousPosition;public bool DummyAutoAnimate = true;public Vector2 ForceStrongWindHair;public Vector2? OverrideDashDirection;public bool FlipInReflection = false;public bool JustRespawned;  // True if the player hasn't moved since respawningpublic bool Dead { get; private set; }private Level level;private Collision onCollideH;private Collision onCollideV;private bool onGround;private bool wasOnGround;private int moveX;private bool flash;private bool wasDucking;private float idleTimer;private static Chooser<string> idleColdOptions = new Chooser<string>().Add("idleA", 5f).Add("idleB", 3f).Add("idleC", 1f);private static Chooser<string> idleNoBackpackOptions = new Chooser<string>().Add("idleA", 1f).Add("idleB", 3f).Add("idleC", 3f);private static Chooser<string> idleWarmOptions = new Chooser<string>().Add("idleA", 5f).Add("idleB", 3f);public int StrawberryCollectIndex = 0;public float StrawberryCollectResetTimer = 0f;private Hitbox hurtbox;private float jumpGraceTimer;public bool AutoJump;public float AutoJumpTimer;private float varJumpSpeed;private float varJumpTimer;private int forceMoveX;private float forceMoveXTimer;private int hopWaitX;   // If you climb hop onto a moving solid, snap to beside it until you get above itprivate float hopWaitXSpeed;private Vector2 lastAim;private float dashCooldownTimer;private float dashRefillCooldownTimer;public Vector2 DashDir;private float wallSlideTimer = WallSlideTime;private int wallSlideDir;private float climbNoMoveTimer;private Vector2 carryOffset;private Vector2 deadOffset;private float introEase;private float wallSpeedRetentionTimer; // If you hit a wall, start this timer. If coast is clear within this timer, retain h-speedprivate float wallSpeedRetained;private int wallBoostDir;private float wallBoostTimer;   // If you climb jump and then do a sideways input within this timer, switch to wall jumpprivate float maxFall;private float dashAttackTimer;private List<ChaserState> chaserStates;private bool wasTired;private HashSet<Trigger> triggersInside;private float highestAirY;private bool dashStartedOnGround;private bool fastJump;private int lastClimbMove;private float noWindTimer;private float dreamDashCanEndTimer;private Solid climbHopSolid;private Vector2 climbHopSolidPosition;private SoundSource wallSlideSfx;private SoundSource swimSurfaceLoopSfx;private float playFootstepOnLand;private float minHoldTimer;public Booster CurrentBooster;private Booster lastBooster;private bool calledDashEvents;private int lastDashes;private Sprite sweatSprite;private int startHairCount;private bool launched;private float launchedTimer;private float dashTrailTimer;private List<ChaserStateSound> activeSounds = new List<ChaserStateSound>();private FMOD.Studio.EventInstance idleSfx;private readonly Hitbox normalHitbox = new Hitbox(8, 11, -4, -11);private readonly Hitbox duckHitbox = new Hitbox(8, 6, -4, -6);private readonly Hitbox normalHurtbox = new Hitbox(8, 9, -4, -11);private readonly Hitbox duckHurtbox = new Hitbox(8, 4, -4, -6);private readonly Hitbox starFlyHitbox = new Hitbox(8, 8, -4, -10);private readonly Hitbox starFlyHurtbox = new Hitbox(6, 6, -3, -9);private Vector2 normalLightOffset = new Vector2(0, -8);private Vector2 duckingLightOffset = new Vector2(0, -3);private List<Entity> temp = new List<Entity>();// hairpublic static readonly Color NormalHairColor = Calc.HexToColor("AC3232");public static readonly Color FlyPowerHairColor = Calc.HexToColor("F2EB6D");public static readonly Color UsedHairColor = Calc.HexToColor("44B7FF");public static readonly Color FlashHairColor = Color.White;public static readonly Color TwoDashesHairColor = Calc.HexToColor("ff6def");private float hairFlashTimer;public Color? OverrideHairColor;private Vector2 windDirection;private float windTimeout;private float windHairTimer;// level-start intropublic enum IntroTypes { Transition, Respawn, WalkInRight, WalkInLeft, Jump, WakeUp, Fall, TempleMirrorVoid, None }public IntroTypes IntroType;private MirrorReflection reflection;#endregion#region constructor / added / removedpublic Player(Vector2 position, PlayerSpriteMode spriteMode): base(new Vector2((int)position.X, (int)position.Y)){Depth = Depths.Player;Tag = Tags.Persistent;// spriteSprite = new PlayerSprite(spriteMode);Add(Hair = new PlayerHair(Sprite));Add(Sprite);Hair.Color = NormalHairColor;startHairCount = Sprite.HairCount;// sweat spritesweatSprite = GFX.SpriteBank.Create("player_sweat");Add(sweatSprite);// physicsCollider = normalHitbox;hurtbox = normalHurtbox;onCollideH = OnCollideH;onCollideV = OnCollideV;// statesStateMachine = new StateMachine(23);StateMachine.SetCallbacks(StNormal, NormalUpdate, null, NormalBegin, NormalEnd);StateMachine.SetCallbacks(StClimb, ClimbUpdate, null, ClimbBegin, ClimbEnd);StateMachine.SetCallbacks(StDash, DashUpdate, DashCoroutine, DashBegin, DashEnd);StateMachine.SetCallbacks(StSwim, SwimUpdate, null, SwimBegin, null);StateMachine.SetCallbacks(StBoost, BoostUpdate, BoostCoroutine, BoostBegin, BoostEnd);StateMachine.SetCallbacks(StRedDash, RedDashUpdate, RedDashCoroutine, RedDashBegin, RedDashEnd);StateMachine.SetCallbacks(StHitSquash, HitSquashUpdate, null, HitSquashBegin, null);StateMachine.SetCallbacks(StLaunch, LaunchUpdate, null, LaunchBegin, null);StateMachine.SetCallbacks(StPickup, null, PickupCoroutine, null, null);StateMachine.SetCallbacks(StDreamDash, DreamDashUpdate, null, DreamDashBegin, DreamDashEnd);StateMachine.SetCallbacks(StSummitLaunch, SummitLaunchUpdate, null, SummitLaunchBegin, null);StateMachine.SetCallbacks(StDummy, DummyUpdate, null, DummyBegin, null);StateMachine.SetCallbacks(StIntroWalk, null, IntroWalkCoroutine, null, null);StateMachine.SetCallbacks(StIntroJump, null, IntroJumpCoroutine, null, null);StateMachine.SetCallbacks(StIntroRespawn, null, null, IntroRespawnBegin, IntroRespawnEnd);StateMachine.SetCallbacks(StIntroWakeUp, null, IntroWakeUpCoroutine, null, null);StateMachine.SetCallbacks(StTempleFall, TempleFallUpdate, TempleFallCoroutine);StateMachine.SetCallbacks(StReflectionFall, ReflectionFallUpdate, ReflectionFallCoroutine, ReflectionFallBegin, ReflectionFallEnd);StateMachine.SetCallbacks(StBirdDashTutorial, BirdDashTutorialUpdate, BirdDashTutorialCoroutine, BirdDashTutorialBegin, null);StateMachine.SetCallbacks(StFrozen, FrozenUpdate, null, null, null);StateMachine.SetCallbacks(StStarFly, StarFlyUpdate, StarFlyCoroutine, StarFlyBegin, StarFlyEnd);StateMachine.SetCallbacks(StCassetteFly, CassetteFlyUpdate, CassetteFlyCoroutine, CassetteFlyBegin, CassetteFlyEnd);StateMachine.SetCallbacks(StAttract, AttractUpdate, null, AttractBegin, AttractEnd);Add(StateMachine);// other stuffAdd(Leader = new Leader(new Vector2(0, -8)));lastAim = Vector2.UnitX;Facing = Facings.Right;chaserStates = new List<ChaserState>();triggersInside = new HashSet<Trigger>();Add(Light = new VertexLight(normalLightOffset, Color.White, 1f, 32, 64));Add(new WaterInteraction(() => { return StateMachine.State == StDash || StateMachine.State == StReflectionFall; }));//WindAdd(new WindMover(WindMove));Add(wallSlideSfx = new SoundSource());Add(swimSurfaceLoopSfx = new SoundSource());Sprite.OnFrameChange = (anim) =>{if (Scene != null && !Dead){// footstepsvar frame = Sprite.CurrentAnimationFrame;if ((anim.Equals(PlayerSprite.RunCarry) && (frame == 0 || frame == 6)) ||(anim.Equals(PlayerSprite.RunFast) && (frame == 0 || frame == 6)) ||(anim.Equals(PlayerSprite.RunSlow) && (frame == 0 || frame == 6)) ||(anim.Equals(PlayerSprite.Walk) && (frame == 0 || frame == 6)) ||(anim.Equals(PlayerSprite.RunStumble) && frame == 6) ||(anim.Equals(PlayerSprite.Flip) && frame == 4) ||(anim.Equals(PlayerSprite.RunWind) && (frame == 0 || frame == 6)) ||(anim.Equals("idleC") && Sprite.Mode == PlayerSpriteMode.MadelineNoBackpack && (frame == 3 || frame == 6 || frame == 8 || frame == 11)) ||(anim.Equals("carryTheoWalk") && (frame == 0 || frame == 6))){var landed = SurfaceIndex.GetPlatformByPriority(CollideAll<Platform>(Position + Vector2.UnitY, temp));if (landed != null)Play(Sfxs.char_mad_footstep, SurfaceIndex.Param, landed.GetStepSoundIndex(this));}// climbing (holds)else if ((anim.Equals(PlayerSprite.ClimbUp) && (frame == 5)) ||(anim.Equals(PlayerSprite.ClimbDown) && (frame == 5))){var holding = SurfaceIndex.GetPlatformByPriority(CollideAll<Solid>(Center + Vector2.UnitX * (int)Facing, temp));if (holding != null)Play(Sfxs.char_mad_handhold, SurfaceIndex.Param, holding.GetWallSoundIndex(this, (int)Facing));}else if (anim.Equals("wakeUp") && frame == 19)Play(Sfxs.char_mad_campfire_stand);else if (anim.Equals("sitDown") && frame == 12)Play(Sfxs.char_mad_summit_sit);else if (anim.Equals("push") && (frame == 8 || frame == 15))Dust.BurstFG(Position + new Vector2(-(int)Facing * 5, -1), new Vector2(-(int)Facing, -0.5f).Angle(), 1, 0);}};Sprite.OnLastFrame = (anim) =>{if (Scene != null && !Dead && Sprite.CurrentAnimationID == "idle" && !level.InCutscene && idleTimer > 3f){if (Calc.Random.Chance(0.2f)){var next = "";if (Sprite.Mode == PlayerSpriteMode.Madeline)next = (level.CoreMode == Session.CoreModes.Hot ? idleWarmOptions : idleColdOptions).Choose();elsenext = idleNoBackpackOptions.Choose();if (!string.IsNullOrEmpty(next)){Sprite.Play(next);if (Sprite.Mode == PlayerSpriteMode.Madeline){if (next == "idleB")idleSfx = Play(Sfxs.char_mad_idle_scratch);else if (next == "idleC")idleSfx = Play(Sfxs.char_mad_idle_sneeze);}else if (next == "idleA")idleSfx = Play(Sfxs.char_mad_idle_crackknuckles);}}}};// cancel special idle sounds if the anim changedSprite.OnChange = (last, next) =>{if ((last == "idleB" || last == "idleC") && next != null && !next.StartsWith("idle") && idleSfx != null)Audio.Stop(idleSfx);};Add(reflection = new MirrorReflection());}public override void Added(Scene scene){base.Added(scene);level = SceneAs<Level>();lastDashes = Dashes = MaxDashes;if (X > level.Bounds.Center.X && IntroType != IntroTypes.None)Facing = Facings.Left;switch (IntroType){case IntroTypes.Respawn:StateMachine.State = StIntroRespawn;JustRespawned = true;break;case IntroTypes.WalkInRight:IntroWalkDirection = Facings.Right;StateMachine.State = StIntroWalk;break;case IntroTypes.WalkInLeft:IntroWalkDirection = Facings.Left;StateMachine.State = StIntroWalk;break;case IntroTypes.Jump:StateMachine.State = StIntroJump;break;case IntroTypes.WakeUp:Sprite.Play("asleep");Facing = Facings.Right;StateMachine.State = StIntroWakeUp;break;case IntroTypes.None:StateMachine.State = StNormal;break;case IntroTypes.Fall:StateMachine.State = StReflectionFall;break;case IntroTypes.TempleMirrorVoid:StartTempleMirrorVoidSleep();break;}IntroType = IntroTypes.Transition;StartHair();PreviousPosition = Position;}public void StartTempleMirrorVoidSleep(){Sprite.Play("asleep");Facing = Facings.Right;StateMachine.State = StDummy;StateMachine.Locked = true;DummyAutoAnimate = false;DummyGravity = false;}public override void Removed(Scene scene){base.Removed(scene);level = null;Audio.Stop(conveyorLoopSfx);foreach (var trigger in triggersInside){trigger.Triggered = false;trigger.OnLeave(this);}}public override void SceneEnd(Scene scene){base.SceneEnd(scene);Audio.Stop(conveyorLoopSfx);}#endregion#region Renderingpublic override void Render(){var was = Sprite.RenderPosition;Sprite.RenderPosition = Sprite.RenderPosition.Floor();if (StateMachine.State == StIntroRespawn){DeathEffect.Draw(Center + deadOffset, Hair.Color, introEase);}else{if (StateMachine.State != StStarFly){if (IsTired && flash)Sprite.Color = Color.Red;elseSprite.Color = Color.White;}// set up scaleif (reflection.IsRendering && FlipInReflection){Facing = (Facings)(-(int)Facing);Hair.Facing = Facing;}Sprite.Scale.X *= (int)Facing;// sweat scaleif (sweatSprite.LastAnimationID == "idle")sweatSprite.Scale = Sprite.Scale;else{sweatSprite.Scale.Y = Sprite.Scale.Y;sweatSprite.Scale.X = Math.Abs(Sprite.Scale.X) * Math.Sign(sweatSprite.Scale.X);}// drawbase.Render();// star fly transformif (Sprite.CurrentAnimationID == PlayerSprite.StartStarFly){var p = (Sprite.CurrentAnimationFrame / (float)Sprite.CurrentAnimationTotalFrames);var white = GFX.Game.GetAtlasSubtexturesAt("characters/player/startStarFlyWhite", Sprite.CurrentAnimationFrame);white.Draw(Sprite.RenderPosition, Sprite.Origin, starFlyColor * p, Sprite.Scale, Sprite.Rotation, 0);}// revert scaleSprite.Scale.X *= (int)Facing;if (reflection.IsRendering && FlipInReflection){Facing = (Facings)(-(int)Facing);Hair.Facing = Facing;}}Sprite.RenderPosition = was;}public override void DebugRender(Camera camera){base.DebugRender(camera);{Collider was = Collider;Collider = hurtbox;Draw.HollowRect(Collider, Color.Lime);Collider = was;}}#endregion#region Updatingpublic override void Update(){//Infinite Stamina variantif (SaveData.Instance.AssistMode && SaveData.Instance.Assists.InfiniteStamina)Stamina = ClimbMaxStamina;PreviousPosition = Position;//Vars       {// strawb reset timerStrawberryCollectResetTimer -= Engine.DeltaTime;if (StrawberryCollectResetTimer <= 0)StrawberryCollectIndex = 0;// idle timeridleTimer += Engine.DeltaTime;if (level != null && level.InCutscene)idleTimer = -5;else if (Speed.X != 0 || Speed.Y != 0)idleTimer = 0;//Underwater musicif (!Dead)Audio.MusicUnderwater = UnderwaterMusicCheck();//Just respawnedif (JustRespawned && Speed != Vector2.Zero)JustRespawned = false;//Get groundif (StateMachine.State == StDreamDash)onGround = OnSafeGround = false;else if (Speed.Y >= 0){Platform first = CollideFirst<Solid>(Position + Vector2.UnitY);if (first == null)first = CollideFirstOutside<JumpThru>(Position + Vector2.UnitY);if (first != null){onGround = true;OnSafeGround = first.Safe;}elseonGround = OnSafeGround = false;}elseonGround = OnSafeGround = false;if (StateMachine.State == StSwim)OnSafeGround = true;//Safe Ground Blocked?if (OnSafeGround){foreach (SafeGroundBlocker blocker in Scene.Tracker.GetComponents<SafeGroundBlocker>()){if (blocker.Check(this)){OnSafeGround = false;break;}}}playFootstepOnLand -= Engine.DeltaTime;//Highest Air Yif (onGround)highestAirY = Y;elsehighestAirY = Math.Min(Y, highestAirY);//Flashingif (Scene.OnInterval(.05f))flash = !flash;//Wall Slideif (wallSlideDir != 0){wallSlideTimer = Math.Max(wallSlideTimer - Engine.DeltaTime, 0);wallSlideDir = 0;}//Wall Boostif (wallBoostTimer > 0){wallBoostTimer -= Engine.DeltaTime;if (moveX == wallBoostDir){Speed.X = WallJumpHSpeed * moveX;Stamina += ClimbJumpCost;wallBoostTimer = 0;sweatSprite.Play("idle");                       }}//After Dashif (onGround && StateMachine.State != StClimb){AutoJump = false;Stamina = ClimbMaxStamina;wallSlideTimer = WallSlideTime;}//Dash Attackif (dashAttackTimer > 0)dashAttackTimer -= Engine.DeltaTime;//Jump Graceif (onGround){dreamJump = false;jumpGraceTimer = JumpGraceTime;}else if (jumpGraceTimer > 0)jumpGraceTimer -= Engine.DeltaTime;//Dashes{if (dashCooldownTimer > 0)dashCooldownTimer -= Engine.DeltaTime;if (dashRefillCooldownTimer > 0)dashRefillCooldownTimer -= Engine.DeltaTime;else if (SaveData.Instance.AssistMode && SaveData.Instance.Assists.DashMode == Assists.DashModes.Infinite && !level.InCutscene)RefillDash();else if (!Inventory.NoRefills){if (StateMachine.State == StSwim)RefillDash();else if (onGround)if (CollideCheck<Solid, NegaBlock>(Position + Vector2.UnitY) || CollideCheckOutside<JumpThru>(Position + Vector2.UnitY))if (!CollideCheck<Spikes>(Position) || (SaveData.Instance.AssistMode && SaveData.Instance.Assists.Invincible))RefillDash();}}//Var Jumpif (varJumpTimer > 0)varJumpTimer -= Engine.DeltaTime;//Auto Jumpif (AutoJumpTimer > 0){if (AutoJump){AutoJumpTimer -= Engine.DeltaTime;if (AutoJumpTimer <= 0)AutoJump = false;}elseAutoJumpTimer = 0;}//Force Move Xif (forceMoveXTimer > 0){forceMoveXTimer -= Engine.DeltaTime;moveX = forceMoveX;}else{moveX = Input.MoveX.Value;climbHopSolid = null;}//Climb Hop Solid Movementif (climbHopSolid != null && !climbHopSolid.Collidable)climbHopSolid = null;else if (climbHopSolid != null && climbHopSolid.Position != climbHopSolidPosition){var move = climbHopSolid.Position - climbHopSolidPosition;climbHopSolidPosition = climbHopSolid.Position;MoveHExact((int)move.X);MoveVExact((int)move.Y);}//Windif (noWindTimer > 0)noWindTimer -= Engine.DeltaTime;//Facingif (moveX != 0 && InControl&& StateMachine.State != StClimb && StateMachine.State != StPickup && StateMachine.State != StRedDash && StateMachine.State != StHitSquash){var to = (Facings)moveX;if (to != Facing && Ducking)Sprite.Scale = new Vector2(0.8f, 1.2f);Facing = to;}//AiminglastAim = Input.GetAimVector(Facing);//Wall Speed Retentionif (wallSpeedRetentionTimer > 0){if (Math.Sign(Speed.X) == -Math.Sign(wallSpeedRetained))wallSpeedRetentionTimer = 0;else if (!CollideCheck<Solid>(Position + Vector2.UnitX * Math.Sign(wallSpeedRetained))){Speed.X = wallSpeedRetained;wallSpeedRetentionTimer = 0;}elsewallSpeedRetentionTimer -= Engine.DeltaTime;}//Hop Wait Xif (hopWaitX != 0){if (Math.Sign(Speed.X) == -hopWaitX || Speed.Y > 0)hopWaitX = 0;else if (!CollideCheck<Solid>(Position + Vector2.UnitX * hopWaitX)){Speed.X = hopWaitXSpeed;hopWaitX = 0;}}// Wind Timeoutif (windTimeout > 0)windTimeout -= Engine.DeltaTime;// Hair{var windDir = windDirection;if (ForceStrongWindHair.Length() > 0)windDir = ForceStrongWindHair;if (windTimeout > 0 && windDir.X != 0){windHairTimer += Engine.DeltaTime * 8f;Hair.StepPerSegment = new Vector2(windDir.X * 5f, (float)Math.Sin(windHairTimer));Hair.StepInFacingPerSegment = 0f;Hair.StepApproach = 128f;Hair.StepYSinePerSegment = 0;}else if (Dashes > 1){Hair.StepPerSegment = new Vector2((float)Math.Sin(Scene.TimeActive * 2) * 0.7f - (int)Facing * 3, (float)Math.Sin(Scene.TimeActive * 1f));Hair.StepInFacingPerSegment = 0f;Hair.StepApproach = 90f;Hair.StepYSinePerSegment = 1f;Hair.StepPerSegment.Y += windDir.Y * 2f;}else{Hair.StepPerSegment = new Vector2(0, 2f);Hair.StepInFacingPerSegment = 0.5f;Hair.StepApproach = 64f;Hair.StepYSinePerSegment = 0;Hair.StepPerSegment.Y += windDir.Y * 0.5f;}}if (StateMachine.State == StRedDash)Sprite.HairCount = 1;else if (StateMachine.State != StStarFly)Sprite.HairCount = (Dashes > 1 ? 5 : startHairCount);//Min Hold Timeif (minHoldTimer > 0)minHoldTimer -= Engine.DeltaTime;//Launch Particlesif (launched){var sq = Speed.LengthSquared();if (sq < LaunchedMinSpeedSq)launched = false;else{var was = launchedTimer;launchedTimer += Engine.DeltaTime;if (launchedTimer >= .5f){launched = false;launchedTimer = 0;}else if (Calc.OnInterval(launchedTimer, was, .15f))level.Add(Engine.Pooler.Create<SpeedRing>().Init(Center, Speed.Angle(), Color.White));}}elselaunchedTimer = 0;}if (IsTired){Input.Rumble(RumbleStrength.Light, RumbleLength.Short);if (!wasTired){wasTired = true;}}elsewasTired = false;base.Update();//Light Offsetif (Ducking)Light.Position = duckingLightOffset;elseLight.Position = normalLightOffset;//Jump Thru Assistif (!onGround && Speed.Y <= 0 && (StateMachine.State != StClimb || lastClimbMove == -1) && CollideCheck<JumpThru>() && !JumpThruBoostBlockedCheck())MoveV(JumpThruAssistSpeed * Engine.DeltaTime);//Dash Floor Snappingif (!onGround && DashAttacking && DashDir.Y == 0){if (CollideCheck<Solid>(Position + Vector2.UnitY * DashVFloorSnapDist) || CollideCheckOutside<JumpThru>(Position + Vector2.UnitY * DashVFloorSnapDist))MoveVExact(DashVFloorSnapDist);}//Falling unduckingif (Speed.Y > 0 && CanUnDuck && Collider != starFlyHitbox && !onGround)Ducking = false;//Physicsif (StateMachine.State != StDreamDash && StateMachine.State != StAttract)MoveH(Speed.X * Engine.DeltaTime, onCollideH);if (StateMachine.State != StDreamDash && StateMachine.State != StAttract)MoveV(Speed.Y * Engine.DeltaTime, onCollideV);//Swimmingif (StateMachine.State == StSwim){//Stay at water surfaceif (Speed.Y < 0 && Speed.Y >= SwimMaxRise){while (!SwimCheck()){Speed.Y = 0;if (MoveVExact(1))break;}}}else if (StateMachine.State == StNormal && SwimCheck())StateMachine.State = StSwim;else if (StateMachine.State == StClimb && SwimCheck()){var water = CollideFirst<Water>(Position);if (water != null && Center.Y < water.Center.Y){while (SwimCheck())if (MoveVExact(-1))break;if (SwimCheck())StateMachine.State = StSwim;}elseStateMachine.State = StSwim;}// wall slide SFX{var isSliding = Sprite.CurrentAnimationID != null && Sprite.CurrentAnimationID.Equals(PlayerSprite.WallSlide) && Speed.Y > 0;if (isSliding){if (!wallSlideSfx.Playing)Loop(wallSlideSfx, Sfxs.char_mad_wallslide);var platform = SurfaceIndex.GetPlatformByPriority(CollideAll<Solid>(Center + Vector2.UnitX * (int)Facing, temp));if (platform != null)wallSlideSfx.Param(SurfaceIndex.Param, platform.GetWallSoundIndex(this, (int)Facing));}elseStop(wallSlideSfx);}// update spriteUpdateSprite();//Carry held itemUpdateCarry();//Triggersif (StateMachine.State != StReflectionFall){foreach (Trigger trigger in Scene.Tracker.GetEntities<Trigger>()){if (CollideCheck(trigger)){if (!trigger.Triggered){trigger.Triggered = true;triggersInside.Add(trigger);trigger.OnEnter(this);}trigger.OnStay(this);}else if (trigger.Triggered){triggersInside.Remove(trigger);trigger.Triggered = false;trigger.OnLeave(this);}}}//Strawberry BlockStrawberriesBlocked = CollideCheck<BlockField>();// Camera (lerp by distance using delta-time)if (InControl || ForceCameraUpdate){if (StateMachine.State == StReflectionFall){level.Camera.Position = CameraTarget;}else{var from = level.Camera.Position;var target = CameraTarget;var multiplier = StateMachine.State == StTempleFall ? 8 : 1f;level.Camera.Position = from + (target - from) * (1f - (float)Math.Pow(0.01f / multiplier, Engine.DeltaTime));}}//Player Collidersif (!Dead && StateMachine.State != StCassetteFly){Collider was = Collider;Collider = hurtbox;foreach (PlayerCollider pc in Scene.Tracker.GetComponents<PlayerCollider>()){if (pc.Check(this) && Dead){Collider = was;return;}}// If the current collider is not the hurtbox we set it to, that means a collision callback changed it. Keep the new one!bool keepNew = (Collider != hurtbox);if (!keepNew)    Collider = was;}//Boundsif (InControl && !Dead && StateMachine.State != StDreamDash)level.EnforceBounds(this);UpdateChaserStates();UpdateHair(true);//Sounds on ducking state changeif (wasDucking != Ducking){wasDucking = Ducking;if (wasDucking)Play(Sfxs.char_mad_duck);else if (onGround)Play(Sfxs.char_mad_stand);}// shallow swim sfxif (Speed.X != 0 && ((StateMachine.State == StSwim && !SwimUnderwaterCheck()) || (StateMachine.State == StNormal && CollideCheck<Water>(Position)))){if (!swimSurfaceLoopSfx.Playing)swimSurfaceLoopSfx.Play(Sfxs.char_mad_water_move_shallow);}elseswimSurfaceLoopSfx.Stop();wasOnGround = onGround;}private void CreateTrail(){TrailManager.Add(this, wasDashB ? NormalHairColor : UsedHairColor);}public void CleanUpTriggers(){if (triggersInside.Count > 0){foreach (var trigger in triggersInside){trigger.OnLeave(this);trigger.Triggered = false;}triggersInside.Clear();}}private void UpdateChaserStates(){while (chaserStates.Count > 0 && Scene.TimeActive - chaserStates[0].TimeStamp > ChaserStateMaxTime)chaserStates.RemoveAt(0);chaserStates.Add(new ChaserState(this));activeSounds.Clear();}#endregion#region Hair & Spriteprivate void StartHair(){Hair.Facing = Facing;Hair.Start();UpdateHair(true);}public void UpdateHair(bool applyGravity){// colorif (StateMachine.State == StStarFly){Hair.Color = Sprite.Color;applyGravity = false;}else if (Dashes == 0 && Dashes < MaxDashes)Hair.Color = Color.Lerp(Hair.Color, UsedHairColor, 6f * Engine.DeltaTime);else{Color color;if (lastDashes != Dashes){color = FlashHairColor;hairFlashTimer = .12f;}else if (hairFlashTimer > 0){color = FlashHairColor;hairFlashTimer -= Engine.DeltaTime;}else if (Dashes == 2)color = TwoDashesHairColor;elsecolor = NormalHairColor;Hair.Color = color;}if (OverrideHairColor != null)Hair.Color = OverrideHairColor.Value;Hair.Facing = Facing;Hair.SimulateMotion = applyGravity;lastDashes = Dashes;}private void UpdateSprite(){//TweenSprite.Scale.X = Calc.Approach(Sprite.Scale.X, 1f, 1.75f * Engine.DeltaTime);Sprite.Scale.Y = Calc.Approach(Sprite.Scale.Y, 1f, 1.75f * Engine.DeltaTime);//Animationif (InControl && Sprite.CurrentAnimationID != PlayerSprite.Throw && StateMachine.State != StTempleFall && StateMachine.State != StReflectionFall && StateMachine.State != StStarFly && StateMachine.State != StCassetteFly){if (StateMachine.State == StAttract){Sprite.Play(PlayerSprite.FallFast);}else if (StateMachine.State == StSummitLaunch){Sprite.Play(PlayerSprite.Launch);}// picking upelse if (StateMachine.State == StPickup){Sprite.Play(PlayerSprite.PickUp);}// swimingelse if (StateMachine.State == StSwim){if (Input.MoveY.Value > 0)Sprite.Play(PlayerSprite.SwimDown);else if (Input.MoveY.Value < 0)Sprite.Play(PlayerSprite.SwimUp);elseSprite.Play(PlayerSprite.SwimIdle);}// dream dashingelse if (StateMachine.State == StDreamDash){if (Sprite.CurrentAnimationID != PlayerSprite.DreamDashIn && Sprite.CurrentAnimationID != PlayerSprite.DreamDashLoop)Sprite.Play(PlayerSprite.DreamDashIn);}else if (Sprite.DreamDashing && Sprite.LastAnimationID != PlayerSprite.DreamDashOut){Sprite.Play(PlayerSprite.DreamDashOut);}else if (Sprite.CurrentAnimationID != PlayerSprite.DreamDashOut){// during dashif (DashAttacking){if (onGround && DashDir.Y == 0 && !Ducking && Speed.X != 0 && moveX == -Math.Sign(Speed.X)){if (Scene.OnInterval(.02f))Dust.Burst(Position, Calc.Up, 1);Sprite.Play(PlayerSprite.Skid);}elseSprite.Play(PlayerSprite.Dash);}// climbingelse if (StateMachine.State == StClimb){if (lastClimbMove < 0)Sprite.Play(PlayerSprite.ClimbUp);else if (lastClimbMove > 0)Sprite.Play(PlayerSprite.WallSlide);else if (!CollideCheck<Solid>(Position + new Vector2((int)Facing, 6)))Sprite.Play(PlayerSprite.Dangling);else if (Input.MoveX == -(int)Facing){if (Sprite.CurrentAnimationID != PlayerSprite.ClimbLookBack)Sprite.Play(PlayerSprite.ClimbLookBackStart);}elseSprite.Play(PlayerSprite.WallSlide);}// duckingelse if (Ducking && StateMachine.State == StNormal){Sprite.Play(PlayerSprite.Duck);}else if (onGround){fastJump = false;if (Holding == null && moveX != 0 && CollideCheck<Solid>(Position + Vector2.UnitX * moveX)){Sprite.Play("push");}else if (Math.Abs(Speed.X) <= RunAccel / 40f && moveX == 0){if (Holding != null){Sprite.Play(PlayerSprite.IdleCarry);}else if (!Scene.CollideCheck<Solid>(Position + new Vector2((int)Facing * 1, 2)) && !Scene.CollideCheck<Solid>(Position + new Vector2((int)Facing * 4, 2)) && !CollideCheck<JumpThru>(Position + new Vector2((int)Facing * 4, 2))){Sprite.Play(PlayerSprite.FrontEdge);}else if (!Scene.CollideCheck<Solid>(Position + new Vector2(-(int)Facing * 1, 2)) && !Scene.CollideCheck<Solid>(Position + new Vector2(-(int)Facing * 4, 2)) && !CollideCheck<JumpThru>(Position + new Vector2(-(int)Facing * 4, 2))){Sprite.Play("edgeBack");}else if (Input.MoveY.Value == -1){if (Sprite.LastAnimationID != PlayerSprite.LookUp)Sprite.Play(PlayerSprite.LookUp);}else{if (Sprite.CurrentAnimationID != null && !Sprite.CurrentAnimationID.Contains("idle"))Sprite.Play(PlayerSprite.Idle);}}else if (Holding != null){Sprite.Play(PlayerSprite.RunCarry);}else if (Math.Sign(Speed.X) == -moveX && moveX != 0){if (Math.Abs(Speed.X) > MaxRun)Sprite.Play(PlayerSprite.Skid);else if (Sprite.CurrentAnimationID != PlayerSprite.Skid)Sprite.Play(PlayerSprite.Flip);}else if (windDirection.X != 0 && windTimeout > 0f && (int)Facing == -Math.Sign(windDirection.X)){Sprite.Play(PlayerSprite.RunWind);}else if (!Sprite.Running){if (Math.Abs(Speed.X) < MaxRun * .5f)Sprite.Play(PlayerSprite.RunSlow);elseSprite.Play(PlayerSprite.RunFast);}}// wall slidingelse if (wallSlideDir != 0 && Holding == null){Sprite.Play(PlayerSprite.WallSlide);}// jumping upelse if (Speed.Y < 0){if (Holding != null){Sprite.Play(PlayerSprite.JumpCarry);}else if (fastJump || Math.Abs(Speed.X) > MaxRun){fastJump = true;Sprite.Play(PlayerSprite.JumpFast);}elseSprite.Play(PlayerSprite.JumpSlow);}// falling downelse{if (Holding != null){Sprite.Play(PlayerSprite.FallCarry);}else if (fastJump || Speed.Y >= MaxFall || level.InSpace){fastJump = true;if (Sprite.LastAnimationID != PlayerSprite.FallFast)Sprite.Play(PlayerSprite.FallFast);}elseSprite.Play(PlayerSprite.FallSlow);}}}if (StateMachine.State != Player.StDummy){if (level.InSpace)Sprite.Rate = .5f;elseSprite.Rate = 1f;}}public void CreateSplitParticles(){level.Particles.Emit(P_Split, 16, Center, Vector2.One * 6);}#endregion#region Getterspublic Vector2 CameraTarget{get{Vector2 at = new Vector2();Vector2 target = new Vector2(X - Celeste.GameWidth / 2, Y - Celeste.GameHeight / 2);if (StateMachine.State != StReflectionFall)target += new Vector2(level.CameraOffset.X, level.CameraOffset.Y);if (StateMachine.State == StStarFly){target.X += .2f * Speed.X;target.Y += .2f * Speed.Y;}else if (StateMachine.State == StRedDash){target.X += 48 * Math.Sign(Speed.X);target.Y += 48 * Math.Sign(Speed.Y);}else if (StateMachine.State == StSummitLaunch){target.Y -= 64;}else if (StateMachine.State == StReflectionFall){target.Y += 32;}if (CameraAnchorLerp.Length() > 0){if (CameraAnchorIgnoreX && !CameraAnchorIgnoreY)target.Y = MathHelper.Lerp(target.Y, CameraAnchor.Y, CameraAnchorLerp.Y);else if (!CameraAnchorIgnoreX && CameraAnchorIgnoreY)target.X = MathHelper.Lerp(target.X, CameraAnchor.X, CameraAnchorLerp.X);else if (CameraAnchorLerp.X == CameraAnchorLerp.Y)target = Vector2.Lerp(target, CameraAnchor, CameraAnchorLerp.X);else{target.X = MathHelper.Lerp(target.X, CameraAnchor.X, CameraAnchorLerp.X);target.Y = MathHelper.Lerp(target.Y, CameraAnchor.Y, CameraAnchorLerp.Y);}}at.X = MathHelper.Clamp(target.X, level.Bounds.Left, level.Bounds.Right - Celeste.GameWidth);at.Y = MathHelper.Clamp(target.Y, level.Bounds.Top, level.Bounds.Bottom - Celeste.GameHeight);if (level.CameraLockMode != Level.CameraLockModes.None){var locker = Scene.Tracker.GetComponent<CameraLocker>();//X Snappingif (level.CameraLockMode != Level.CameraLockModes.BoostSequence){at.X = Math.Max(at.X, level.Camera.X);if (locker != null)at.X = Math.Min(at.X, Math.Max(level.Bounds.Left, locker.Entity.X - locker.MaxXOffset));}//Y Snappingif (level.CameraLockMode == Level.CameraLockModes.FinalBoss){at.Y = Math.Max(at.Y, level.Camera.Y);if (locker != null)at.Y = Math.Min(at.Y, Math.Max(level.Bounds.Top, locker.Entity.Y - locker.MaxYOffset));}else if (level.CameraLockMode == Level.CameraLockModes.BoostSequence){level.CameraUpwardMaxY = Math.Min(level.Camera.Y + CameraLocker.UpwardMaxYOffset, level.CameraUpwardMaxY);at.Y = Math.Min(at.Y, level.CameraUpwardMaxY);if (locker != null)at.Y = Math.Max(at.Y, Math.Min(level.Bounds.Bottom - 180, locker.Entity.Y - locker.MaxYOffset));}}// snap above killboxesvar killboxes = Scene.Tracker.GetEntities<Killbox>();foreach (var box in killboxes){if (!box.Collidable)continue;if (Top < box.Bottom && Right > box.Left && Left < box.Right)at.Y = Math.Min(at.Y, box.Top - 180);}return at;}}public bool GetChasePosition(float sceneTime, float timeAgo, out ChaserState chaseState){if (!Dead){bool tooLongAgoFound = false;foreach (var state in chaserStates){float time = sceneTime - state.TimeStamp;if (time <= timeAgo){if (tooLongAgoFound || timeAgo - time < .02f){chaseState = state;return true;}else{chaseState = new ChaserState();return false;}}elsetooLongAgoFound = true;}}chaseState = new ChaserState();return false;}public bool CanRetry{get{switch (StateMachine.State){default:return true;case StIntroJump:case StIntroWalk:case StIntroWakeUp:case StIntroRespawn:case StReflectionFall:return false;}}}public bool TimePaused{get{if (Dead)return true;switch (StateMachine.State){default:return false;case StIntroJump:case StIntroWalk:case StIntroWakeUp:case StIntroRespawn:case StSummitLaunch:return true;}}}public bool InControl{get{switch (StateMachine.State){default:return true;case StIntroJump:case StIntroWalk:case StIntroWakeUp:case StIntroRespawn:case StDummy:case StFrozen:case StBirdDashTutorial:return false;}}}public PlayerInventory Inventory{get{if (level != null && level.Session != null)return level.Session.Inventory;return PlayerInventory.Default;}}#endregion#region Transitionspublic void OnTransition(){wallSlideTimer = WallSlideTime;jumpGraceTimer = 0;forceMoveXTimer = 0;chaserStates.Clear();RefillDash();RefillStamina();Leader.TransferFollowers();}public bool TransitionTo(Vector2 target, Vector2 direction){MoveTowardsX(target.X, 60f * Engine.DeltaTime);MoveTowardsY(target.Y, 60f * Engine.DeltaTime);//Update hair because the normal update loop is not firing right now!UpdateHair(false);UpdateCarry();//Returns true when transition is completeif (Position == target){ZeroRemainderX();ZeroRemainderY();Speed.X = (int)Math.Round(Speed.X);Speed.Y = (int)Math.Round(Speed.Y);return true;}elsereturn false;}public void BeforeSideTransition(){}public void BeforeDownTransition(){if (StateMachine.State != StRedDash && StateMachine.State != StReflectionFall && StateMachine.State != StStarFly){StateMachine.State = StNormal;Speed.Y = Math.Max(0, Speed.Y);AutoJump = false;varJumpTimer = 0;}foreach (var platform in Scene.Tracker.GetEntities<Platform>())if (!(platform is SolidTiles) && CollideCheckOutside(platform, Position + Vector2.UnitY * Height))platform.Collidable = false;}public void BeforeUpTransition(){Speed.X = 0;if (StateMachine.State != StRedDash && StateMachine.State != StReflectionFall && StateMachine.State != StStarFly){varJumpSpeed = Speed.Y = JumpSpeed;if (StateMachine.State == StSummitLaunch)StateMachine.State = StIntroJump;elseStateMachine.State = StNormal;AutoJump = true;AutoJumpTimer = 0;varJumpTimer = VarJumpTime;}dashCooldownTimer = 0.2f;}#endregion#region Jumps 'n' Stuffpublic bool OnSafeGround{get; private set;}public bool LoseShards{get{return onGround;}}private const float LaunchedBoostCheckSpeedSq = 100 * 100;private const float LaunchedJumpCheckSpeedSq = 220 * 220;private const float LaunchedMinSpeedSq = 140 * 140;private const float LaunchedDoubleSpeedSq = 150 * 150;private bool LaunchedBoostCheck(){if (LiftBoost.LengthSquared() >= LaunchedBoostCheckSpeedSq && Speed.LengthSquared() >= LaunchedJumpCheckSpeedSq){launched = true;return true;}else{launched = false;return false;}}public void Jump(bool particles = true, bool playSfx = true){Input.Jump.ConsumeBuffer();jumpGraceTimer = 0;varJumpTimer = VarJumpTime;AutoJump = false;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;Speed.X += JumpHBoost * moveX;Speed.Y = JumpSpeed;Speed += LiftBoost;varJumpSpeed = Speed.Y;LaunchedBoostCheck();if (playSfx){if (launched)Play(Sfxs.char_mad_jump_assisted);if (dreamJump)Play(Sfxs.char_mad_jump_dreamblock);elsePlay(Sfxs.char_mad_jump);}Sprite.Scale = new Vector2(.6f, 1.4f);if (particles)Dust.Burst(BottomCenter, Calc.Up, 4);SaveData.Instance.TotalJumps++;}private void SuperJump(){Input.Jump.ConsumeBuffer();jumpGraceTimer = 0;varJumpTimer = VarJumpTime;AutoJump = false;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;Speed.X = SuperJumpH * (int)Facing;Speed.Y = JumpSpeed;Speed += LiftBoost;Play(Sfxs.char_mad_jump);if (Ducking){Ducking = false;Speed.X *= DuckSuperJumpXMult;Speed.Y *= DuckSuperJumpYMult;Play(Sfxs.char_mad_jump_superslide);}elsePlay(Sfxs.char_mad_jump_super);varJumpSpeed = Speed.Y;launched = true;Sprite.Scale = new Vector2(.6f, 1.4f);Dust.Burst(BottomCenter, Calc.Up, 4);SaveData.Instance.TotalJumps++;}private bool WallJumpCheck(int dir){return ClimbBoundsCheck(dir) && CollideCheck<Solid>(Position + Vector2.UnitX * dir * WallJumpCheckDist);}private void WallJump(int dir){Ducking = false;Input.Jump.ConsumeBuffer();jumpGraceTimer = 0;varJumpTimer = VarJumpTime;AutoJump = false;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;if (moveX != 0){forceMoveX = dir;forceMoveXTimer = WallJumpForceTime;}//Get lift of wall jumped off ofif (LiftSpeed == Vector2.Zero){Solid wall = CollideFirst<Solid>(Position + Vector2.UnitX * WallJumpCheckDist);if (wall != null)LiftSpeed = wall.LiftSpeed;}Speed.X = WallJumpHSpeed * dir;Speed.Y = JumpSpeed;Speed += LiftBoost;varJumpSpeed = Speed.Y;LaunchedBoostCheck();// wall-sound?var pushOff = SurfaceIndex.GetPlatformByPriority(CollideAll<Platform>(Position - Vector2.UnitX * dir * 4, temp));if (pushOff != null)Play(Sfxs.char_mad_land, SurfaceIndex.Param, pushOff.GetWallSoundIndex(this, -dir));// jump sfxPlay(dir < 0 ? Sfxs.char_mad_jump_wall_right : Sfxs.char_mad_jump_wall_left);Sprite.Scale = new Vector2(.6f, 1.4f);if (dir == -1)Dust.Burst(Center + Vector2.UnitX * 2, Calc.UpLeft, 4);elseDust.Burst(Center + Vector2.UnitX * -2, Calc.UpRight, 4);SaveData.Instance.TotalWallJumps++;}private void SuperWallJump(int dir){Ducking = false;Input.Jump.ConsumeBuffer();jumpGraceTimer = 0;varJumpTimer = SuperWallJumpVarTime;AutoJump = false;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;Speed.X = SuperWallJumpH * dir;Speed.Y = SuperWallJumpSpeed;Speed += LiftBoost;varJumpSpeed = Speed.Y;launched = true;Play(dir < 0 ? Sfxs.char_mad_jump_wall_right : Sfxs.char_mad_jump_wall_left);Play(Sfxs.char_mad_jump_superwall);Sprite.Scale = new Vector2(.6f, 1.4f);if (dir == -1)Dust.Burst(Center + Vector2.UnitX * 2, Calc.UpLeft, 4);elseDust.Burst(Center + Vector2.UnitX * -2, Calc.UpRight, 4);SaveData.Instance.TotalWallJumps++;}private void ClimbJump(){if (!onGround){Stamina -= ClimbJumpCost;sweatSprite.Play("jump", true);Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);}dreamJump = false;Jump(false, false);if (moveX == 0){wallBoostDir = -(int)Facing;wallBoostTimer = ClimbJumpBoostTime;}if (Facing == Facings.Right){Play(Sfxs.char_mad_jump_climb_right);Dust.Burst(Center + Vector2.UnitX * 2, Calc.UpLeft, 4);}else{Play(Sfxs.char_mad_jump_climb_left);Dust.Burst(Center + Vector2.UnitX * -2, Calc.UpRight, 4);}}public void Bounce(float fromY){if (StateMachine.State == StBoost && CurrentBooster != null){CurrentBooster.PlayerReleased();CurrentBooster = null;}var was = Collider;Collider = normalHitbox;{MoveVExact((int)(fromY - Bottom));if (!Inventory.NoRefills)RefillDash();RefillStamina();StateMachine.State = StNormal;jumpGraceTimer = 0;varJumpTimer = BounceVarJumpTime;AutoJump = true;AutoJumpTimer = BounceAutoJumpTime;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;varJumpSpeed = Speed.Y = BounceSpeed;launched = false;Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);Sprite.Scale = new Vector2(.6f, 1.4f);}Collider = was;}public void SuperBounce(float fromY){if (StateMachine.State == StBoost && CurrentBooster != null){CurrentBooster.PlayerReleased();CurrentBooster = null;}var was = Collider;Collider = normalHitbox;{MoveV(fromY - Bottom);if (!Inventory.NoRefills)RefillDash();RefillStamina();StateMachine.State = StNormal;jumpGraceTimer = 0;varJumpTimer = SuperBounceVarJumpTime;AutoJump = true;AutoJumpTimer = 0;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;Speed.X = 0;varJumpSpeed = Speed.Y = SuperBounceSpeed;launched = false;level.DirectionalShake(-Vector2.UnitY, .1f);Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium);Sprite.Scale = new Vector2(.5f, 1.5f);}Collider = was;}private const float SideBounceSpeed = 240;private const float SideBounceForceMoveXTime = .3f;public void SideBounce(int dir, float fromX, float fromY){var was = Collider;Collider = normalHitbox;{MoveV(Calc.Clamp(fromY - Bottom, -4, 4));if (dir > 0)MoveH(fromX - Left);else if (dir < 0)MoveH(fromX - Right);if (!Inventory.NoRefills)RefillDash();RefillStamina();StateMachine.State = StNormal;jumpGraceTimer = 0;varJumpTimer = BounceVarJumpTime;AutoJump = true;AutoJumpTimer = 0;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;forceMoveX = dir;forceMoveXTimer = SideBounceForceMoveXTime;wallBoostTimer = 0;launched = false;Speed.X = SideBounceSpeed * dir;varJumpSpeed = Speed.Y = BounceSpeed;level.DirectionalShake(Vector2.UnitX * dir, .1f);Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium);Sprite.Scale = new Vector2(1.5f, .5f);}Collider = was;}public void Rebound(int direction = 0){Speed.X = direction * ReboundSpeedX;Speed.Y = ReboundSpeedY;varJumpSpeed = Speed.Y;varJumpTimer = ReboundVarJumpTime;AutoJump = true;AutoJumpTimer = 0;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;launched = false;dashAttackTimer = 0;forceMoveXTimer = 0;StateMachine.State = StNormal;}public void ReflectBounce(Vector2 direction){if (direction.X != 0)Speed.X = direction.X * ReflectBoundSpeed;if (direction.Y != 0)Speed.Y = direction.Y * ReflectBoundSpeed;AutoJumpTimer = 0;dashAttackTimer = 0;wallSlideTimer = WallSlideTime;wallBoostTimer = 0;launched = false;dashAttackTimer = 0;forceMoveXTimer = 0;StateMachine.State = StNormal;}public int MaxDashes{get{if (SaveData.Instance.AssistMode && SaveData.Instance.Assists.DashMode != Assists.DashModes.Normal && !level.InCutscene)return 2;elsereturn Inventory.Dashes;}}public bool RefillDash(){if (Dashes < MaxDashes){Dashes = MaxDashes;return true;}elsereturn false;}public bool UseRefill(){if (Dashes < MaxDashes || Stamina < ClimbTiredThreshold){Dashes = MaxDashes;RefillStamina();return true;}elsereturn false;}public void RefillStamina(){Stamina = ClimbMaxStamina;}public PlayerDeadBody Die(Vector2 direction, bool evenIfInvincible = false, bool registerDeathInStats = true){var session = level.Session;bool invincible = (!evenIfInvincible && SaveData.Instance.AssistMode && SaveData.Instance.Assists.Invincible);if (!Dead && !invincible && StateMachine.State != StReflectionFall){Stop(wallSlideSfx);if (registerDeathInStats){session.Deaths++;session.DeathsInCurrentLevel++;SaveData.Instance.AddDeath(session.Area);}// has gold strawb?Strawberry goldenStrawb = null;foreach (var strawb in Leader.Followers)if (strawb.Entity is Strawberry && (strawb.Entity as Strawberry).Golden && !(strawb.Entity as Strawberry).Winged)goldenStrawb = (strawb.Entity as Strawberry);Dead = true;Leader.LoseFollowers();Depth = Depths.Top;Speed = Vector2.Zero;StateMachine.Locked = true;Collidable = false;Drop();if (lastBooster != null)lastBooster.PlayerDied();level.InCutscene = false;level.Shake();Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);var body = new PlayerDeadBody(this, direction);if (goldenStrawb != null){body.DeathAction = () =>{var exit = new LevelExit(LevelExit.Mode.GoldenBerryRestart, session);exit.GoldenStrawberryEntryLevel = goldenStrawb.ID.Level;Engine.Scene = exit;};}Scene.Add(body);Scene.Remove(this);var lookout = Scene.Tracker.GetEntity<Lookout>();if (lookout != null)lookout.StopInteracting();return body;}return null;}private Vector2 LiftBoost{get{Vector2 val = LiftSpeed;if (Math.Abs(val.X) > LiftXCap)val.X = LiftXCap * Math.Sign(val.X);if (val.Y > 0)val.Y = 0;else if (val.Y < LiftYCap)val.Y = LiftYCap;return val;}}#endregion#region Duckingpublic bool Ducking{get{return Collider == duckHitbox || Collider == duckHurtbox;}set{if (value){Collider = duckHitbox;hurtbox = duckHurtbox;}else{Collider = normalHitbox;hurtbox = normalHurtbox;}}}public bool CanUnDuck{get{if (!Ducking)return true;Collider was = Collider;Collider = normalHitbox;bool ret = !CollideCheck<Solid>();Collider = was;return ret;}}public bool CanUnDuckAt(Vector2 at){Vector2 was = Position;Position = at;bool ret = CanUnDuck;Position = was;return ret;}public bool DuckFreeAt(Vector2 at){Vector2 oldP = Position;Collider oldC = Collider;Position = at;Collider = duckHitbox;bool ret = !CollideCheck<Solid>();Position = oldP;Collider = oldC;return ret;}private void Duck(){Collider = duckHitbox;}private void UnDuck(){Collider = normalHitbox;}#endregion#region Holdingpublic Holdable Holding{get; set;}public void UpdateCarry(){if (Holding != null){// don't hold stuff that doesn't exist anymoreif (Holding.Scene == null)Holding = null;elseHolding.Carry(Position + carryOffset + Vector2.UnitY * Sprite.CarryYOffset);}}public void Swat(int dir){if (Holding != null){Holding.Release(new Vector2(.8f * dir, -.25f));Holding = null;}}private bool Pickup(Holdable pickup){if (pickup.Pickup(this)){Ducking = false;Holding = pickup;minHoldTimer = HoldMinTime;return true;}elsereturn false;}private void Throw(){if (Holding != null){if (Input.MoveY.Value == 1)Drop();else{Input.Rumble(RumbleStrength.Strong, RumbleLength.Short);Holding.Release(Vector2.UnitX * (int)Facing);Speed.X += ThrowRecoil * -(int)Facing;}Play(Sfxs.char_mad_crystaltheo_throw);Sprite.Play("throw");Holding = null;}}private void Drop(){if (Holding != null){Input.Rumble(RumbleStrength.Light, RumbleLength.Short);Holding.Release(Vector2.Zero);Holding = null;}}#endregion#region Physicspublic void StartJumpGraceTime(){jumpGraceTimer = JumpGraceTime;}public override bool IsRiding(Solid solid){if (StateMachine.State == StDreamDash)return CollideCheck(solid);if (StateMachine.State == StClimb || StateMachine.State == StHitSquash)return CollideCheck(solid, Position + Vector2.UnitX * (int)Facing);return base.IsRiding(solid);}public override bool IsRiding(JumpThru jumpThru){if (StateMachine.State == StDreamDash)return false;return StateMachine.State != StClimb && Speed.Y >= 0 && base.IsRiding(jumpThru);}public bool BounceCheck(float y){return Bottom <= y + 3;}public void PointBounce(Vector2 from){const float BounceSpeed = 200f;const float MinX = 120f;if (StateMachine.State == StBoost && CurrentBooster != null)CurrentBooster.PlayerReleased();RefillDash();RefillStamina();Speed = (Center - from).SafeNormalize(BounceSpeed);Speed.X *= 1.2f;if (Math.Abs(Speed.X) < MinX){if (Speed.X == 0)Speed.X = -(int)Facing * MinX;elseSpeed.X = Math.Sign(Speed.X) * MinX;}}private void WindMove(Vector2 move){if (!JustRespawned && noWindTimer <= 0 && InControl && StateMachine.State != StBoost && StateMachine.State != StDash && StateMachine.State != StSummitLaunch){//Horizontalif (move.X != 0 && StateMachine.State != StClimb){windTimeout = 0.2f;windDirection.X = Math.Sign(move.X);if (!CollideCheck<Solid>(Position + Vector2.UnitX * -Math.Sign(move.X) * WindWallDistance)){if (Ducking && onGround)move.X *= DuckWindMult;if (move.X < 0)move.X = Math.Max(move.X, level.Bounds.Left - (ExactPosition.X + Collider.Left));elsemove.X = Math.Min(move.X, level.Bounds.Right - (ExactPosition.X + Collider.Right));MoveH(move.X);}}//Verticalif (move.Y != 0){windTimeout = 0.2f;windDirection.Y = Math.Sign(move.Y);if (Speed.Y < 0 || !OnGround()){if (StateMachine.State == StClimb){if (move.Y > 0 && climbNoMoveTimer <= 0)move.Y *= .4f;elsereturn;}MoveV(move.Y);}}}}private void OnCollideH(CollisionData data){if (StateMachine.State == StStarFly){if (starFlyTimer < StarFlyEndNoBounceTime)Speed.X = 0;else{Play(Sfxs.game_06_feather_state_bump);Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);Speed.X *= StarFlyWallBounce;}return;}if (StateMachine.State == StDreamDash)return;//Dash Blocksif (DashAttacking && data.Hit != null && data.Hit.OnDashCollide != null && data.Direction.X == Math.Sign(DashDir.X)){var result = data.Hit.OnDashCollide(this, data.Direction);if (StateMachine.State == StRedDash)result = DashCollisionResults.Ignore;if (result == DashCollisionResults.Rebound){Rebound(-Math.Sign(Speed.X));return;}else if (result == DashCollisionResults.Bounce){ReflectBounce(new Vector2(-Math.Sign(Speed.X), 0));return;}else if (result == DashCollisionResults.Ignore)return;}//Dash corner correctionif (StateMachine.State == StDash || StateMachine.State == StRedDash){if (onGround && DuckFreeAt(Position + Vector2.UnitX * Math.Sign(Speed.X))){Ducking = true;return;}else if (Speed.Y == 0 && Speed.X != 0){for (int i = 1; i <= DashCornerCorrection; i++){for (int j = 1; j >= -1; j -= 2){if (!CollideCheck<Solid>(Position + new Vector2(Math.Sign(Speed.X), i * j))){MoveVExact(i * j);MoveHExact(Math.Sign(Speed.X));return;}}}}}//Dream Dashif (DreamDashCheck(Vector2.UnitX * Math.Sign(Speed.X))){StateMachine.State = StDreamDash;dashAttackTimer = 0;return;}//Speed retentionif (wallSpeedRetentionTimer <= 0){wallSpeedRetained = Speed.X;wallSpeedRetentionTimer = WallSpeedRetentionTime;}//Collide eventif (data.Hit != null && data.Hit.OnCollide != null)data.Hit.OnCollide(data.Direction);Speed.X = 0;dashAttackTimer = 0;if (StateMachine.State == StRedDash){Input.Rumble(RumbleStrength.Medium, RumbleLength.Short);level.Displacement.AddBurst(Center, .5f, 8, 48, .4f, Ease.QuadOut, Ease.QuadOut);StateMachine.State = StHitSquash;}}private void OnCollideV(CollisionData data){if (StateMachine.State == StStarFly){if (starFlyTimer < StarFlyEndNoBounceTime)Speed.Y = 0;else{Play(Sfxs.game_06_feather_state_bump);Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);Speed.Y *= StarFlyWallBounce;}return;}else if (StateMachine.State == StSwim){Speed.Y = 0;return;}else if (StateMachine.State == StDreamDash)return;//Dash Blocksif (data.Hit != null && data.Hit.OnDashCollide != null){if (DashAttacking && data.Direction.Y == Math.Sign(DashDir.Y)){var result = data.Hit.OnDashCollide(this, data.Direction);if (StateMachine.State == StRedDash)result = DashCollisionResults.Ignore;if (result == DashCollisionResults.Rebound){Rebound(0);return;}else if (result == DashCollisionResults.Bounce){ReflectBounce(new Vector2(0, -Math.Sign(Speed.Y)));return;}else if (result == DashCollisionResults.Ignore)return;}else if (StateMachine.State == StSummitLaunch){data.Hit.OnDashCollide(this, data.Direction);return;}}if (Speed.Y > 0){//Dash corner correctionif ((StateMachine.State == StDash || StateMachine.State == StRedDash) && !dashStartedOnGround){if (Speed.X <= 0){for (int i = -1; i >= -DashCornerCorrection; i--){if (!OnGround(Position + new Vector2(i, 0))){MoveHExact(i);MoveVExact(1);return;}}}if (Speed.X >= 0){for (int i = 1; i <= DashCornerCorrection; i++){if (!OnGround(Position + new Vector2(i, 0))){MoveHExact(i);MoveVExact(1);return;}}}}//Dream Dashif (DreamDashCheck(Vector2.UnitY * Math.Sign(Speed.Y))){StateMachine.State = StDreamDash;dashAttackTimer = 0;return;}//Dash Slideif (DashDir.X != 0 && DashDir.Y > 0 && Speed.Y > 0){DashDir.X = Math.Sign(DashDir.X);DashDir.Y = 0;Speed.Y = 0;Speed.X *= DodgeSlideSpeedMult;Ducking = true;}//Landed on ground effectsif (StateMachine.State != StClimb){float squish = Math.Min(Speed.Y / FastMaxFall, 1);Sprite.Scale.X = MathHelper.Lerp(1, 1.6f, squish);Sprite.Scale.Y = MathHelper.Lerp(1, .4f, squish);if (Speed.Y >= MaxFall / 2)Dust.Burst(Position, Calc.Angle(new Vector2(0, -1)), 8);if (highestAirY < Y - 50 && Speed.Y >= MaxFall && Math.Abs(Speed.X) >= MaxRun)Sprite.Play(PlayerSprite.RunStumble);Input.Rumble(RumbleStrength.Light, RumbleLength.Short);// landed SFXvar platform = SurfaceIndex.GetPlatformByPriority(CollideAll<Platform>(Position + new Vector2(0, 1), temp));if (platform != null){var surface = platform.GetLandSoundIndex(this);if (surface >= 0)Play(playFootstepOnLand > 0f ? Sfxs.char_mad_footstep : Sfxs.char_mad_land, SurfaceIndex.Param, surface);if (platform is DreamBlock)(platform as DreamBlock).FootstepRipple(Position);}playFootstepOnLand = 0f;}}else {if (Speed.Y < 0){//Corner Correction{if (Speed.X <= 0){for (int i = 1; i <= UpwardCornerCorrection; i++){if (!CollideCheck<Solid>(Position + new Vector2(-i, -1))){Position += new Vector2(-i, -1);return;}}}if (Speed.X >= 0){for (int i = 1; i <= UpwardCornerCorrection; i++){if (!CollideCheck<Solid>(Position + new Vector2(i, -1))){Position += new Vector2(i, -1);return;}}}}if (varJumpTimer < VarJumpTime - CeilingVarJumpGrace)varJumpTimer = 0;}//Dream Dashif (DreamDashCheck(Vector2.UnitY * Math.Sign(Speed.Y))){StateMachine.State = StDreamDash;dashAttackTimer = 0;return;}}//Collide eventif (data.Hit != null && data.Hit.OnCollide != null)data.Hit.OnCollide(data.Direction);dashAttackTimer = 0;Speed.Y = 0;if (StateMachine.State == StRedDash){Input.Rumble(RumbleStrength.Medium, RumbleLength.Short);level.Displacement.AddBurst(Center, .5f, 8, 48, .4f, Ease.QuadOut, Ease.QuadOut);StateMachine.State = StHitSquash;}}private bool DreamDashCheck(Vector2 dir){if (Inventory.DreamDash && DashAttacking && (dir.X == Math.Sign(DashDir.X) || dir.Y == Math.Sign(DashDir.Y))){var block = CollideFirst<DreamBlock>(Position + dir);if (block != null){if (CollideCheck<Solid, DreamBlock>(Position + dir)){Vector2 side = new Vector2(Math.Abs(dir.Y), Math.Abs(dir.X));bool checkNegative, checkPositive;if (dir.X != 0){checkNegative = Speed.Y <= 0;checkPositive = Speed.Y >= 0;}else{checkNegative = Speed.X <= 0;checkPositive = Speed.X >= 0;}if (checkNegative){for (int i = -1; i >= -DashCornerCorrection; i--){var at = Position + dir + side * i;if (!CollideCheck<Solid, DreamBlock>(at)){Position += side * i;dreamBlock = block;return true;}}}if (checkPositive){for (int i = 1; i <= DashCornerCorrection; i++){var at = Position + dir + side * i;if (!CollideCheck<Solid, DreamBlock>(at)){Position += side * i;dreamBlock = block;return true;}}}return false;}else{dreamBlock = block;return true;}}        }return false;}public void OnBoundsH(){Speed.X = 0;if (StateMachine.State == StRedDash)StateMachine.State = StNormal;}public void OnBoundsV(){Speed.Y = 0;if (StateMachine.State == StRedDash)StateMachine.State = StNormal;}override protected void OnSquish(CollisionData data){bool ducked = false;if (!Ducking){ducked = true;Ducking = true;data.Pusher.Collidable = true;if (!CollideCheck<Solid>()){data.Pusher.Collidable = false;return;}var was = Position;Position = data.TargetPosition;if (!CollideCheck<Solid>()){data.Pusher.Collidable = false;return;}Position = was;data.Pusher.Collidable = false;}if (!TrySquishWiggle(data))Die(Vector2.Zero);else if (ducked && CanUnDuck)Ducking = false;}#endregion#region Normal Stateprivate void NormalBegin(){maxFall = MaxFall;}private void NormalEnd(){wallBoostTimer = 0;wallSpeedRetentionTimer = 0;hopWaitX = 0;}public bool ClimbBoundsCheck(int dir){return Left + dir * ClimbCheckDist >= level.Bounds.Left && Right + dir * ClimbCheckDist < level.Bounds.Right;}public bool ClimbCheck(int dir, int yAdd = 0){return ClimbBoundsCheck(dir) && !ClimbBlocker.Check(Scene, this, Position + Vector2.UnitY * yAdd + Vector2.UnitX * ClimbCheckDist * (int)Facing) && CollideCheck<Solid>(Position + new Vector2(dir * ClimbCheckDist, yAdd));}private const float SpacePhysicsMult = .6f;private int NormalUpdate(){//Use Lift Boost if walked off platformif (LiftBoost.Y < 0 && wasOnGround && !onGround && Speed.Y >= 0)Speed.Y = LiftBoost.Y;if (Holding == null){if (Input.Grab.Check && !IsTired && !Ducking){//Grabbing Holdablesforeach (Holdable hold in Scene.Tracker.GetComponents<Holdable>())if (hold.Check(this) && Pickup(hold))return StPickup;//Climbingif (Speed.Y >= 0 && Math.Sign(Speed.X) != -(int)Facing){if (ClimbCheck((int)Facing)){Ducking = false;return StClimb;}if (Input.MoveY < 1 && level.Wind.Y <= 0){for (int i = 1; i <= ClimbUpCheckDist; i++){if (!CollideCheck<Solid>(Position + Vector2.UnitY * -i) && ClimbCheck((int)Facing, -i)){MoveVExact(-i);Ducking = false;return StClimb;}}}}}//Dashingif (CanDash){Speed += LiftBoost;                   return StartDash();}//Duckingif (Ducking){if (onGround && Input.MoveY != 1){if (CanUnDuck){Ducking = false;Sprite.Scale = new Vector2(.8f, 1.2f);}else if (Speed.X == 0){for (int i = DuckCorrectCheck; i > 0; i--){if (CanUnDuckAt(Position + Vector2.UnitX * i)){MoveH(DuckCorrectSlide * Engine.DeltaTime);break;}else if (CanUnDuckAt(Position - Vector2.UnitX * i)){MoveH(-DuckCorrectSlide * Engine.DeltaTime);break;}}}}}else if(onGround && Input.MoveY == 1 && Speed.Y >= 0){Ducking = true;Sprite.Scale = new Vector2(1.4f, .6f);}}else{//Throwif (!Input.Grab.Check && minHoldTimer <= 0)Throw();//Duckingif (!Ducking && onGround && Input.MoveY == 1 && Speed.Y >= 0){Drop();Ducking = true;Sprite.Scale = new Vector2(1.4f, .6f);}}//Running and Frictionif (Ducking && onGround)Speed.X = Calc.Approach(Speed.X, 0, DuckFriction * Engine.DeltaTime);else{float mult = onGround ? 1 : AirMult;if (onGround && level.CoreMode == Session.CoreModes.Cold)mult *= .3f;float max = Holding == null ? MaxRun : HoldingMaxRun;if (level.InSpace)max *= SpacePhysicsMult;if (Math.Abs(Speed.X) > max && Math.Sign(Speed.X) == moveX)Speed.X = Calc.Approach(Speed.X, max * moveX, RunReduce * mult * Engine.DeltaTime);  //Reduce back from beyond the max speedelseSpeed.X = Calc.Approach(Speed.X, max * moveX, RunAccel * mult * Engine.DeltaTime);   //Approach the max speed}//Vertical{//Calculate current max fall speed{float mf = MaxFall;float fmf = FastMaxFall;if (level.InSpace){mf *= SpacePhysicsMult;fmf *= SpacePhysicsMult;}//Fast Fallif (Input.MoveY == 1 && Speed.Y >= mf){maxFall = Calc.Approach(maxFall, fmf, FastMaxAccel * Engine.DeltaTime);float half = mf + (fmf - mf) * .5f;if (Speed.Y >= half){float spriteLerp = Math.Min(1f, (Speed.Y - half) / (fmf - half));Sprite.Scale.X = MathHelper.Lerp(1f, .5f, spriteLerp);Sprite.Scale.Y = MathHelper.Lerp(1f, 1.5f, spriteLerp);}}elsemaxFall = Calc.Approach(maxFall, mf, FastMaxAccel * Engine.DeltaTime);}//Gravityif (!onGround){float max = maxFall;//Wall Slideif ((moveX == (int)Facing || (moveX == 0 && Input.Grab.Check)) && Input.MoveY.Value != 1){if (Speed.Y >= 0 && wallSlideTimer > 0 && Holding == null && ClimbBoundsCheck((int)Facing) && CollideCheck<Solid>(Position + Vector2.UnitX * (int)Facing) && CanUnDuck){Ducking = false;wallSlideDir = (int)Facing;}if (wallSlideDir != 0){if (wallSlideTimer > WallSlideTime * .5f && ClimbBlocker.Check(level, this, Position + Vector2.UnitX * wallSlideDir))wallSlideTimer = WallSlideTime * .5f;max = MathHelper.Lerp(MaxFall, WallSlideStartMax, wallSlideTimer / WallSlideTime);if (wallSlideTimer / WallSlideTime > .65f)CreateWallSlideParticles(wallSlideDir);}}float mult = (Math.Abs(Speed.Y) < HalfGravThreshold && (Input.Jump.Check || AutoJump)) ? .5f : 1f;if (level.InSpace)mult *= SpacePhysicsMult;Speed.Y = Calc.Approach(Speed.Y, max, Gravity * mult * Engine.DeltaTime);}//Variable Jumpingif (varJumpTimer > 0){if (AutoJump || Input.Jump.Check)Speed.Y = Math.Min(Speed.Y, varJumpSpeed);elsevarJumpTimer = 0;}//Jumpingif (Input.Jump.Pressed){Water water = null;if (jumpGraceTimer > 0){Jump();}else if (CanUnDuck){bool canUnduck = CanUnDuck;if (canUnduck && WallJumpCheck(1)){if (Facing == Facings.Right && Input.Grab.Check && Stamina > 0 && Holding == null && !ClimbBlocker.Check(Scene, this, Position + Vector2.UnitX * WallJumpCheckDist))ClimbJump();else if (DashAttacking && DashDir.X == 0 && DashDir.Y == -1)SuperWallJump(-1);elseWallJump(-1);}else if (canUnduck && WallJumpCheck(-1)){if (Facing == Facings.Left && Input.Grab.Check && Stamina > 0 && Holding == null && !ClimbBlocker.Check(Scene, this, Position + Vector2.UnitX * -WallJumpCheckDist))ClimbJump();else if (DashAttacking && DashDir.X == 0 && DashDir.Y == -1)SuperWallJump(1);elseWallJump(1);}else if ((water = CollideFirst<Water>(Position + Vector2.UnitY * 2)) != null){Jump();water.TopSurface.DoRipple(Position, 1);}}}}return StNormal;}public void CreateWallSlideParticles(int dir){if (Scene.OnInterval(.01f)){Vector2 at = Center;if (dir == 1)at += new Vector2(5, 4);elseat += new Vector2(-5, 4);Dust.Burst(at, Calc.Up, 1);}}#endregion#region Climb Stateprivate bool IsTired{get{return CheckStamina < ClimbTiredThreshold;}}private float CheckStamina{get{if (wallBoostTimer > 0)return Stamina + ClimbJumpCost;elsereturn Stamina;}}private void PlaySweatEffectDangerOverride(string state){if (Stamina <= ClimbTiredThreshold)sweatSprite.Play("danger");elsesweatSprite.Play(state);}private FMOD.Studio.EventInstance conveyorLoopSfx;private void ClimbBegin(){AutoJump = false;Speed.X = 0;Speed.Y *= ClimbGrabYMult;wallSlideTimer = WallSlideTime;climbNoMoveTimer = ClimbNoMoveTime;wallBoostTimer = 0;lastClimbMove = 0;Input.Rumble(RumbleStrength.Medium, RumbleLength.Short);for (int i = 0; i < ClimbCheckDist; i++)if (!CollideCheck<Solid>(Position + Vector2.UnitX * (int)Facing))Position += Vector2.UnitX * (int)Facing;elsebreak;// tell the thing we grabbed itvar platform = SurfaceIndex.GetPlatformByPriority(CollideAll<Solid>(Position + Vector2.UnitX * (int)Facing, temp));if (platform != null)Play(Sfxs.char_mad_grab, SurfaceIndex.Param, platform.GetWallSoundIndex(this, (int)Facing));}private void ClimbEnd(){if (conveyorLoopSfx != null){conveyorLoopSfx.setParameterValue("end", 1);conveyorLoopSfx.release();conveyorLoopSfx = null;}wallSpeedRetentionTimer = 0;if (sweatSprite != null && sweatSprite.CurrentAnimationID != "jump")sweatSprite.Play("idle");}private const float WallBoosterSpeed = -160f;private const float WallBoosterLiftSpeed = -80;private const float WallBoosterAccel = 600f;private const float WallBoostingHopHSpeed = 100f;private const float WallBoosterOverTopSpeed = -180f;private const float IceBoosterSpeed = 40;private const float IceBoosterAccel = 300f;private bool wallBoosting;private int ClimbUpdate(){climbNoMoveTimer -= Engine.DeltaTime;//Refill stamina on groundif (onGround)Stamina = ClimbMaxStamina;//Wall Jumpif (Input.Jump.Pressed && (!Ducking || CanUnDuck)){if (moveX == -(int)Facing)WallJump(-(int)Facing);elseClimbJump();return StNormal;}//Dashingif (CanDash){Speed += LiftBoost;return StartDash();}//Let goif (!Input.Grab.Check){Speed += LiftBoost;Play(Sfxs.char_mad_grab_letgo);return StNormal;}//No wall to holdif (!CollideCheck<Solid>(Position + Vector2.UnitX * (int)Facing)){//Climbed over ledge?if (Speed.Y < 0){if (wallBoosting){Speed += LiftBoost;Play(Sfxs.char_mad_grab_letgo);}elseClimbHop();}return StNormal;}var booster = WallBoosterCheck();if (climbNoMoveTimer <= 0 && booster != null){//WallboosterwallBoosting = true;if (conveyorLoopSfx == null)conveyorLoopSfx = Audio.Play(Sfxs.game_09_conveyor_activate, Position, "end", 0);Audio.Position(conveyorLoopSfx, Position);Speed.Y = Calc.Approach(Speed.Y, WallBoosterSpeed, WallBoosterAccel * Engine.DeltaTime);LiftSpeed = Vector2.UnitY * Math.Max(Speed.Y, WallBoosterLiftSpeed);Input.Rumble(RumbleStrength.Light, RumbleLength.Short);}else{wallBoosting = false;if (conveyorLoopSfx != null){conveyorLoopSfx.setParameterValue("end", 1);conveyorLoopSfx.release();conveyorLoopSfx = null;}//Climbingfloat target = 0;bool trySlip = false;if (climbNoMoveTimer <= 0){if (ClimbBlocker.Check(Scene, this, Position + Vector2.UnitX * (int)Facing))trySlip = true;else if (Input.MoveY.Value == -1){target = ClimbUpSpeed;//Up Limitif (CollideCheck<Solid>(Position - Vector2.UnitY) || (ClimbHopBlockedCheck() && SlipCheck(-1))){if (Speed.Y < 0)Speed.Y = 0;target = 0;trySlip = true;}else if (SlipCheck()){//HoppingClimbHop();return StNormal;}}else if (Input.MoveY.Value == 1){target = ClimbDownSpeed;if (onGround){if (Speed.Y > 0)Speed.Y = 0;target = 0;}elseCreateWallSlideParticles((int)Facing);}elsetrySlip = true;}elsetrySlip = true;lastClimbMove = Math.Sign(target);//Slip down if hands above the ledge and no vertical inputif (trySlip && SlipCheck())target = ClimbSlipSpeed;//Set SpeedSpeed.Y = Calc.Approach(Speed.Y, target, ClimbAccel * Engine.DeltaTime);}//Down Limitif (Input.MoveY.Value != 1 && Speed.Y > 0 && !CollideCheck<Solid>(Position + new Vector2((int)Facing, 1)))Speed.Y = 0;//Staminaif (climbNoMoveTimer <= 0){if (lastClimbMove == -1){Stamina -= ClimbUpCost * Engine.DeltaTime;if (Stamina <= ClimbTiredThreshold)sweatSprite.Play("danger");else if (sweatSprite.CurrentAnimationID != "climbLoop")sweatSprite.Play("climb");if (Scene.OnInterval(.2f))Input.Rumble(RumbleStrength.Climb, RumbleLength.Short);}else{if (lastClimbMove == 0)Stamina -= ClimbStillCost * Engine.DeltaTime;if (!onGround){PlaySweatEffectDangerOverride("still");if (Scene.OnInterval(.8f))Input.Rumble(RumbleStrength.Climb, RumbleLength.Short);}elsePlaySweatEffectDangerOverride("idle");}}elsePlaySweatEffectDangerOverride("idle");//Too tired           if (Stamina <= 0){Speed += LiftBoost;return StNormal;}return StClimb;}private WallBooster WallBoosterCheck(){if (ClimbBlocker.Check(Scene, this, Position + Vector2.UnitX * (int)Facing))return null;foreach (WallBooster booster in Scene.Tracker.GetEntities<WallBooster>())if (booster.Facing == Facing && CollideCheck(booster))return booster;return null;}private void ClimbHop(){climbHopSolid = CollideFirst<Solid>(Position + Vector2.UnitX * (int)Facing);playFootstepOnLand = 0.5f;if (climbHopSolid != null){climbHopSolidPosition = climbHopSolid.Position;hopWaitX = (int)Facing;hopWaitXSpeed = (int)Facing * ClimbHopX;}else{hopWaitX = 0;Speed.X = (int)Facing * ClimbHopX;}Speed.Y = Math.Min(Speed.Y, ClimbHopY);forceMoveX = 0;forceMoveXTimer = ClimbHopForceTime;fastJump = false;noWindTimer = ClimbHopNoWindTime;Play(Sfxs.char_mad_climb_ledge);}private bool SlipCheck(float addY = 0){Vector2 at;if (Facing == Facings.Right)at = TopRight + Vector2.UnitY * (4 + addY);elseat = TopLeft - Vector2.UnitX + Vector2.UnitY * (4 + addY);return !Scene.CollideCheck<Solid>(at) && !Scene.CollideCheck<Solid>(at + Vector2.UnitY * (-4 + addY));}private bool ClimbHopBlockedCheck(){//If you have a cassette note, you're blockedforeach (Follower follower in Leader.Followers)if (follower.Entity is StrawberrySeed)return true;//If you hit a ledge blocker, you're blockedforeach (LedgeBlocker blocker in Scene.Tracker.GetComponents<LedgeBlocker>())if (blocker.HopBlockCheck(this))return true;//If there's a solid in the way, you're blockedif (CollideCheck<Solid>(Position - Vector2.UnitY * 6))return true;return false;}private bool JumpThruBoostBlockedCheck(){foreach (LedgeBlocker blocker in Scene.Tracker.GetComponents<LedgeBlocker>())if (blocker.JumpThruBoostCheck(this))return true;return false;}#endregion#region Dash Statepublic int StartDash(){wasDashB = Dashes == 2;Dashes = Math.Max(0, Dashes - 1);Input.Dash.ConsumeBuffer();return StDash;}public bool DashAttacking{get{return dashAttackTimer > 0 || StateMachine.State == StRedDash;}}private Vector2 beforeDashSpeed;private bool wasDashB;public bool CanDash{get{return Input.Dash.Pressed && dashCooldownTimer <= 0 && Dashes > 0 &&(TalkComponent.PlayerOver == null || !Input.Talk.Pressed);}}public bool StartedDashing{get; private set;}private void CallDashEvents(){if (!calledDashEvents){calledDashEvents = true;if (CurrentBooster == null){// Increment CounterSaveData.Instance.TotalDashes++;level.Session.Dashes++;Stats.Increment(Stat.DASHES);// Sfxs{var rightDashSound = DashDir.Y < 0 || (DashDir.Y == 0 && DashDir.X > 0);if (DashDir == Vector2.Zero)rightDashSound = Facing == Facings.Right;if (rightDashSound){if (wasDashB)Play(Sfxs.char_mad_dash_pink_right);elsePlay(Sfxs.char_mad_dash_red_right);}else{if (wasDashB)Play(Sfxs.char_mad_dash_pink_left);elsePlay(Sfxs.char_mad_dash_red_left);}if (SwimCheck())Play(Sfxs.char_mad_water_dash_gen);}//Dash Listenersforeach (DashListener dl in Scene.Tracker.GetComponents<DashListener>())if (dl.OnDash != null)dl.OnDash(DashDir);}else{//BoosterCurrentBooster.PlayerBoosted(this, DashDir);CurrentBooster = null;}}}private void DashBegin(){calledDashEvents = false;dashStartedOnGround = onGround;launched = false;if (Engine.TimeRate > 0.25f)Celeste.Freeze(.05f);dashCooldownTimer = DashCooldown;dashRefillCooldownTimer = DashRefillCooldown;StartedDashing = true;wallSlideTimer = WallSlideTime;dashTrailTimer = 0;level.Displacement.AddBurst(Center, .4f, 8, 64, .5f, Ease.QuadOut, Ease.QuadOut);Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);dashAttackTimer = DashAttackTime;beforeDashSpeed = Speed;Speed = Vector2.Zero;DashDir = Vector2.Zero;if (!onGround && Ducking && CanUnDuck)Ducking = false;}private void DashEnd(){CallDashEvents();}private int DashUpdate(){StartedDashing = false;//Trailif (dashTrailTimer > 0){dashTrailTimer -= Engine.DeltaTime;if (dashTrailTimer <= 0)CreateTrail();}//Grab Holdablesif (Holding == null && Input.Grab.Check && !IsTired && CanUnDuck){//Grabbing Holdablesforeach (Holdable hold in Scene.Tracker.GetComponents<Holdable>())if (hold.Check(this) && Pickup(hold))return StPickup;}if (DashDir.Y == 0){//JumpThru Correctionforeach (JumpThru jt in Scene.Tracker.GetEntities<JumpThru>())if (CollideCheck(jt) && Bottom - jt.Top <= DashHJumpThruNudge)MoveVExact((int)(jt.Top - Bottom));//Super Jumpif (CanUnDuck && Input.Jump.Pressed && jumpGraceTimer > 0){SuperJump();return StNormal;}}if (DashDir.X == 0 && DashDir.Y == -1){if (Input.Jump.Pressed && CanUnDuck){if (WallJumpCheck(1)){SuperWallJump(-1);return StNormal;}else if (WallJumpCheck(-1)){SuperWallJump(1);return StNormal;}}}else{if (Input.Jump.Pressed && CanUnDuck){if (WallJumpCheck(1)){WallJump(-1);return StNormal;}else if (WallJumpCheck(-1)){WallJump(1);return StNormal;}}}if (Speed != Vector2.Zero && level.OnInterval(0.02f))level.ParticlesFG.Emit(wasDashB ? P_DashB : P_DashA, Center + Calc.Random.Range(Vector2.One *-2, Vector2.One * 2), DashDir.Angle());return StDash;}private IEnumerator DashCoroutine(){yield return null;var dir = lastAim;if (OverrideDashDirection.HasValue)dir = OverrideDashDirection.Value;var newSpeed = dir * DashSpeed;if (Math.Sign(beforeDashSpeed.X) == Math.Sign(newSpeed.X) && Math.Abs(beforeDashSpeed.X) > Math.Abs(newSpeed.X))newSpeed.X = beforeDashSpeed.X;Speed = newSpeed;if (CollideCheck<Water>())Speed *= SwimDashSpeedMult;DashDir = dir;SceneAs<Level>().DirectionalShake(DashDir, .2f);if (DashDir.X != 0)Facing = (Facings)Math.Sign(DashDir.X);CallDashEvents();//Feather particlesif (StateMachine.PreviousState == StStarFly)level.Particles.Emit(FlyFeather.P_Boost, 12, Center, Vector2.One * 4, (-dir).Angle());//Dash Slideif (onGround && DashDir.X != 0 && DashDir.Y > 0 && Speed.Y > 0 && (!Inventory.DreamDash || !CollideCheck<DreamBlock>(Position + Vector2.UnitY))){DashDir.X = Math.Sign(DashDir.X);DashDir.Y = 0;Speed.Y = 0;Speed.X *= DodgeSlideSpeedMult;Ducking = true;}SlashFx.Burst(Center, DashDir.Angle());CreateTrail();dashTrailTimer = .08f;//Swap Block checkif (DashDir.X != 0 && Input.Grab.Check){var swapBlock = CollideFirst<SwapBlock>(Position + Vector2.UnitX * Math.Sign(DashDir.X));if (swapBlock != null && swapBlock.Direction.X == Math.Sign(DashDir.X)){StateMachine.State = StClimb;Speed = Vector2.Zero;yield break;}}//Stick to Swap BlocksVector2 swapCancel = Vector2.One;foreach (SwapBlock swapBlock in Scene.Tracker.GetEntities<SwapBlock>()){if (CollideCheck(swapBlock, Position + Vector2.UnitY)){if (swapBlock != null && swapBlock.Swapping){if (DashDir.X != 0 && swapBlock.Direction.X == Math.Sign(DashDir.X))Speed.X = swapCancel.X = 0;if (DashDir.Y != 0 && swapBlock.Direction.Y == Math.Sign(DashDir.Y))Speed.Y = swapCancel.Y = 0;}}}yield return DashTime;CreateTrail();AutoJump = true;AutoJumpTimer = 0;if (DashDir.Y <= 0){Speed = DashDir * EndDashSpeed;Speed.X *= swapCancel.X;Speed.Y *= swapCancel.Y;}if (Speed.Y < 0)Speed.Y *= EndDashUpMult;StateMachine.State = StNormal;}#endregion#region Swim Stateprivate const float SwimYSpeedMult = .5f;    private const float SwimMaxRise = -60;private const float SwimVDeccel = 600f;private const float SwimMax = 80f;private const float SwimUnderwaterMax = 60f;private const float SwimAccel = 600f;private const float SwimReduce = 400f;private const float SwimDashSpeedMult = .75f;private bool SwimCheck(){return CollideCheck<Water>(Position + Vector2.UnitY * -8) && CollideCheck<Water>(Position);}private bool SwimUnderwaterCheck(){return CollideCheck<Water>(Position + Vector2.UnitY * -9);}private bool SwimJumpCheck(){return !CollideCheck<Water>(Position + Vector2.UnitY * -14);}private bool SwimRiseCheck(){return !CollideCheck<Water>(Position + Vector2.UnitY * -18);}private bool UnderwaterMusicCheck(){return CollideCheck<Water>(Position) && CollideCheck<Water>(Position + Vector2.UnitY * -12);}private void SwimBegin(){if (Speed.Y > 0)Speed.Y *= SwimYSpeedMult;Stamina = ClimbMaxStamina;}private int SwimUpdate(){if (!SwimCheck())return StNormal;//Never duck if possibleif (CanUnDuck)Ducking = false;//Dashingif (CanDash){//Dashes = Math.Max(0, Dashes - 1);Input.Dash.ConsumeBuffer();return StDash;}bool underwater = SwimUnderwaterCheck();//Climbingif (!underwater && Speed.Y >= 0 && Input.Grab.Check && !IsTired && CanUnDuck){//Climbingif (Math.Sign(Speed.X) != -(int)Facing && ClimbCheck((int)Facing)){if (!MoveVExact(-1)){Ducking = false;return StClimb;}}}//Movement{Vector2 move = Input.Aim.Value;move = move.SafeNormalize();float maxX = underwater ? SwimUnderwaterMax : SwimMax;float maxY = SwimMax;if (Math.Abs(Speed.X) > SwimMax && Math.Sign(Speed.X) == Math.Sign(move.X))Speed.X = Calc.Approach(Speed.X, maxX * move.X, SwimReduce * Engine.DeltaTime);  //Reduce back from beyond the max speedelseSpeed.X = Calc.Approach(Speed.X, maxX * move.X, SwimAccel * Engine.DeltaTime);   //Approach the max speedif (move.Y == 0 && SwimRiseCheck()){//Rise if close to the surface and not pressing a directionSpeed.Y = Calc.Approach(Speed.Y, SwimMaxRise, SwimAccel * Engine.DeltaTime);}else if (move.Y >= 0 || SwimUnderwaterCheck()){if (Math.Abs(Speed.Y) > SwimMax && Math.Sign(Speed.Y) == Math.Sign(move.Y))Speed.Y = Calc.Approach(Speed.Y, maxY * move.Y, SwimReduce * Engine.DeltaTime);  //Reduce back from beyond the max speedelseSpeed.Y = Calc.Approach(Speed.Y, maxY * move.Y, SwimAccel * Engine.DeltaTime);   //Approach the max speed}}//Popping up onto ledgeconst int ledgePopDist = -3;if (!underwater && moveX != 0&& CollideCheck<Solid>(Position + Vector2.UnitX * moveX)&& !CollideCheck<Solid>(Position + new Vector2(moveX, ledgePopDist))){ClimbHop();}//Jumpingif (Input.Jump.Pressed && SwimJumpCheck()){Jump();return StNormal;}return StSwim;}#endregion#region Boost Stateprivate Vector2 boostTarget;private bool boostRed;public void Boost(Booster booster){StateMachine.State = StBoost;Speed = Vector2.Zero;boostTarget = booster.Center;boostRed = false;lastBooster = CurrentBooster = booster;}public void RedBoost(Booster booster){StateMachine.State = StBoost;Speed = Vector2.Zero;boostTarget = booster.Center;boostRed = true;lastBooster = CurrentBooster = booster;}private void BoostBegin(){RefillDash();RefillStamina();}private void BoostEnd(){var to = (boostTarget - Collider.Center).Floor();MoveToX(to.X);MoveToY(to.Y);}private int BoostUpdate(){Vector2 targetAdd = Input.Aim.Value * 3;var to = Calc.Approach(ExactPosition, boostTarget - Collider.Center + targetAdd, BoostMoveSpeed * Engine.DeltaTime);MoveToX(to.X);MoveToY(to.Y);if (Input.Dash.Pressed){Input.Dash.ConsumePress();if (boostRed)return StRedDash;elsereturn StDash;}return StBoost;}private IEnumerator BoostCoroutine(){yield return BoostTime;if (boostRed)StateMachine.State = StRedDash;elseStateMachine.State = StDash;}#endregion#region Red Dash Stateprivate void RedDashBegin(){calledDashEvents = false;dashStartedOnGround = false;Celeste.Freeze(.05f);Dust.Burst(Position, Calc.Angle(-DashDir), 8);dashCooldownTimer = DashCooldown;dashRefillCooldownTimer = DashRefillCooldown;StartedDashing = true;level.Displacement.AddBurst(Center, .5f, 0, 80, .666f, Ease.QuadOut, Ease.QuadOut);Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);dashAttackTimer = DashAttackTime;Speed = Vector2.Zero;if (!onGround && Ducking && CanUnDuck)Ducking = false;}private void RedDashEnd(){CallDashEvents();}private int RedDashUpdate(){StartedDashing = false;if (CanDash)return StartDash();if (DashDir.Y == 0){//JumpThru Correctionforeach (JumpThru jt in Scene.Tracker.GetEntities<JumpThru>())if (CollideCheck(jt) && Bottom - jt.Top <= DashHJumpThruNudge)MoveVExact((int)(jt.Top - Bottom));//Super Jumpif (CanUnDuck && Input.Jump.Pressed && jumpGraceTimer > 0){SuperJump();return StNormal;}}else if (DashDir.X == 0 && DashDir.Y == -1){if (Input.Jump.Pressed && CanUnDuck){if (WallJumpCheck(1)){SuperWallJump(-1);return StNormal;}else if (WallJumpCheck(-1)){SuperWallJump(1);return StNormal;}}}else{if (Input.Jump.Pressed && CanUnDuck){if (WallJumpCheck(1)){WallJump(-1);return StNormal;}else if (WallJumpCheck(-1)){WallJump(1);return StNormal;}}}return StRedDash;}private IEnumerator RedDashCoroutine(){yield return null;Speed = lastAim * DashSpeed;DashDir = lastAim;SceneAs<Level>().DirectionalShake(DashDir, .2f);CallDashEvents();}#endregion#region Hit Squash Stateprivate const float HitSquashNoMoveTime = .1f;private const float HitSquashFriction = 800;private float hitSquashNoMoveTimer;private void HitSquashBegin(){hitSquashNoMoveTimer = HitSquashNoMoveTime;}private int HitSquashUpdate(){Speed.X = Calc.Approach(Speed.X, 0, HitSquashFriction * Engine.DeltaTime);Speed.Y = Calc.Approach(Speed.Y, 0, HitSquashFriction * Engine.DeltaTime);if (Input.Jump.Pressed){if (onGround)Jump();else if (WallJumpCheck(1))WallJump(-1);else if (WallJumpCheck(-1))WallJump(1);elseInput.Jump.ConsumeBuffer();return StNormal;}if (CanDash)return StartDash();if (Input.Grab.Check && ClimbCheck((int)Facing))return StClimb;if (hitSquashNoMoveTimer > 0)hitSquashNoMoveTimer -= Engine.DeltaTime;elsereturn StNormal;return StHitSquash;}#endregion#region Launch Stateprivate float? launchApproachX;public Vector2 ExplodeLaunch(Vector2 from, bool snapUp = true){Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);Celeste.Freeze(.1f);launchApproachX = null;Vector2 normal = (Center - from).SafeNormalize(-Vector2.UnitY);var dot = Vector2.Dot(normal, Vector2.UnitY);if (snapUp && dot <= -.7f){//If close to up, make it upnormal.X = 0;normal.Y = -1;}else if (dot <= .55f && dot >= -.55f){//If partially down, make it sidewaysnormal.Y = 0;normal.X = Math.Sign(normal.X);}Speed = LaunchSpeed * normal;if (Speed.Y <= 50f){Speed.Y = Math.Min(-150f, Speed.Y);AutoJump = true;}if (Input.MoveX.Value == Math.Sign(Speed.X))Speed.X *= 1.2f;SlashFx.Burst(Center, Speed.Angle());if (!Inventory.NoRefills)RefillDash();RefillStamina();dashCooldownTimer = DashCooldown;StateMachine.State = StLaunch;return normal;}public void FinalBossPushLaunch(int dir){launchApproachX = null;Speed.X = .9f * dir * LaunchSpeed;Speed.Y = -150;AutoJump = true;Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);SlashFx.Burst(Center, Speed.Angle());RefillDash();RefillStamina();dashCooldownTimer = .28f;StateMachine.State = StLaunch;}public void BadelineBoostLaunch(float atX){launchApproachX = atX;Speed.X = 0;Speed.Y = -330f;AutoJump = true;SlashFx.Burst(Center, Speed.Angle());RefillDash();RefillStamina();dashCooldownTimer = DashCooldown;StateMachine.State = StLaunch;}private void LaunchBegin(){launched = true;}private int LaunchUpdate(){//Approach Xif (launchApproachX.HasValue)MoveTowardsX(launchApproachX.Value, 60 * Engine.DeltaTime);//Dashingif (CanDash)return StartDash();//Deccelerationif (Speed.Y < 0)Speed.Y = Calc.Approach(Speed.Y, MaxFall, Gravity * .5f * Engine.DeltaTime);elseSpeed.Y = Calc.Approach(Speed.Y, MaxFall, Gravity * .25f * Engine.DeltaTime);Speed.X = Calc.Approach(Speed.X, 0, RunAccel * .2f * Engine.DeltaTime);if (Speed.Length() < LaunchCancelThreshold)return StNormal;elsereturn StLaunch;}#endregion#region Summit Launch Stateprivate float summitLaunchTargetX;private float summitLaunchParticleTimer;public void SummitLaunch(float targetX){summitLaunchTargetX = targetX;StateMachine.State = StSummitLaunch;}private void SummitLaunchBegin(){wallBoostTimer = 0;Sprite.Play("launch");Speed = -Vector2.UnitY * DashSpeed;summitLaunchParticleTimer = .4f;}private int SummitLaunchUpdate(){summitLaunchParticleTimer -= Engine.DeltaTime;if (summitLaunchParticleTimer > 0 && Scene.OnInterval(.03f))level.ParticlesFG.Emit(BadelineBoost.P_Move, 1, Center, Vector2.One * 4);Facing = Facings.Right;MoveTowardsX(summitLaunchTargetX, 20 * Engine.DeltaTime);Speed = -Vector2.UnitY * DashSpeed;if (level.OnInterval(0.2f))level.Add(Engine.Pooler.Create<SpeedRing>().Init(Center, Calc.Down, Color.White));return StSummitLaunch;}public void StopSummitLaunch(){StateMachine.State = StNormal;Speed.Y = BounceSpeed;AutoJump = true;varJumpSpeed = Speed.Y;}#endregion#region Pickup Stateprivate IEnumerator PickupCoroutine(){Play(Sfxs.char_mad_crystaltheo_lift);Input.Rumble(RumbleStrength.Medium, RumbleLength.Short);Vector2 oldSpeed = Speed;float varJump = varJumpTimer;Speed = Vector2.Zero;Vector2 begin = Holding.Entity.Position - Position;Vector2 end = CarryOffsetTarget;Vector2 control = new Vector2(begin.X + Math.Sign(begin.X) * 2, CarryOffsetTarget.Y - 2);SimpleCurve curve = new SimpleCurve(begin, end, control);carryOffset = begin;var tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeInOut, .16f, true);tween.OnUpdate = (t) =>{carryOffset = curve.GetPoint(t.Eased);};Add(tween);yield return tween.Wait();Speed = oldSpeed;Speed.Y = Math.Min(Speed.Y, 0);varJumpTimer = varJump;StateMachine.State = StNormal;}#endregion#region Dream Dash Stateprivate DreamBlock dreamBlock;private SoundSource dreamSfxLoop;private bool dreamJump;private void DreamDashBegin(){if (dreamSfxLoop == null)Add(dreamSfxLoop = new SoundSource());Speed = DashDir * DreamDashSpeed;TreatNaive = true;Depth = Depths.PlayerDreamDashing;dreamDashCanEndTimer = DreamDashMinTime;Stamina = ClimbMaxStamina;dreamJump = false;Play(Sfxs.char_mad_dreamblock_enter);Loop(dreamSfxLoop, Sfxs.char_mad_dreamblock_travel);}private void DreamDashEnd(){Depth = Depths.Player;if (!dreamJump){AutoJump = true;AutoJumpTimer = 0;}if (!Inventory.NoRefills)RefillDash();RefillStamina();TreatNaive = false;if (dreamBlock != null){if (DashDir.X != 0){jumpGraceTimer = JumpGraceTime;dreamJump = true;}elsejumpGraceTimer = 0;dreamBlock.OnPlayerExit(this);dreamBlock = null;}Stop(dreamSfxLoop);Play(Sfxs.char_mad_dreamblock_exit);Input.Rumble(RumbleStrength.Medium, RumbleLength.Short);}private int DreamDashUpdate(){Input.Rumble(RumbleStrength.Light, RumbleLength.Medium);var oldPos = Position;NaiveMove(Speed * Engine.DeltaTime);if (dreamDashCanEndTimer > 0)dreamDashCanEndTimer -= Engine.DeltaTime;var block = CollideFirst<DreamBlock>();          if (block == null){if (DreamDashedIntoSolid()){if (SaveData.Instance.AssistMode && SaveData.Instance.Assists.Invincible){Position = oldPos;Speed *= -1;Play(Sfxs.game_assist_dreamblockbounce);}elseDie(Vector2.Zero);}else if (dreamDashCanEndTimer <= 0){Celeste.Freeze(.05f);if (Input.Jump.Pressed && DashDir.X != 0){dreamJump = true;Jump();}else{bool left = ClimbCheck(-1);bool right = ClimbCheck(1);if (Input.Grab.Check && (DashDir.Y >= 0 || DashDir.X != 0) && ((moveX == 1 && right) || (moveX == -1 && left))){Facing = (Facings)moveX;return StClimb;}}return StNormal;}}else{dreamBlock = block;if (Scene.OnInterval(0.1f))CreateTrail();//Displacement effectif (level.OnInterval(0.04f)){var displacement = level.Displacement.AddBurst(Center, .3f, 0f, 40f);displacement.WorldClipCollider = dreamBlock.Collider;displacement.WorldClipPadding = 2;}}return StDreamDash;}private bool DreamDashedIntoSolid(){if (CollideCheck<Solid>()){for (int x = 1; x <= DreamDashEndWiggle; x++){for (int xm = -1; xm <= 1; xm += 2){for (int y = 1; y <= DreamDashEndWiggle; y++){for (int ym = -1; ym <= 1; ym += 2){Vector2 add = new Vector2(x * xm, y * ym);if (!CollideCheck<Solid>(Position + add)){Position += add;return false;}}}}}return true;}elsereturn false;}#endregion#region Star Fly Stateprivate const float StarFlyTransformDeccel = 1000f;private const float StarFlyTime = 2f;private const float StarFlyStartSpeed = 250f;private const float StarFlyTargetSpeed = 140f;private const float StarFlyMaxSpeed = 190f;private const float StarFlyMaxLerpTime = 1f;private const float StarFlySlowSpeed = StarFlyTargetSpeed * .65f;private const float StarFlyAccel = 1000f;private const float StarFlyRotateSpeed = 320 * Calc.DtR;private const float StarFlyEndX = 160f;private const float StarFlyEndXVarJumpTime = .1f;private const float StarFlyEndFlashDuration = 0.5f;private const float StarFlyEndNoBounceTime = .2f;private const float StarFlyWallBounce = -.5f;private const float StarFlyMaxExitY = 0f;private const float StarFlyMaxExitX = 140;private const float StarFlyExitUp = -100;private Color starFlyColor = Calc.HexToColor("ffd65c");private BloomPoint starFlyBloom;private float starFlyTimer;private bool starFlyTransforming;private float starFlySpeedLerp;private Vector2 starFlyLastDir;private SoundSource starFlyLoopSfx;private SoundSource starFlyWarningSfx;public bool StartStarFly(){RefillStamina();if (StateMachine.State == StReflectionFall)return false;if (StateMachine.State == StStarFly){starFlyTimer = StarFlyTime;Sprite.Color = starFlyColor;Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium);}elseStateMachine.State = StStarFly;return true;}private void StarFlyBegin(){Sprite.Play(PlayerSprite.StartStarFly);starFlyTransforming = true;starFlyTimer = StarFlyTime;starFlySpeedLerp = 0;jumpGraceTimer = 0;if (starFlyBloom == null)Add(starFlyBloom = new BloomPoint(new Vector2(0, -6), 0f, 16));starFlyBloom.Visible = true;starFlyBloom.Alpha = 0f;Collider = starFlyHitbox;hurtbox = starFlyHurtbox;if (starFlyLoopSfx == null){Add(starFlyLoopSfx = new SoundSource());starFlyLoopSfx.DisposeOnTransition = false;Add(starFlyWarningSfx = new SoundSource());starFlyWarningSfx.DisposeOnTransition = false;}starFlyLoopSfx.Play(Sfxs.game_06_feather_state_loop, "feather_speed", 1);starFlyWarningSfx.Stop();}private void StarFlyEnd(){Play(Sfxs.game_06_feather_state_end);starFlyWarningSfx.Stop();starFlyLoopSfx.Stop();Hair.DrawPlayerSpriteOutline = false;Sprite.Color = Color.White;level.Displacement.AddBurst(Center, 0.25f, 8, 32);starFlyBloom.Visible = false;Sprite.HairCount = startHairCount;StarFlyReturnToNormalHitbox();if (StateMachine.State != StDash)level.Particles.Emit(FlyFeather.P_Boost, 12, Center, Vector2.One * 4, (-Speed).Angle());}private void StarFlyReturnToNormalHitbox(){Collider = normalHitbox;hurtbox = normalHurtbox;if (CollideCheck<Solid>()){Vector2 start = Position;//Try moving upY -= (normalHitbox.Bottom - starFlyHitbox.Bottom);if (CollideCheck<Solid>())Position = start;elsereturn;//Try ducking and moving upDucking = true;Y -= (duckHitbox.Bottom - starFlyHitbox.Bottom);if (CollideCheck<Solid>())Position = start;elsereturn;throw new Exception("Could not get out of solids when exiting Star Fly State!");}}private IEnumerator StarFlyCoroutine(){while (Sprite.CurrentAnimationID == PlayerSprite.StartStarFly)yield return null;while (Speed != Vector2.Zero)yield return null;yield return .1f;Sprite.Color = starFlyColor;Sprite.HairCount = 7;Hair.DrawPlayerSpriteOutline = true;level.Displacement.AddBurst(Center, 0.25f, 8, 32);starFlyTransforming = false;starFlyTimer = StarFlyTime;RefillDash();RefillStamina();//Speed boost{var dir = Input.Aim.Value;if (dir == Vector2.Zero)dir = Vector2.UnitX * (int)Facing;Speed = dir * StarFlyStartSpeed;starFlyLastDir = dir;level.Particles.Emit(FlyFeather.P_Boost, 12, Center, Vector2.One * 4, (-dir).Angle());}Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);level.DirectionalShake(starFlyLastDir);while (starFlyTimer > StarFlyEndFlashDuration)yield return null;starFlyWarningSfx.Play(Sfxs.game_06_feather_state_warning);}private int StarFlyUpdate(){starFlyBloom.Alpha = Calc.Approach(starFlyBloom.Alpha, 0.7f, Engine.DeltaTime * 2f);if (starFlyTransforming){Speed = Calc.Approach(Speed, Vector2.Zero, StarFlyTransformDeccel * Engine.DeltaTime);}else{//Movement{Vector2 aim = Input.Aim.Value;bool slow = false;if (aim == Vector2.Zero){slow = true;aim = starFlyLastDir;}//Figure out directionVector2 currentDir = Speed.SafeNormalize(Vector2.Zero);if (currentDir == Vector2.Zero)currentDir = aim;elsecurrentDir = currentDir.RotateTowards(aim.Angle(), StarFlyRotateSpeed * Engine.DeltaTime);starFlyLastDir = currentDir;//Figure out max speedfloat maxSpeed;if (slow){starFlySpeedLerp = 0;maxSpeed = StarFlySlowSpeed;}else if (currentDir != Vector2.Zero && Vector2.Dot(currentDir, aim) >= .45f){starFlySpeedLerp = Calc.Approach(starFlySpeedLerp, 1, Engine.DeltaTime / StarFlyMaxLerpTime);maxSpeed = MathHelper.Lerp(StarFlyTargetSpeed, StarFlyMaxSpeed, starFlySpeedLerp);}else{starFlySpeedLerp = 0;maxSpeed = StarFlyTargetSpeed;}starFlyLoopSfx.Param("feather_speed", slow ? 0 : 1);//Approach max speedfloat currentSpeed = Speed.Length();currentSpeed = Calc.Approach(currentSpeed, maxSpeed, StarFlyAccel * Engine.DeltaTime);//Set speedSpeed = currentDir * currentSpeed;//Particlesif (level.OnInterval(.02f))level.Particles.Emit(FlyFeather.P_Flying, 1, Center, Vector2.One * 2, (-Speed).Angle());}//Jump cancellingif (Input.Jump.Pressed){if (OnGround(3)){Jump();return StNormal;}else if (WallJumpCheck(-1)){WallJump(1);return StNormal;}else if (WallJumpCheck(1)){WallJump(-1);return StNormal;}}//Grab cancellingif (Input.Grab.Check){if (Input.MoveX.Value != -1 && ClimbCheck(1)){Facing = Facings.Right;return StClimb;}else if (Input.MoveX.Value != 1 && ClimbCheck(-1)){Facing = Facings.Left;return StClimb;}}//Dash cancellingif (CanDash)return StartDash();//TimerstarFlyTimer -= Engine.DeltaTime;if (starFlyTimer <= 0){if (Input.MoveY.Value == -1)Speed.Y = StarFlyExitUp;if (Input.MoveY.Value < 1){varJumpSpeed = Speed.Y;AutoJump = true;AutoJumpTimer = 0;varJumpTimer = VarJumpTime;}if (Speed.Y > StarFlyMaxExitY)Speed.Y = StarFlyMaxExitY;if (Math.Abs(Speed.X) > StarFlyMaxExitX)Speed.X = StarFlyMaxExitX * Math.Sign(Speed.X);Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium);return StNormal;}// Flicker at endif (starFlyTimer < StarFlyEndFlashDuration && Scene.OnInterval(0.05f)){if (Sprite.Color == starFlyColor)Sprite.Color = NormalHairColor;elseSprite.Color = starFlyColor;}}return StStarFly;}#endregion#region Cassette Fly Stateprivate SimpleCurve cassetteFlyCurve;private float cassetteFlyLerp;public void StartCassetteFly(Vector2 targetPosition, Vector2 control){StateMachine.State = StCassetteFly;cassetteFlyCurve = new SimpleCurve(Position, targetPosition, control);cassetteFlyLerp = 0;Speed = Vector2.Zero;}private void CassetteFlyBegin(){Sprite.Play("bubble");Sprite.Y += 5;}private void CassetteFlyEnd(){}private int CassetteFlyUpdate(){return StCassetteFly;}private IEnumerator CassetteFlyCoroutine(){level.CanRetry = false;level.FormationBackdrop.Display = true;level.FormationBackdrop.Alpha = 0.5f;Sprite.Scale = Vector2.One * 1.25f;Depth = Depths.FormationSequences;yield return 0.4f;while (cassetteFlyLerp < 1f){if (level.OnInterval(.03f))level.Particles.Emit(P_CassetteFly, 2, Center, Vector2.One * 4);cassetteFlyLerp = Calc.Approach(cassetteFlyLerp, 1f, 1.6f * Engine.DeltaTime);Position = cassetteFlyCurve.GetPoint(Ease.SineInOut(cassetteFlyLerp));level.Camera.Position = CameraTarget;yield return null;}Position = cassetteFlyCurve.End;Sprite.Scale = Vector2.One * 1.25f;Sprite.Y -= 5;Sprite.Play(PlayerSprite.FallFast);yield return 0.2f;level.CanRetry = true;level.FormationBackdrop.Display = false;level.FormationBackdrop.Alpha = 0.5f;StateMachine.State = StNormal;Depth = Depths.Player;yield break;}#endregion#region Attract Stateprivate Vector2 attractTo;public void StartAttract(Vector2 attractTo){this.attractTo = Calc.Round(attractTo);StateMachine.State = StAttract;}private void AttractBegin(){Speed = Vector2.Zero;}private void AttractEnd(){}private int AttractUpdate(){if (Vector2.Distance(attractTo, ExactPosition) <= 1.5f){Position = attractTo;ZeroRemainderX();ZeroRemainderY();}else{Vector2 at = Calc.Approach(ExactPosition, attractTo, 200 * Engine.DeltaTime);MoveToX(at.X);MoveToY(at.Y);}return StAttract;}public bool AtAttractTarget{get{return StateMachine.State == StAttract && ExactPosition == attractTo;}}#endregion#region Dummy Statepublic bool DummyMoving = false;public bool DummyGravity = true;public bool DummyFriction = true;public bool DummyMaxspeed = true;private void DummyBegin(){DummyMoving = false;DummyGravity = true;DummyAutoAnimate = true;}private int DummyUpdate(){if (CanUnDuck)Ducking = false;// gravityif (!onGround && DummyGravity){float mult = (Math.Abs(Speed.Y) < HalfGravThreshold && (Input.Jump.Check || AutoJump)) ? .5f : 1f;if (level.InSpace)mult *= SpacePhysicsMult;Speed.Y = Calc.Approach(Speed.Y, MaxFall, Gravity * mult * Engine.DeltaTime);}// variable jumpingif (varJumpTimer > 0){if (AutoJump || Input.Jump.Check)Speed.Y = Math.Min(Speed.Y, varJumpSpeed);elsevarJumpTimer = 0;}if (!DummyMoving){if (Math.Abs(Speed.X) > MaxRun && DummyMaxspeed)Speed.X = Calc.Approach(Speed.X, MaxRun * Math.Sign(Speed.X), RunAccel * 2.5f * Engine.DeltaTime);if (DummyFriction)Speed.X = Calc.Approach(Speed.X, 0, RunAccel * Engine.DeltaTime);}//Spriteif (DummyAutoAnimate){if (onGround){if (Speed.X == 0)Sprite.Play("idle");elseSprite.Play(PlayerSprite.Walk);}else{if (Speed.Y < 0)Sprite.Play(PlayerSprite.JumpSlow);elseSprite.Play(PlayerSprite.FallSlow);}}return StDummy;}public IEnumerator DummyWalkTo(float x, bool walkBackwards = false, float speedMultiplier = 1f, bool keepWalkingIntoWalls = false){StateMachine.State = StDummy;if (Math.Abs(X - x) > 4 && !Dead){DummyMoving = true;if (walkBackwards){Sprite.Rate = -1;Facing = (Facings)Math.Sign(X - x);}else{Facing = (Facings)Math.Sign(x - X);}while (Math.Abs(x - X) > 4 && Scene != null && (keepWalkingIntoWalls || !CollideCheck<Solid>(Position + Vector2.UnitX * Math.Sign(x - X)))){Speed.X = Calc.Approach(Speed.X, Math.Sign(x - X) * WalkSpeed * speedMultiplier, RunAccel * Engine.DeltaTime);yield return null;}Sprite.Rate = 1;Sprite.Play(PlayerSprite.Idle);DummyMoving = false;}}public IEnumerator DummyWalkToExact(int x, bool walkBackwards = false, float speedMultiplier = 1f){StateMachine.State = StDummy;if (X != x){DummyMoving = true;if (walkBackwards){Sprite.Rate = -1;Facing = (Facings)Math.Sign(X - x);}else{Facing = (Facings)Math.Sign(x - X);}var last = Math.Sign(X - x);while (X != x && !CollideCheck<Solid>(Position + new Vector2((int)Facing, 0))){Speed.X = Calc.Approach(Speed.X, Math.Sign(x - X) * WalkSpeed * speedMultiplier, RunAccel * Engine.DeltaTime);// handle case where we overstepvar next = Math.Sign(X - x);if (next != last){X = x;break;}last = next;yield return null;}Speed.X = 0;Sprite.Rate = 1;Sprite.Play(PlayerSprite.Idle);DummyMoving = false;}}public IEnumerator DummyRunTo(float x, bool fastAnim = false){StateMachine.State = StDummy;if (Math.Abs(X - x) > 4){DummyMoving = true;if (fastAnim)Sprite.Play(PlayerSprite.RunFast);else if (!Sprite.LastAnimationID.StartsWith("run"))Sprite.Play(PlayerSprite.RunSlow);Facing = (Facings)Math.Sign(x - X);while (Math.Abs(X - x) > 4){Speed.X = Calc.Approach(Speed.X, Math.Sign(x - X) * MaxRun, RunAccel * Engine.DeltaTime);yield return null;}Sprite.Play(PlayerSprite.Idle);DummyMoving = false;}}#endregion#region Frozen Stateprivate int FrozenUpdate(){return StFrozen;}#endregion#region Temple Fall Stateprivate int TempleFallUpdate(){Facing = Facings.Right;if (!onGround){var center = level.Bounds.Left + 160;int mX;if (Math.Abs(center - X) > 4)mX = Math.Sign(center - X);elsemX = 0;Speed.X = Calc.Approach(Speed.X, MaxRun * .6f * mX, RunAccel * .5f * AirMult * Engine.DeltaTime);}if (!onGround && DummyGravity)Speed.Y = Calc.Approach(Speed.Y, MaxFall * 2, Gravity * 0.25f * Engine.DeltaTime);return StTempleFall;}private IEnumerator TempleFallCoroutine(){Sprite.Play(PlayerSprite.FallFast);while (!onGround)yield return null;Play(Sfxs.char_mad_mirrortemple_landing);if (Dashes <= 1)Sprite.Play(PlayerSprite.LandInPose);elseSprite.Play(PlayerSprite.Idle);Sprite.Scale.Y = 0.7f;Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);level.DirectionalShake(new Vector2(0, 1f), 0.5f);Speed.X = 0;level.Particles.Emit(P_SummitLandA, 12, BottomCenter, Vector2.UnitX * 3, Calc.Up);level.Particles.Emit(P_SummitLandB, 8, BottomCenter - Vector2.UnitX * 2, Vector2.UnitX * 2, Calc.Left + 15 * Calc.DtR);level.Particles.Emit(P_SummitLandB, 8, BottomCenter + Vector2.UnitX * 2, Vector2.UnitX * 2, Calc.Right - 15 * Calc.DtR);for (var p = 0f; p < 1; p += Engine.DeltaTime)yield return null;StateMachine.State = StNormal;}#endregion#region Reflection Fall Stateprivate void ReflectionFallBegin(){IgnoreJumpThrus = true;}private void ReflectionFallEnd(){FallEffects.Show(false);IgnoreJumpThrus = false;}private int ReflectionFallUpdate(){Facing = Facings.Right;if (Scene.OnInterval(.05f)){wasDashB = true;CreateTrail();}// fallif (CollideCheck<Water>())Speed.Y = Calc.Approach(Speed.Y, -20f, 400f * Engine.DeltaTime);elseSpeed.Y = Calc.Approach(Speed.Y, MaxFall * 2, Gravity * 0.25f * Engine.DeltaTime);// remove feathersvar feathers = Scene.Tracker.GetEntities<FlyFeather>();foreach (var feather in feathers)feather.RemoveSelf();// smash crystal spinnersvar hit = Scene.CollideFirst<CrystalStaticSpinner>(new Rectangle((int)(X - 6), (int)(Y - 6), 12, 12));if (hit != null){hit.Destroy();level.Shake();Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium);Celeste.Freeze(0.01f);}return StReflectionFall;}private IEnumerator ReflectionFallCoroutine(){Sprite.Play(PlayerSprite.FallBig);level.StartCutscene(OnReflectionFallSkip);// wait a bit before enteringfor (float t = 0f; t < 2f; t += Engine.DeltaTime){Speed.Y = 0f;yield return null;}// start falling at max speedFallEffects.Show(true);Speed.Y = MaxFall * 2f;// wait for waiterwhile (!CollideCheck<Water>())yield return null;Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);FallEffects.Show(false);Sprite.Play("bigFallRecover");level.Session.Audio.Music.Event = Sfxs.music_reflection_main;level.Session.Audio.Apply();level.EndCutscene();yield return 1.2f;StateMachine.State = StNormal;}private void OnReflectionFallSkip(Level level){level.OnEndOfFrame += () =>{level.Remove(this);level.UnloadLevel();level.Session.Level = "00";level.Session.RespawnPoint = level.GetSpawnPoint(new Vector2(level.Bounds.Left, level.Bounds.Bottom));level.LoadLevel(IntroTypes.None);FallEffects.Show(false);level.Session.Audio.Music.Event = Sfxs.music_reflection_main;level.Session.Audio.Apply();};}#endregion#region Intro Walk Stateprivate Facings IntroWalkDirection;public IEnumerator IntroWalkCoroutine(){var start = Position;if (IntroWalkDirection == Facings.Right){X = level.Bounds.Left - 16;Facing = Facings.Right;}else{X = level.Bounds.Right + 16;Facing = Facings.Left;}yield return .3f;Sprite.Play(PlayerSprite.RunSlow);while (Math.Abs(X - start.X) > 2 && !CollideCheck<Solid>(Position + new Vector2((int)Facing, 0))){MoveTowardsX(start.X, 64 * Engine.DeltaTime);yield return null;}Position = start;Sprite.Play(PlayerSprite.Idle);yield return .2f;StateMachine.State = StNormal;}#endregion#region Intro Jump Stateprivate IEnumerator IntroJumpCoroutine(){var start = Position;var wasSummitJump = StateMachine.PreviousState == StSummitLaunch;Depth = Depths.Top;Facing = Facings.Right;if (!wasSummitJump){Y = level.Bounds.Bottom + 16;yield return .5f;}else{start.Y = level.Bounds.Bottom - 24;MoveToX((int)Math.Round(X / 8) * 8);}// move up{if (!wasSummitJump)Sprite.Play(PlayerSprite.JumpSlow);while (Y > start.Y - 8){Y += -120 * Engine.DeltaTime;yield return null;}Speed.Y = -100;}// slow down{while (Speed.Y < 0){Speed.Y += Engine.DeltaTime * 800f;yield return null;}Speed.Y = 0;if (wasSummitJump){yield return 0.2f;Play(Sfxs.char_mad_summit_areastart);Sprite.Play("launchRecover");yield return 0.1f;}elseyield return 0.1f;}// fall down{if (!wasSummitJump)Sprite.Play(PlayerSprite.FallSlow);while (!onGround){Speed.Y += Engine.DeltaTime * 800f;yield return null;}}// land{if (StateMachine.PreviousState != StSummitLaunch)Position = start;Depth = Depths.Player;level.DirectionalShake(Vector2.UnitY);Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);}if (wasSummitJump){level.Particles.Emit(P_SummitLandA, 12, BottomCenter, Vector2.UnitX * 3, Calc.Up);level.Particles.Emit(P_SummitLandB, 8, BottomCenter - Vector2.UnitX * 2, Vector2.UnitX * 2, Calc.Left + 15 * Calc.DtR);level.Particles.Emit(P_SummitLandB, 8, BottomCenter + Vector2.UnitX * 2, Vector2.UnitX * 2, Calc.Right - 15 * Calc.DtR);level.ParticlesBG.Emit(P_SummitLandC, 30, BottomCenter, Vector2.UnitX * 5);yield return 0.35f;for (int i = 0; i < Hair.Nodes.Count; i++)Hair.Nodes[i] = new Vector2(0, 2 + i);}StateMachine.State = StNormal;}#endregion#region Intro Wake Up Stateprivate IEnumerator IntroWakeUpCoroutine(){Sprite.Play("asleep");yield return .5f;yield return Sprite.PlayRoutine("wakeUp");yield return .2f;StateMachine.State = StNormal;}#endregion#region Intro Respawn Stateprivate Tween respawnTween;private void IntroRespawnBegin(){Play(Sfxs.char_mad_revive);Depth = Depths.Top;introEase = 1f;Vector2 from = Position;const float pad = 40;from.X = MathHelper.Clamp(from.X, level.Bounds.Left + pad, level.Bounds.Right - pad);from.Y = MathHelper.Clamp(from.Y, level.Bounds.Top + pad, level.Bounds.Bottom - pad);deadOffset = from;from = from - Position;respawnTween = Tween.Create(Tween.TweenMode.Oneshot, null, .6f, true);respawnTween.OnUpdate = (t) =>{deadOffset = Vector2.Lerp(from, Vector2.Zero, t.Eased);introEase = 1 - t.Eased; };respawnTween.OnComplete = (t) =>{if (StateMachine.State == StIntroRespawn){StateMachine.State = StNormal;Sprite.Scale = new Vector2(1.5f, .5f);}};Add(respawnTween);}private void IntroRespawnEnd(){Depth = Depths.Player;deadOffset = Vector2.Zero;Remove(respawnTween);respawnTween = null;}#endregion#region Bird Dash Tutorialprivate void BirdDashTutorialBegin(){DashBegin();Play(Sfxs.char_mad_dash_red_right);Sprite.Play(PlayerSprite.Dash);}private int BirdDashTutorialUpdate(){return StBirdDashTutorial;}private IEnumerator BirdDashTutorialCoroutine(){yield return null;CreateTrail();Add(Alarm.Create(Alarm.AlarmMode.Oneshot, CreateTrail, 0.08f, true));Add(Alarm.Create(Alarm.AlarmMode.Oneshot, CreateTrail, DashTime, true));var aim = new Vector2(1, -1).SafeNormalize();Facing = Facings.Right;Speed = aim * DashSpeed;DashDir = aim;SceneAs<Level>().DirectionalShake(DashDir, .2f);SlashFx.Burst(Center, DashDir.Angle());for (float time = 0; time < DashTime; time += Engine.DeltaTime){if (Speed != Vector2.Zero && level.OnInterval(0.02f))level.ParticlesFG.Emit(P_DashA, Center + Calc.Random.Range(Vector2.One * -2, Vector2.One * 2), DashDir.Angle());yield return null;}AutoJump = true;AutoJumpTimer = 0;if (DashDir.Y <= 0)Speed = DashDir * EndDashSpeed;if (Speed.Y < 0)Speed.Y *= EndDashUpMult;Sprite.Play(PlayerSprite.FallFast);var climbing = false;while (!OnGround() && !climbing){Speed.Y = Calc.Approach(Speed.Y, MaxFall, Gravity * Engine.DeltaTime);if (CollideCheck<Solid>(Position + new Vector2(1, 0)))climbing = true;if (Top > level.Bounds.Bottom){level.CancelCutscene();Die(Vector2.Zero);}yield return null;}if (climbing){Sprite.Play(PlayerSprite.WallSlide);Dust.Burst(Position + new Vector2(4, -6), Calc.Angle(new Vector2(-4, 0)), 1);Speed.Y = 0;yield return 0.2f;Sprite.Play(PlayerSprite.ClimbUp);while (CollideCheck<Solid>(Position + new Vector2(1, 0))){Y += ClimbUpSpeed * Engine.DeltaTime;yield return null;}Play(Sfxs.char_mad_climb_ledge);Sprite.Play(PlayerSprite.JumpFast);Speed.Y = JumpSpeed;while (!OnGround()){Speed.Y = Calc.Approach(Speed.Y, MaxFall, Gravity * Engine.DeltaTime);Speed.X = 20f;yield return null;}Speed.X = 0;Speed.Y = 0;Sprite.Play(PlayerSprite.Walk);for (float time = 0f; time < 0.5f; time += Engine.DeltaTime){X += 32 * Engine.DeltaTime;yield return null;}Sprite.Play(PlayerSprite.Tired);}else{Sprite.Play(PlayerSprite.Tired);Speed.Y = 0;while (Speed.X != 0){Speed.X = Calc.Approach(Speed.X, 0, 240 * Engine.DeltaTime);if (Scene.OnInterval(0.04f))Dust.Burst(BottomCenter + new Vector2(0, -2), Calc.UpLeft);yield return null;}}}#endregion#region Chaser State Trackingpublic FMOD.Studio.EventInstance Play(string sound, string param = null, float value = 0){AddChaserStateSound(sound, param, value);return Audio.Play(sound, Center, param, value);}public void Loop(SoundSource sfx, string sound){AddChaserStateSound(sound, null, 0, ChaserStateSound.Actions.Loop);sfx.Play(sound);}public void Stop(SoundSource sfx){if (sfx.Playing){AddChaserStateSound(sfx.EventName, null, 0, ChaserStateSound.Actions.Stop);sfx.Stop();}}private void AddChaserStateSound(string sound, ChaserStateSound.Actions action){AddChaserStateSound(sound, null, 0, action);}private void AddChaserStateSound(string sound, string param = null, float value = 0, ChaserStateSound.Actions action = ChaserStateSound.Actions.Oneshot){string eventName = null;Sfxs.MadelineToBadelineSound.TryGetValue(sound, out eventName);if (eventName != null)activeSounds.Add(new ChaserStateSound() { Event = eventName, Parameter = param, ParameterValue = value, Action = action });}public struct ChaserStateSound{public enum Actions{Oneshot,Loop,Stop}public string Event;public string Parameter;public float ParameterValue;public Actions Action;}public struct ChaserState{public Vector2 Position;public float TimeStamp;public string Animation;public Facings Facing;public bool OnGround;public Color HairColor;public int Depth;private ChaserStateSound sound0;private ChaserStateSound sound1;private ChaserStateSound sound2;private ChaserStateSound sound3;private ChaserStateSound sound4;public int Sounds;public ChaserState(Player player){Position = player.Position;TimeStamp = player.Scene.TimeActive;Animation = player.Sprite.CurrentAnimationID;Facing = player.Facing;OnGround = player.onGround;HairColor = player.Hair.Color;Depth = player.Depth;var sounds = player.activeSounds;Sounds = Math.Min(5, sounds.Count);sound0 = Sounds > 0 ? sounds[0] : default(ChaserStateSound);sound1 = Sounds > 1 ? sounds[1] : default(ChaserStateSound);sound2 = Sounds > 2 ? sounds[2] : default(ChaserStateSound);sound3 = Sounds > 3 ? sounds[3] : default(ChaserStateSound);sound4 = Sounds > 4 ? sounds[4] : default(ChaserStateSound);}public ChaserStateSound this[int index]{get{switch (index){case 0: return sound0;case 1: return sound1;case 2: return sound2;case 3: return sound3;case 4: return sound4;}return new ChaserStateSound();}}}#endregion}
}
? 2019 GitHub, Inc.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88656.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88656.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88656.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于大模型的鼻咽癌全周期預測及診療優化研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 國內外研究現狀 1.3 研究目標與創新點 二、大模型技術與鼻咽癌相關理論基礎 2.1 大模型技術概述 2.2 鼻咽癌疾病知識 2.3 大模型在醫學領域的應用 三、數據收集與預處理 3.1 數據來源 3.2 數據清洗 3.3 數據標注 3.4 數據標…

基于odoo17的設計模式詳解---訪問模式

大家好&#xff0c;我是你的Odoo技術伙伴。想象一下&#xff0c;我們有一個復雜的對象結構&#xff0c;比如一個由不同類型的訂單行&#xff08;銷售行、折扣行、備注行&#xff09;組成的銷售訂單。現在&#xff0c;我們需要對這個結構執行一些新的操作&#xff0c;比如&#…

使用langgraph 構建RAG 智能問答代理

RAG 智能問答代理&#xff1a; ? 支持用戶持續提問 ? 根據模型判斷是否需要查資料 ? 自動調用 PDF 檢索工具查找內容 ? 自動引用內容回答 ? 可以輸入 exit / quit 退出 下載需要的library pip install langchain-google-genai pip install langgraph pip install langchai…

零基礎搭建監控系統:Grafana+InfluxDB 保姆級教程,5分鐘可視化服務器性能!?

??你是否遇到過這些問題&#xff1f;??服務器突然卡頓&#xff0c;卻找不到性能瓶頸需要手動查看日志&#xff0c;無法實時監控數據運維報表全靠截圖拼接&#xff0c;領導直呼“太原始”?今天教你用GrafanaInfluxDB構建企業級監控系統&#xff0c;從此告別“盲人摸象”式運…

Java中的wait和notify、Condition接口的使用

Java中的wait和notify機制基礎概念在Java中&#xff0c;wait()和notify()是Object類的原生方法&#xff0c;用于實現線程間的協作&#xff1a;wait()使當前線程釋放對象鎖并進入等待狀態必須在synchronized代碼塊內調用語法&#xff1a;obj.wait() 或 obj.wait(long timeout)線…

【Modern C++ Part9】Prefer-alias-declarations-to-typedefs

條款9&#xff1a;優先使用聲明別名而不是typedef 我有信心說&#xff0c;大家都同意使用STL容器是個好的想法&#xff0c;并且我希望&#xff0c;條款18可以說服你使用std::unique_ptr也是個好想法&#xff0c;但是我想絕對我們中間沒有人喜歡寫像這樣std::unique_ptr<std:…

STM32第二十一天定時器TIM

1 定時器基礎知識a:上來說就是用來定時的機器&#xff0c;是存在于STM32單片機中的一個外設。STM32總共有8個定時器&#xff0c;分別是2個高級定時器(TIM1、TIM8)&#xff0c;4個通用定時器 (TIM2、TIM3、TIM4、TIM5) 和2個基本定時器 (TIM6、TIM7)&#xff0c;如下圖所示&…

鼎捷T100程序開發:校驗程序詳解

校驗程序概述 T100系統校驗程序需要確保系統數據的準確性、完整性和一致性&#xff0c;相當于企業信息系統的"健康體檢醫生"。它通過預設規則掃描系統數據&#xff0c;識別異常和錯誤&#xff0c;確保業務運行可靠。通過持續完善的校驗機制&#xff0c;企業能夠構建數…

BaseDao 通用查詢方法設計與實現

BaseDao 通用查詢方法設計與實現 一、通用查詢方法設計思路 1. 核心查詢功能矩陣查詢類型方法名功能說明復雜度主鍵查詢findById()根據主鍵獲取單個實體?全量查詢findAll()獲取全部實體?條件查詢findByCondition()動態條件查詢???分頁查詢findPage()分頁結果集????排序…

llama.cpp gguf主要量化方法

量化是一種通過降低模型參數的表示精度來減少模型的大小和計算存儲需求的方法&#xff0c;如把單精度fp32轉化為int8來減少存儲和計算成本。 常見的是線性量化&#xff0c;公式 r S(q-Z)&#xff0c;將實數值r映射為量化的整數值q&#xff0c;其中縮放因子S和零點Z根據參數分…

汽車級MCU選型新方向:eVTOL垂槳控制監控芯片的替代選型技術分析

摘要&#xff1a;隨著eVTOL&#xff08;電動垂直起降航空器&#xff09;領域的蓬勃發展&#xff0c;對于高性能、高可靠性的垂槳控制監控芯片的需求日益迫切。本文旨在深入探討汽車級MCU&#xff08;微控制單元&#xff09;在這一新興領域的應用潛力&#xff0c;以國科安芯推出…

Deepoc具身智能大模型:送餐機器人如何學會“讀心術”

Deepoc具身智能大模型&#xff1a;送餐機器人如何學會“讀心術”深夜十點的商場火鍋店&#xff0c;一臺銀色機器人正穿越喧鬧的人群。當它感知到奔跑的兒童突然變向&#xff0c;驅動輪立即反向微調0.3度&#xff1b;托盤上的牛油鍋底因顧客推椅產生晃動&#xff0c;平衡系統瞬間…

學習設計模式《十七》——狀態模式

一、基礎概念 狀態模式的本質是【根據狀態來分離和選擇行為】。 狀態模式的定義&#xff1a;允許一個對象在其內部狀態改變時改變它的行為&#xff1b;對象看起來似乎修改了它的類。 認識狀態模式序號認識狀態模式說明1狀態和行為通常指的是對象實例的屬性的值&#xff1b;而行…

python的婚紗影樓管理系統

前端開發框架:vue.js 數據庫 mysql 版本不限 后端語言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 數據庫工具&#xff1a;Navicat/SQLyog等都可以 隨著婚紗…

濾波電路Multisim電路仿真實驗匯總——硬件工程師筆記

目錄 1 濾波電路基礎知識 1.1 濾波電路的分類 1.1.1 按頻率選擇性分類 1.1.2 按實現方式分類 1.2 濾波電路的設計 1.2.1 確定濾波器類型 1.2.2 計算截止頻率 1.2.3 選擇濾波階數 1.2.4 考慮元件參數 1.2.5 仿真驗證 1.3 濾波電路的應用 1.3.1 電源濾波 1.3.2 音頻…

C++隨機打亂函數:簡化源碼與原理深度剖析

文章目錄一、Fisher-Yates洗牌算法核心原理二、std::random_shuffle簡化實現與缺陷分析簡化源碼&#xff08;核心邏輯&#xff09;原理層面的致命缺陷三、std::shuffle的現代改進與實現簡化源碼&#xff08;核心邏輯&#xff09;原理層面的關鍵改進四、隨機數生成器工作原理URB…

DBeaver連接MySQL8.0報錯Public Key Retrieval is not allowed

DBeaver 鏈接本地mysql8.0服務報錯Public Key Retrieval is not allowed為什么會出現這個錯誤&#xff1f;MySQL 8.0 默認使用新的認證插件&#xff1a;caching_sha2_password某些客戶端&#xff08;比如老版本的 JDBC 驅動或配置不當的 DBeaver&#xff09;在連接時&#xff0…

SpringBoot系列—統一功能處理(攔截器)

上篇文章&#xff1a; SpringBoot系列—MyBatis-plushttps://blog.csdn.net/sniper_fandc/article/details/148979284?fromshareblogdetail&sharetypeblogdetail&sharerId148979284&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目錄 1 攔…

《匯編語言:基于X86處理器》第7章 整數運算(3)

本章將介紹匯編語言最大的優勢之一:基本的二進制移位和循環移位技術。實際上&#xff0c;位操作是計算機圖形學、數據加密和硬件控制的固有部分。實現位操作的指令是功能強大的工具&#xff0c;但是高級語言只能實現其中的一部分&#xff0c;并且由于高級語言要求與平臺無關&am…

應用筆記|數字化儀在醫學SS-OCT中的應用

引言近些年來&#xff0c;OCT&#xff08;光學相干斷層掃描&#xff0c;Optical Coherence Tomography&#xff09;作為一種非破壞性3D光學成像技術逐漸在醫學眼科設備中流行起來。OCT可提供實時一維深度或二維截面或三維立體的圖像&#xff0c;分辨率可達微米&#xff08;μm&…