using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MLEM.Animations; using MLEM.Extended.Extensions; using MLEM.Extensions; using MLEM.Input; using MLEM.Startup; using MLEM.Textures; using MonoGame.Extended; using RectangleF = MLEM.Misc.RectangleF; namespace GreatSpringGameJam { public class Player : Entity { private static readonly UniformTextureAtlas PlayerTexture = new(MlemGame.LoadContent("Textures/Player"), 4, 1); public RectangleF Bounds => new(this.Position + new Vector2(4 / 16F, 0), new Vector2(9 / 16F, 1)); private readonly SpriteAnimationGroup animations; private bool walking; private bool onGround; private Vector2 velocity; private TimeSpan jumpTime; private bool facingRight; private HeldItem heldItem; public Player(Map map, Vector2 position) : base(map, position) { this.animations = new SpriteAnimationGroup(); this.animations.Add(new SpriteAnimation(0.15F, PlayerTexture[1], PlayerTexture[2], PlayerTexture[3], PlayerTexture[0]), () => this.walking); this.animations.Add(new SpriteAnimation(1, PlayerTexture[0]), () => !this.walking); } public override void Update(GameTime time) { base.Update(time); // input var move = 0F; if (MlemGame.Input.IsAnyDown(Keys.A, Keys.Left)) { move -= 0.04F; this.facingRight = false; } if (MlemGame.Input.IsAnyDown(Keys.D, Keys.Right)) { move += 0.04F; this.facingRight = true; } this.walking = move != 0; this.velocity.X += move; if (MlemGame.Input.IsAnyDown(Keys.Up, Keys.Space)) { // only start jumping if we just started pressing the buttons if (this.onGround && MlemGame.Input.IsAnyPressed(Keys.Up, Keys.Space)) this.jumpTime = TimeSpan.FromSeconds(0.3F); this.jumpTime -= time.ElapsedGameTime; if (this.jumpTime > TimeSpan.Zero) this.velocity.Y = -0.15F; } else { this.jumpTime = TimeSpan.Zero; } // movement and collisions this.Position += this.velocity; this.Collide(() => this.Bounds.ToExtended(), ref this.velocity, out this.onGround); this.velocity *= new Vector2(this.onGround ? 0.5F : 0.6F, 0.9F); this.velocity.Y += 0.02F; // item usage var rot = this.GetHeldRotation(); var rotVec = new Vector2(MathF.Cos(rot), MathF.Sin(rot)); if (MlemGame.Input.IsDown(MouseButton.Left)) { this.heldItem = HeldItem.SnowBlower; Random.NextUnitVector(out var vel); vel = vel * 0.03F + rotVec * 0.13F; this.Map.AddEntity(new SnowBlowerWind(this.Map, this.Position + Vector2.One / 2 + rotVec * 0.75F, vel)); } else if (MlemGame.Input.IsDown(MouseButton.Right)) { this.heldItem = HeldItem.WateringCan; if (Random.NextSingle() <= 0.45F) { Random.NextUnitVector(out var vel); vel = vel * 0.015F + rotVec * 0.03F; this.Map.AddEntity(new WaterDrop(this.Map, this.Position + Vector2.One / 2 + rotVec * 0.65F, vel)); } } this.animations.Update(time); } public override void Draw(GameTime time, SpriteBatch batch) { base.Draw(time, batch); // draw self var pos = this.Position * this.Map.TileSize; var effects = this.facingRight ? SpriteEffects.FlipHorizontally : SpriteEffects.None; batch.Draw(this.animations.CurrentRegion, pos, Color.White, 0, Vector2.Zero, 1, effects, 0); // draw held item var tex = this.heldItem switch { HeldItem.SnowBlower => StuffTexture[0, 0], HeldItem.WateringCan => StuffTexture[2, 0], _ => null }; var origin = new Vector2(tex.Width * 0.25F, tex.Height / 2); var rot = this.GetHeldRotation(); var flip = rot >= MathHelper.PiOver2 && rot <= MathHelper.Pi * 1.5F ? SpriteEffects.FlipVertically : SpriteEffects.None; batch.Draw(tex, pos + this.Map.TileSize / 2, Color.White, rot, origin, 1, flip, 0); } private float GetHeldRotation() { var myPos = GameImpl.Instance.Camera.ToCameraPos((this.Position + Vector2.One / 2) * this.Map.TileSize); var mousePos = MlemGame.Input.MousePosition.ToVector2(); var (x, y) = myPos - mousePos; return MathF.Atan2(y, x) + MathHelper.Pi; } private enum HeldItem { SnowBlower, WateringCan } } }