diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..91953df
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+bin/
+obj/
+/packages/
+riderModule.iml
+/_ReSharper.Caches/
+.idea
\ No newline at end of file
diff --git a/GreatSpringGameJam.sln b/GreatSpringGameJam.sln
new file mode 100644
index 0000000..c907b24
--- /dev/null
+++ b/GreatSpringGameJam.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreatSpringGameJam", "GreatSpringGameJam\GreatSpringGameJam.csproj", "{788D2084-3F31-4331-9870-B5F01F79E7B0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {788D2084-3F31-4331-9870-B5F01F79E7B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {788D2084-3F31-4331-9870-B5F01F79E7B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {788D2084-3F31-4331-9870-B5F01F79E7B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {788D2084-3F31-4331-9870-B5F01F79E7B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/GreatSpringGameJam/Content/Content.mgcb b/GreatSpringGameJam/Content/Content.mgcb
new file mode 100644
index 0000000..826bf32
--- /dev/null
+++ b/GreatSpringGameJam/Content/Content.mgcb
@@ -0,0 +1,31 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin
+/intermediateDir:obj
+/platform:DesktopGL
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+/reference:..\..\packages\monogame.extended.content.pipeline\3.8.0\tools\MonoGame.Extended.Content.Pipeline.dll
+
+#---------------------------------- Content ---------------------------------#
+
+#begin Maps/Level1.tmx
+/importer:TiledMapImporter
+/processor:TiledMapProcessor
+/build:Maps/Level1.tmx
+
+#begin Tilesets/World.tsx
+/importer:TiledMapTilesetImporter
+/processor:TiledMapTilesetProcessor
+/build:Tilesets/World.tsx
+
+#begin Textures/Player.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/Player.png
+
diff --git a/GreatSpringGameJam/Content/Contentless.json b/GreatSpringGameJam/Content/Contentless.json
new file mode 100644
index 0000000..805bbcc
--- /dev/null
+++ b/GreatSpringGameJam/Content/Contentless.json
@@ -0,0 +1,8 @@
+{
+ "exclude": [
+ "obj/",
+ "bin/",
+ "Palettes/",
+ "Tilesets/*.png"
+ ]
+}
\ No newline at end of file
diff --git a/GreatSpringGameJam/Content/Maps/Level1.tmx b/GreatSpringGameJam/Content/Maps/Level1.tmx
new file mode 100644
index 0000000..b4f18f4
--- /dev/null
+++ b/GreatSpringGameJam/Content/Maps/Level1.tmx
@@ -0,0 +1,38 @@
+
+
diff --git a/GreatSpringGameJam/Content/Palettes/skedd16-1x.png b/GreatSpringGameJam/Content/Palettes/skedd16-1x.png
new file mode 100644
index 0000000..1fb1957
Binary files /dev/null and b/GreatSpringGameJam/Content/Palettes/skedd16-1x.png differ
diff --git a/GreatSpringGameJam/Content/Textures/Player.aseprite b/GreatSpringGameJam/Content/Textures/Player.aseprite
new file mode 100644
index 0000000..b7895e6
Binary files /dev/null and b/GreatSpringGameJam/Content/Textures/Player.aseprite differ
diff --git a/GreatSpringGameJam/Content/Textures/Player.png b/GreatSpringGameJam/Content/Textures/Player.png
new file mode 100644
index 0000000..69161d1
Binary files /dev/null and b/GreatSpringGameJam/Content/Textures/Player.png differ
diff --git a/GreatSpringGameJam/Content/Tilesets/World.aseprite b/GreatSpringGameJam/Content/Tilesets/World.aseprite
new file mode 100644
index 0000000..4bf1ba8
Binary files /dev/null and b/GreatSpringGameJam/Content/Tilesets/World.aseprite differ
diff --git a/GreatSpringGameJam/Content/Tilesets/World.png b/GreatSpringGameJam/Content/Tilesets/World.png
new file mode 100644
index 0000000..a09c7fa
Binary files /dev/null and b/GreatSpringGameJam/Content/Tilesets/World.png differ
diff --git a/GreatSpringGameJam/Content/Tilesets/World.tsx b/GreatSpringGameJam/Content/Tilesets/World.tsx
new file mode 100644
index 0000000..825cfee
--- /dev/null
+++ b/GreatSpringGameJam/Content/Tilesets/World.tsx
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GreatSpringGameJam/Entity.cs b/GreatSpringGameJam/Entity.cs
new file mode 100644
index 0000000..b88351e
--- /dev/null
+++ b/GreatSpringGameJam/Entity.cs
@@ -0,0 +1,22 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace GreatSpringGameJam {
+ public class Entity {
+
+ public readonly Map Map;
+ public Vector2 Position;
+
+ public Entity(Map map, Vector2 position) {
+ this.Map = map;
+ this.Position = position;
+ }
+
+ public virtual void Update(GameTime time) {
+ }
+
+ public virtual void Draw(GameTime time, SpriteBatch batch) {
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/GreatSpringGameJam/GameImpl.cs b/GreatSpringGameJam/GameImpl.cs
new file mode 100644
index 0000000..081bd7b
--- /dev/null
+++ b/GreatSpringGameJam/GameImpl.cs
@@ -0,0 +1,48 @@
+using Microsoft.Xna.Framework;
+using MLEM.Cameras;
+using MLEM.Startup;
+
+namespace GreatSpringGameJam {
+ public class GameImpl : MlemGame {
+
+ public static GameImpl Instance { get; private set; }
+ public Map Map { get; private set; }
+ public Player Player { get; private set; }
+ private Camera camera;
+
+ public GameImpl() {
+ Instance = this;
+ }
+
+ protected override void LoadContent() {
+ this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280;
+ this.GraphicsDeviceManager.PreferredBackBufferHeight = 720;
+ this.GraphicsDeviceManager.ApplyChanges();
+ base.LoadContent();
+ this.InputHandler.HandleKeyboardRepeats = false;
+
+ this.camera = new Camera(this.GraphicsDevice) {
+ AutoScaleWithScreen = true,
+ Scale = 4,
+ Position = new Vector2(0, float.MaxValue)
+ };
+ this.Map = new Map("Level1");
+ this.Player = new Player(this.Map, new Vector2(5, 20));
+ this.Map.AddEntity(this.Player);
+ }
+
+ protected override void DoUpdate(GameTime gameTime) {
+ base.DoUpdate(gameTime);
+
+ this.Map.Update(gameTime);
+ this.camera.ConstrainWorldBounds(Vector2.Zero, this.Map.SizeInPixels.ToVector2());
+ }
+
+ protected override void DoDraw(GameTime gameTime) {
+ this.GraphicsDevice.Clear(Color.CornflowerBlue);
+ base.DoDraw(gameTime);
+ this.Map.Draw(gameTime, this.SpriteBatch, this.camera);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/GreatSpringGameJam/GreatSpringGameJam.csproj b/GreatSpringGameJam/GreatSpringGameJam.csproj
new file mode 100644
index 0000000..6b749b1
--- /dev/null
+++ b/GreatSpringGameJam/GreatSpringGameJam.csproj
@@ -0,0 +1,37 @@
+
+
+
+ Exe
+ net5.0
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GreatSpringGameJam/Map.cs b/GreatSpringGameJam/Map.cs
new file mode 100644
index 0000000..c192ca7
--- /dev/null
+++ b/GreatSpringGameJam/Map.cs
@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using MLEM.Cameras;
+using MLEM.Extended.Extensions;
+using MLEM.Extended.Tiled;
+using MLEM.Misc;
+using MLEM.Startup;
+using MonoGame.Extended.Tiled;
+
+namespace GreatSpringGameJam {
+ public class Map {
+
+ public Point SizeInPixels => new(this.map.WidthInPixels, this.map.HeightInPixels);
+ public Vector2 TileSize => this.map.GetTileSize();
+ public readonly TiledMapCollisions Collisions;
+
+ private readonly TiledMap map;
+ private readonly IndividualTiledMapRenderer renderer;
+ private readonly List entities = new();
+
+ public Map(string name) {
+ this.map = MlemGame.LoadContent($"Maps/{name}");
+ this.renderer = new IndividualTiledMapRenderer(this.map);
+ this.Collisions = new TiledMapCollisions(this.map);
+ }
+
+ public void AddEntity(Entity entity) {
+ this.entities.Add(entity);
+ }
+
+ public void Update(GameTime time) {
+ this.renderer.UpdateAnimations(time);
+ foreach (var entity in this.entities)
+ entity.Update(time);
+ }
+
+ public void Draw(GameTime time, SpriteBatch batch, Camera camera) {
+ batch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: camera.ViewMatrix);
+ this.renderer.Draw(batch, camera.GetVisibleRectangle().ToExtended());
+ foreach (var entity in this.entities)
+ entity.Draw(time, batch);
+ batch.End();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/GreatSpringGameJam/Player.cs b/GreatSpringGameJam/Player.cs
new file mode 100644
index 0000000..f7ccf1f
--- /dev/null
+++ b/GreatSpringGameJam/Player.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using MLEM.Animations;
+using MLEM.Extended.Extensions;
+using MLEM.Extended.Tiled;
+using MLEM.Extensions;
+using MLEM.Startup;
+using MLEM.Textures;
+using MonoGame.Extended;
+using MonoGame.Extended.Tiled;
+using RectangleF = MLEM.Misc.RectangleF;
+
+namespace GreatSpringGameJam {
+ public class Player : Entity {
+
+ private static readonly UniformTextureAtlas Atlas = new(MlemGame.LoadContent("Textures/Player"), 4, 1);
+ private readonly SpriteAnimationGroup animations;
+
+ public RectangleF Bounds => new(this.Position + new Vector2(4 / 16F, 0), new Vector2(9 / 16F, 1));
+
+ private bool walking;
+ private bool onGround;
+ private Vector2 velocity;
+ private TimeSpan jumpTime;
+ private bool facingRight;
+
+ public Player(Map map, Vector2 position) : base(map, position) {
+ this.animations = new SpriteAnimationGroup();
+ this.animations.Add(new SpriteAnimation(0.15F, Atlas[1], Atlas[2], Atlas[3], Atlas[0]), () => this.walking);
+ this.animations.Add(new SpriteAnimation(1, Atlas[0]), () => !this.walking);
+ }
+
+ public override void Update(GameTime time) {
+ base.Update(time);
+
+ // input
+ var lastVel = this.velocity;
+ if (MlemGame.Input.IsAnyDown(Keys.A, Keys.Left, Buttons.DPadLeft, Buttons.LeftThumbstickLeft)) {
+ this.velocity.X -= 0.04F;
+ this.facingRight = false;
+ }
+ if (MlemGame.Input.IsAnyDown(Keys.D, Keys.Right, Buttons.DPadRight, Buttons.LeftThumbstickRight)) {
+ this.velocity.X += 0.04F;
+ this.facingRight = true;
+ }
+ this.walking = this.velocity != lastVel;
+ if (MlemGame.Input.IsAnyDown(Keys.Up, Buttons.B, Keys.Space)) {
+ // only start jumping if we just started pressing the buttons
+ if (this.onGround && MlemGame.Input.IsAnyPressed(Keys.Up, Buttons.B, 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.onGround = false;
+ this.Position += this.velocity;
+ foreach (var (normal, penetration) in this.Map.Collisions.GetPenetrations(() => this.Bounds.ToExtended())) {
+ this.Position -= normal * penetration;
+ this.velocity *= new Vector2(Math.Abs(normal.Y), Math.Abs(normal.X));
+ if (normal.Y > 0)
+ this.onGround = true;
+ }
+ this.velocity *= new Vector2(this.onGround ? 0.5F : 0.6F, 0.9F);
+ this.velocity.Y += 0.02F;
+
+ this.animations.Update(time);
+ }
+
+ public override void Draw(GameTime time, SpriteBatch batch) {
+ base.Draw(time, batch);
+ var effects = this.facingRight ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
+ batch.Draw(this.animations.CurrentRegion, this.Position * this.Map.TileSize, Color.White, 0, Vector2.Zero, 1, effects, 0);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/GreatSpringGameJam/Program.cs b/GreatSpringGameJam/Program.cs
new file mode 100644
index 0000000..1e14aac
--- /dev/null
+++ b/GreatSpringGameJam/Program.cs
@@ -0,0 +1,14 @@
+using Microsoft.Xna.Framework;
+using MLEM.Misc;
+
+namespace GreatSpringGameJam {
+ public static class Program {
+
+ public static void Main() {
+ TextInputWrapper.Current = new TextInputWrapper.DesktopGl((w, c) => w.TextInput += c);
+ using var game = new GameImpl();
+ game.Run();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 0000000..c6e98bd
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file