|
| 1 | +using Celeste.Mod.Entities; |
| 2 | +using Microsoft.Xna.Framework; |
| 3 | +using Monocle; |
| 4 | +using System; |
| 5 | +using System.Collections; |
| 6 | + |
| 7 | +namespace Celeste.Mod.SpringCollab2020.Entities { |
| 8 | + [CustomEntity("SpringCollab2020/trollStrawberry")] |
| 9 | + public class TrollStrawberry : Entity { |
| 10 | + |
| 11 | + public EntityID ID; |
| 12 | + |
| 13 | + private Sprite sprite; |
| 14 | + |
| 15 | + public Follower Follower; |
| 16 | + |
| 17 | + private Wiggler wiggler; |
| 18 | + |
| 19 | + private Wiggler rotateWiggler; |
| 20 | + |
| 21 | + private BloomPoint bloom; |
| 22 | + |
| 23 | + private VertexLight light; |
| 24 | + |
| 25 | + private Tween lightTween; |
| 26 | + |
| 27 | + private float wobble; |
| 28 | + |
| 29 | + private Vector2 start; |
| 30 | + |
| 31 | + private float collectTimer; |
| 32 | + |
| 33 | + private bool collected; |
| 34 | + |
| 35 | + private bool flyingAway; |
| 36 | + |
| 37 | + private float flapSpeed; |
| 38 | + |
| 39 | + public bool ReturnHomeWhenLost = true; |
| 40 | + |
| 41 | + public bool Winged { |
| 42 | + get; |
| 43 | + private set; |
| 44 | + } |
| 45 | + |
| 46 | + private bool IsFirstStrawberry { |
| 47 | + get { |
| 48 | + for (int i = Follower.FollowIndex - 1; i >= 0; i--) { |
| 49 | + if (Follower.Leader.Followers[i].Entity is Strawberry strawberry && !strawberry.Golden) { |
| 50 | + return false; |
| 51 | + } |
| 52 | + } |
| 53 | + return true; |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + public TrollStrawberry(EntityData data, Vector2 offset, EntityID gid) { |
| 58 | + ID = gid; |
| 59 | + Position = (start = data.Position + offset); |
| 60 | + Winged = data.Bool("winged", false); |
| 61 | + Depth = -100; |
| 62 | + Collider = new Hitbox(14f, 14f, -7f, -7f); |
| 63 | + Add(new PlayerCollider(new Action<Player>(OnPlayer), null, null)); |
| 64 | + Add(new MirrorReflection()); |
| 65 | + Add(Follower = new Follower(ID, null, new Action(OnLoseLeader))); |
| 66 | + Follower.FollowDelay = 0.3f; |
| 67 | + if (Winged) { |
| 68 | + Add(new DashListener { |
| 69 | + OnDash = new Action<Vector2>(OnDash) |
| 70 | + }); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + public override void Added(Scene scene) { |
| 75 | + base.Added(scene); |
| 76 | + Add(sprite = GFX.SpriteBank.Create("strawberry")); |
| 77 | + if (Winged) { |
| 78 | + sprite.Play("flap", false, false); |
| 79 | + } |
| 80 | + sprite.OnFrameChange = new Action<string>(OnAnimate); |
| 81 | + Add(wiggler = Wiggler.Create(0.4f, 4f, delegate (float v) { |
| 82 | + sprite.Scale = Vector2.One * (1f + v * 0.35f); |
| 83 | + }, false, false)); |
| 84 | + Add(rotateWiggler = Wiggler.Create(0.5f, 4f, delegate (float v) { |
| 85 | + sprite.Rotation = v * 30f * 0.0174532924f; |
| 86 | + }, false, false)); |
| 87 | + Add(bloom = new BloomPoint(1f, 12f)); |
| 88 | + Add(light = new VertexLight(Color.White, 1f, 16, 24)); |
| 89 | + Add(lightTween = light.CreatePulseTween()); |
| 90 | + if ((scene as Level).Session.BloomBaseAdd > 0.1f) { |
| 91 | + bloom.Alpha *= 0.5f; |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + public override void Update() { |
| 96 | + if (!collected) { |
| 97 | + if (!Winged) { |
| 98 | + wobble += Engine.DeltaTime * 4f; |
| 99 | + sprite.Y = (bloom.Y = (light.Y = (float) Math.Sin(wobble) * 2f)); |
| 100 | + } |
| 101 | + int followIndex = Follower.FollowIndex; |
| 102 | + if (Follower.Leader != null && Follower.DelayTimer <= 0f && IsFirstStrawberry) { |
| 103 | + bool collectable = false; |
| 104 | + if (Follower.Leader.Entity is Player player && player.Scene != null && !player.StrawberriesBlocked) { |
| 105 | + collectable = player.OnSafeGround && player.StateMachine.State != 13; |
| 106 | + } |
| 107 | + if (collectable) { |
| 108 | + collectTimer += Engine.DeltaTime; |
| 109 | + if (collectTimer > 0.15f) { |
| 110 | + OnCollect(); |
| 111 | + } |
| 112 | + } else { |
| 113 | + collectTimer = Math.Min(collectTimer, 0f); |
| 114 | + } |
| 115 | + } else { |
| 116 | + if (followIndex > 0) { |
| 117 | + collectTimer = -0.15f; |
| 118 | + } |
| 119 | + if (Winged) { |
| 120 | + Y += flapSpeed * Engine.DeltaTime; |
| 121 | + if (flyingAway) { |
| 122 | + if (Y < (SceneAs<Level>().Bounds.Top - 16)) { |
| 123 | + RemoveSelf(); |
| 124 | + } |
| 125 | + } else { |
| 126 | + flapSpeed = Calc.Approach(flapSpeed, 20f, 170f * Engine.DeltaTime); |
| 127 | + if (Y < start.Y - 5f) { |
| 128 | + Y = start.Y - 5f; |
| 129 | + } else if (Y > start.Y + 5f) { |
| 130 | + Y = start.Y + 5f; |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + base.Update(); |
| 137 | + if (Follower.Leader != null && Scene.OnInterval(0.08f)) { |
| 138 | + SceneAs<Level>().ParticlesFG.Emit(Strawberry.P_Glow, Position + Calc.Random.Range(-Vector2.One * 6f, Vector2.One * 6f)); |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + private void OnDash(Vector2 dir) { |
| 143 | + if (!flyingAway && Winged) { |
| 144 | + Depth = -1000000; |
| 145 | + Add(new Coroutine(FlyAwayRoutine(), true)); |
| 146 | + flyingAway = true; |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + private void OnAnimate(string id) { |
| 151 | + if (!flyingAway && id == "flap" && sprite.CurrentAnimationFrame % 9 == 4) { |
| 152 | + Audio.Play("event:/game/general/strawberry_wingflap", Position); |
| 153 | + flapSpeed = -50f; |
| 154 | + } |
| 155 | + int pulseFrame; |
| 156 | + if (id == "flap") { |
| 157 | + pulseFrame = 25; |
| 158 | + } else { |
| 159 | + pulseFrame = 35; |
| 160 | + } |
| 161 | + if (sprite.CurrentAnimationFrame == pulseFrame) { |
| 162 | + lightTween.Start(); |
| 163 | + if (!collected && (CollideCheck<FakeWall>() || CollideCheck<Solid>())) { |
| 164 | + Audio.Play("event:/game/general/strawberry_pulse", Position); |
| 165 | + SceneAs<Level>().Displacement.AddBurst(Position, 0.6f, 4f, 28f, 0.1f, null, null); |
| 166 | + return; |
| 167 | + } |
| 168 | + Audio.Play("event:/game/general/strawberry_pulse", Position); |
| 169 | + SceneAs<Level>().Displacement.AddBurst(Position, 0.6f, 4f, 28f, 0.2f, null, null); |
| 170 | + } |
| 171 | + } |
| 172 | + |
| 173 | + public void OnPlayer(Player player) { |
| 174 | + if (Follower.Leader == null && !collected) { |
| 175 | + ReturnHomeWhenLost = true; |
| 176 | + if (Winged) { |
| 177 | + Level level = SceneAs<Level>(); |
| 178 | + Winged = false; |
| 179 | + sprite.Rate = 0f; |
| 180 | + Alarm.Set(this, Follower.FollowDelay, delegate { |
| 181 | + sprite.Rate = 1f; |
| 182 | + sprite.Play("idle", false, false); |
| 183 | + level.Particles.Emit(Strawberry.P_WingsBurst, 8, Position + new Vector2(8f, 0f), new Vector2(4f, 2f)); |
| 184 | + level.Particles.Emit(Strawberry.P_WingsBurst, 8, Position - new Vector2(8f, 0f), new Vector2(4f, 2f)); |
| 185 | + }, Alarm.AlarmMode.Oneshot); |
| 186 | + } |
| 187 | + Audio.Play("event:/game/general/strawberry_touch", Position); |
| 188 | + player.Leader.GainFollower(Follower); |
| 189 | + wiggler.Start(); |
| 190 | + Depth = -1000000; |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + public void OnCollect() { |
| 195 | + if (collected) { |
| 196 | + return; |
| 197 | + } |
| 198 | + int collectIndex = 0; |
| 199 | + collected = true; |
| 200 | + if (Follower.Leader != null) { |
| 201 | + Player p = Follower.Leader.Entity as Player; |
| 202 | + collectIndex = p.StrawberryCollectIndex; |
| 203 | + p.StrawberryCollectIndex++; |
| 204 | + p.StrawberryCollectResetTimer = 2.5f; |
| 205 | + Follower.Leader.LoseFollower(Follower); |
| 206 | + } |
| 207 | + Session session = (Scene as Level).Session; |
| 208 | + session.DoNotLoad.Add(ID); |
| 209 | + session.UpdateLevelStartDashes(); |
| 210 | + Add(new Coroutine(CollectRoutine(collectIndex), true)); |
| 211 | + } |
| 212 | + |
| 213 | + private IEnumerator FlyAwayRoutine() { |
| 214 | + rotateWiggler.Start(); |
| 215 | + flapSpeed = -200f; |
| 216 | + Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeOut, 0.25f, start: true); |
| 217 | + tween.OnUpdate = delegate (Tween t) { |
| 218 | + flapSpeed = MathHelper.Lerp(-200f, 0f, t.Eased); |
| 219 | + }; |
| 220 | + Add(tween); |
| 221 | + yield return 0.1f; |
| 222 | + Audio.Play("event:/game/general/strawberry_laugh", Position); |
| 223 | + yield return 0.2f; |
| 224 | + if (!Follower.HasLeader) { |
| 225 | + Audio.Play("event:/game/general/strawberry_flyaway", Position); |
| 226 | + } |
| 227 | + tween = Tween.Create(Tween.TweenMode.Oneshot, null, 0.5f, start: true); |
| 228 | + tween.OnUpdate = delegate (Tween t) { |
| 229 | + flapSpeed = MathHelper.Lerp(0f, -200f, t.Eased); |
| 230 | + }; |
| 231 | + Add(tween); |
| 232 | + } |
| 233 | + |
| 234 | + private IEnumerator CollectRoutine(int collectIndex) { |
| 235 | + Tag = Tags.TransitionUpdate; |
| 236 | + Depth = -2000010; |
| 237 | + Audio.Play("event:/game/general/seed_poof", Position); |
| 238 | + Collidable = false; |
| 239 | + sprite.Scale = Vector2.One * 2f; |
| 240 | + yield return 0.05f; |
| 241 | + Input.Rumble(RumbleStrength.Medium, RumbleLength.Medium); |
| 242 | + for (int i = 0; i < 6; i++) { |
| 243 | + float num = Calc.Random.NextFloat((float) Math.PI * 2f); |
| 244 | + SceneAs<Level>().ParticlesFG.Emit(StrawberrySeed.P_Burst, 1, Position + Calc.AngleToVector(num, 4f), Vector2.Zero, num); |
| 245 | + } |
| 246 | + Visible = false; |
| 247 | + sprite.Play("collect"); |
| 248 | + while (sprite.Animating) { |
| 249 | + yield return null; |
| 250 | + } |
| 251 | + //Scene.Add(new StrawberryPoints(Position, isGhostBerry, collectIndex, Moon)); |
| 252 | + RemoveSelf(); |
| 253 | + } |
| 254 | + |
| 255 | + private void OnLoseLeader() { |
| 256 | + if (!collected && ReturnHomeWhenLost) { |
| 257 | + Alarm.Set(this, 0.15f, delegate { |
| 258 | + Vector2 displacement = (start - Position).SafeNormalize(); |
| 259 | + float dist = Vector2.Distance(Position, start); |
| 260 | + float scaleFactor = Calc.ClampedMap(dist, 16f, 120f, 16f, 96f); |
| 261 | + Vector2 control = start + displacement * 16f + displacement.Perpendicular() * scaleFactor * Calc.Random.Choose(1, -1); |
| 262 | + SimpleCurve curve = new SimpleCurve(Position, start, control); |
| 263 | + Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.SineOut, MathHelper.Max(dist / 100f, 0.4f), start: true); |
| 264 | + tween.OnUpdate = delegate (Tween f) { |
| 265 | + Position = curve.GetPoint(f.Eased); |
| 266 | + }; |
| 267 | + tween.OnComplete = delegate { |
| 268 | + Depth = 0; |
| 269 | + }; |
| 270 | + Add(tween); |
| 271 | + }); |
| 272 | + } |
| 273 | + } |
| 274 | + } |
| 275 | +} |
0 commit comments