From 5a73410b54a09eaf83069bbb5d20793295c01b6c Mon Sep 17 00:00:00 2001 From: James Turk Date: Fri, 6 Sep 2024 19:47:51 -0400 Subject: [PATCH] old experiments in writing C# games --- Dakota/.gitignore | 5 + Dakota/.gitmodules | 3 + Dakota/.idea/.idea.Dakota/.idea/.gitignore | 11 + Dakota/.idea/.idea.Dakota/.idea/encodings.xml | 4 + .../.idea/.idea.Dakota/.idea/indexLayout.xml | 8 + Dakota/.idea/.idea.Dakota/.idea/vcs.xml | 7 + Dakota/Dakota.sln | 28 ++ Dakota/Dakota.sln.DotSettings | 2 + Dakota/Dakota.sln.DotSettings.user | 12 + Dakota/Dakota/Content/Content.mgcb | 27 ++ Dakota/Dakota/Content/Sprites/Ship00.png | Bin 0 -> 233 bytes Dakota/Dakota/Dakota.csproj | 33 ++ Dakota/Dakota/Data/GameConstants.cs | 31 ++ Dakota/Dakota/Data/PrimaryWeapon.cs | 24 + Dakota/Dakota/Data/ZoneData.cs | 58 +++ Dakota/Dakota/Game1.cs | 14 + Dakota/Dakota/Icon.bmp | Bin 0 -> 262282 bytes Dakota/Dakota/Icon.ico | Bin 0 -> 147541 bytes Dakota/Dakota/Missions/Mission1.cs | 118 +++++ Dakota/Dakota/Missions/SpaceMission.cs | 123 +++++ Dakota/Dakota/Program.cs | 16 + Dakota/Dakota/Renderables/DialogBox.cs | 58 +++ Dakota/Dakota/Renderables/StaticBox.cs | 35 ++ Dakota/Dakota/Renderables/TimedVectorText.cs | 29 ++ Dakota/Dakota/Renderables/VectorSprite.cs | 37 ++ Dakota/Dakota/Renderables/VectorText.cs | 458 ++++++++++++++++++ Dakota/Dakota/ScreenSpin.cs | 28 ++ Dakota/Dakota/Space/Asteroid.cs | 87 ++++ Dakota/Dakota/Space/BaddieEmitter.cs | 54 +++ Dakota/Dakota/Space/GalacticMap.cs | 68 +++ Dakota/Dakota/Space/NoFlyZone.cs | 36 ++ Dakota/Dakota/Space/PlayerShip.cs | 159 ++++++ Dakota/Dakota/Space/Projectile.cs | 71 +++ Dakota/Dakota/Space/SpaceControls.cs | 47 ++ Dakota/Dakota/Space/SpaceHud.cs | 96 ++++ Dakota/Dakota/Space/SpaceHudMap.cs | 79 +++ Dakota/Dakota/Space/SpaceObject.cs | 77 +++ Dakota/Dakota/Space/SpaceScene.cs | 226 +++++++++ Dakota/Dakota/Space/StarField.cs | 46 ++ Dakota/Dakota/Utils/DakotaUtils.cs | 18 + Dakota/Dakota/app.manifest | 44 ++ Dakota/DakotaTests/DakotaTests.csproj | 23 + Dakota/DakotaTests/TestDakotaUtils.cs | 48 ++ Dakota/Sprites/Ship00.aseprite | Bin 0 -> 665 bytes Inky/.gitignore | 5 + Inky/.gitmodules | 3 + Inky/.idea/.idea.Inky/.idea/.gitignore | 11 + Inky/.idea/.idea.Inky/.idea/encodings.xml | 4 + Inky/.idea/.idea.Inky/.idea/indexLayout.xml | 8 + Inky/.idea/.idea.Inky/.idea/vcs.xml | 6 + Inky/Inky.sln | 22 + Inky/Inky/Content/Content.mgcb | 15 + Inky/Inky/DemoControls.cs | 48 ++ Inky/Inky/Game1.cs | 13 + Inky/Inky/GameConstants.cs | 11 + Inky/Inky/Icon.bmp | Bin 0 -> 262282 bytes Inky/Inky/Icon.ico | Bin 0 -> 147541 bytes Inky/Inky/Inky.csproj | 33 ++ Inky/Inky/InkyScene.cs | 36 ++ Inky/Inky/Lanky.cs | 156 ++++++ Inky/Inky/Lanky2.cs | 89 ++++ Inky/Inky/Program.cs | 14 + Inky/Inky/Skeletron/Head.cs | 7 + Inky/Inky/Skeletron/Line.cs | 29 ++ Inky/Inky/Skeletron/Rectangle.cs | 28 ++ Inky/Inky/Skeletron/Shape.cs | 70 +++ Inky/Inky/Skeletron/Skeletron.cs | 37 ++ Inky/Inky/app.manifest | 43 ++ Inky/newproj.txt | 20 + UPWG/.gitignore | 5 + UPWG/Content/jason.jpg | Bin 0 -> 73512 bytes UPWG/Data/moves.csv | 39 ++ UPWG/Elite/Elite.csproj | 11 + UPWG/Elite/Engine.cs | 81 ++++ UPWG/Elite/Entity.cs | 23 + UPWG/Elite/Scene.cs | 56 +++ UPWG/Elite/Sprite.cs | 23 + UPWG/Elite/TextElement.cs | 23 + UPWG/Manhattan/Manhattan.cs | 33 ++ UPWG/Manhattan/Manhattan.csproj | 33 ++ UPWG/Manhattan/Program.cs | 16 + UPWG/Manhattan/app.manifest | 43 ++ UPWG/UPWG.Core/Battle.cs | 367 ++++++++++++++ UPWG/UPWG.Core/Moves.cs | 78 +++ UPWG/UPWG.Core/StateMachine.cs | 45 ++ UPWG/UPWG.Core/UPWG.Core.csproj | 11 + UPWG/UPWG.Core/Wrestler.cs | 61 +++ UPWG/UPWG.Main/Animation.cs | 160 ++++++ UPWG/UPWG.Main/BattleTest.cs | 197 ++++++++ UPWG/UPWG.Main/Content/Arial.spritefont | 60 +++ UPWG/UPWG.Main/Content/Content.mgcb | 20 + UPWG/UPWG.Main/Icon.bmp | Bin 0 -> 262282 bytes UPWG/UPWG.Main/Icon.ico | Bin 0 -> 147541 bytes UPWG/UPWG.Main/Program.cs | 14 + UPWG/UPWG.Main/UPWG.Main.csproj | 36 ++ UPWG/UPWG.Main/app.manifest | 43 ++ UPWG/UPWG.sln | 34 ++ 97 files changed, 4568 insertions(+) create mode 100644 Dakota/.gitignore create mode 100644 Dakota/.gitmodules create mode 100644 Dakota/.idea/.idea.Dakota/.idea/.gitignore create mode 100644 Dakota/.idea/.idea.Dakota/.idea/encodings.xml create mode 100644 Dakota/.idea/.idea.Dakota/.idea/indexLayout.xml create mode 100644 Dakota/.idea/.idea.Dakota/.idea/vcs.xml create mode 100644 Dakota/Dakota.sln create mode 100644 Dakota/Dakota.sln.DotSettings create mode 100644 Dakota/Dakota.sln.DotSettings.user create mode 100644 Dakota/Dakota/Content/Content.mgcb create mode 100644 Dakota/Dakota/Content/Sprites/Ship00.png create mode 100644 Dakota/Dakota/Dakota.csproj create mode 100644 Dakota/Dakota/Data/GameConstants.cs create mode 100644 Dakota/Dakota/Data/PrimaryWeapon.cs create mode 100644 Dakota/Dakota/Data/ZoneData.cs create mode 100644 Dakota/Dakota/Game1.cs create mode 100644 Dakota/Dakota/Icon.bmp create mode 100644 Dakota/Dakota/Icon.ico create mode 100644 Dakota/Dakota/Missions/Mission1.cs create mode 100644 Dakota/Dakota/Missions/SpaceMission.cs create mode 100644 Dakota/Dakota/Program.cs create mode 100644 Dakota/Dakota/Renderables/DialogBox.cs create mode 100644 Dakota/Dakota/Renderables/StaticBox.cs create mode 100644 Dakota/Dakota/Renderables/TimedVectorText.cs create mode 100644 Dakota/Dakota/Renderables/VectorSprite.cs create mode 100644 Dakota/Dakota/Renderables/VectorText.cs create mode 100644 Dakota/Dakota/ScreenSpin.cs create mode 100644 Dakota/Dakota/Space/Asteroid.cs create mode 100644 Dakota/Dakota/Space/BaddieEmitter.cs create mode 100644 Dakota/Dakota/Space/GalacticMap.cs create mode 100644 Dakota/Dakota/Space/NoFlyZone.cs create mode 100644 Dakota/Dakota/Space/PlayerShip.cs create mode 100644 Dakota/Dakota/Space/Projectile.cs create mode 100644 Dakota/Dakota/Space/SpaceControls.cs create mode 100644 Dakota/Dakota/Space/SpaceHud.cs create mode 100644 Dakota/Dakota/Space/SpaceHudMap.cs create mode 100644 Dakota/Dakota/Space/SpaceObject.cs create mode 100644 Dakota/Dakota/Space/SpaceScene.cs create mode 100644 Dakota/Dakota/Space/StarField.cs create mode 100644 Dakota/Dakota/Utils/DakotaUtils.cs create mode 100644 Dakota/Dakota/app.manifest create mode 100644 Dakota/DakotaTests/DakotaTests.csproj create mode 100644 Dakota/DakotaTests/TestDakotaUtils.cs create mode 100644 Dakota/Sprites/Ship00.aseprite create mode 100644 Inky/.gitignore create mode 100644 Inky/.gitmodules create mode 100644 Inky/.idea/.idea.Inky/.idea/.gitignore create mode 100644 Inky/.idea/.idea.Inky/.idea/encodings.xml create mode 100644 Inky/.idea/.idea.Inky/.idea/indexLayout.xml create mode 100644 Inky/.idea/.idea.Inky/.idea/vcs.xml create mode 100644 Inky/Inky.sln create mode 100644 Inky/Inky/Content/Content.mgcb create mode 100644 Inky/Inky/DemoControls.cs create mode 100644 Inky/Inky/Game1.cs create mode 100644 Inky/Inky/GameConstants.cs create mode 100644 Inky/Inky/Icon.bmp create mode 100644 Inky/Inky/Icon.ico create mode 100644 Inky/Inky/Inky.csproj create mode 100644 Inky/Inky/InkyScene.cs create mode 100644 Inky/Inky/Lanky.cs create mode 100644 Inky/Inky/Lanky2.cs create mode 100644 Inky/Inky/Program.cs create mode 100644 Inky/Inky/Skeletron/Head.cs create mode 100644 Inky/Inky/Skeletron/Line.cs create mode 100644 Inky/Inky/Skeletron/Rectangle.cs create mode 100644 Inky/Inky/Skeletron/Shape.cs create mode 100644 Inky/Inky/Skeletron/Skeletron.cs create mode 100644 Inky/Inky/app.manifest create mode 100644 Inky/newproj.txt create mode 100755 UPWG/.gitignore create mode 100755 UPWG/Content/jason.jpg create mode 100755 UPWG/Data/moves.csv create mode 100755 UPWG/Elite/Elite.csproj create mode 100755 UPWG/Elite/Engine.cs create mode 100755 UPWG/Elite/Entity.cs create mode 100755 UPWG/Elite/Scene.cs create mode 100755 UPWG/Elite/Sprite.cs create mode 100755 UPWG/Elite/TextElement.cs create mode 100755 UPWG/Manhattan/Manhattan.cs create mode 100755 UPWG/Manhattan/Manhattan.csproj create mode 100755 UPWG/Manhattan/Program.cs create mode 100755 UPWG/Manhattan/app.manifest create mode 100755 UPWG/UPWG.Core/Battle.cs create mode 100755 UPWG/UPWG.Core/Moves.cs create mode 100755 UPWG/UPWG.Core/StateMachine.cs create mode 100755 UPWG/UPWG.Core/UPWG.Core.csproj create mode 100755 UPWG/UPWG.Core/Wrestler.cs create mode 100755 UPWG/UPWG.Main/Animation.cs create mode 100755 UPWG/UPWG.Main/BattleTest.cs create mode 100755 UPWG/UPWG.Main/Content/Arial.spritefont create mode 100755 UPWG/UPWG.Main/Content/Content.mgcb create mode 100755 UPWG/UPWG.Main/Icon.bmp create mode 100755 UPWG/UPWG.Main/Icon.ico create mode 100755 UPWG/UPWG.Main/Program.cs create mode 100755 UPWG/UPWG.Main/UPWG.Main.csproj create mode 100755 UPWG/UPWG.Main/app.manifest create mode 100755 UPWG/UPWG.sln diff --git a/Dakota/.gitignore b/Dakota/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/Dakota/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/Dakota/.gitmodules b/Dakota/.gitmodules new file mode 100644 index 0000000..acc7e36 --- /dev/null +++ b/Dakota/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Nez"] + path = Nez + url = git@github.com:prime31/Nez.git diff --git a/Dakota/.idea/.idea.Dakota/.idea/.gitignore b/Dakota/.idea/.idea.Dakota/.idea/.gitignore new file mode 100644 index 0000000..3ee979a --- /dev/null +++ b/Dakota/.idea/.idea.Dakota/.idea/.gitignore @@ -0,0 +1,11 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.Dakota.iml +/projectSettingsUpdater.xml +/modules.xml +/contentModel.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/Dakota/.idea/.idea.Dakota/.idea/encodings.xml b/Dakota/.idea/.idea.Dakota/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/Dakota/.idea/.idea.Dakota/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Dakota/.idea/.idea.Dakota/.idea/indexLayout.xml b/Dakota/.idea/.idea.Dakota/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/Dakota/.idea/.idea.Dakota/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Dakota/.idea/.idea.Dakota/.idea/vcs.xml b/Dakota/.idea/.idea.Dakota/.idea/vcs.xml new file mode 100644 index 0000000..bee80b2 --- /dev/null +++ b/Dakota/.idea/.idea.Dakota/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Dakota/Dakota.sln b/Dakota/Dakota.sln new file mode 100644 index 0000000..84e8990 --- /dev/null +++ b/Dakota/Dakota.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dakota", "Dakota\Dakota.csproj", "{2FF89EAA-6572-40AB-8A4D-BCE3275F113F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nez.MG38", "Nez\Nez.Portable\Nez.MG38.csproj", "{0A2DAF9B-E04C-4624-BA85-82CEA0F7D1F6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DakotaTests", "DakotaTests\DakotaTests.csproj", "{24B9130D-9930-43D2-A3EA-8D3055355502}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2FF89EAA-6572-40AB-8A4D-BCE3275F113F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FF89EAA-6572-40AB-8A4D-BCE3275F113F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FF89EAA-6572-40AB-8A4D-BCE3275F113F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2FF89EAA-6572-40AB-8A4D-BCE3275F113F}.Release|Any CPU.Build.0 = Release|Any CPU + {0A2DAF9B-E04C-4624-BA85-82CEA0F7D1F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A2DAF9B-E04C-4624-BA85-82CEA0F7D1F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A2DAF9B-E04C-4624-BA85-82CEA0F7D1F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A2DAF9B-E04C-4624-BA85-82CEA0F7D1F6}.Release|Any CPU.Build.0 = Release|Any CPU + {24B9130D-9930-43D2-A3EA-8D3055355502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24B9130D-9930-43D2-A3EA-8D3055355502}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24B9130D-9930-43D2-A3EA-8D3055355502}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24B9130D-9930-43D2-A3EA-8D3055355502}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Dakota/Dakota.sln.DotSettings b/Dakota/Dakota.sln.DotSettings new file mode 100644 index 0000000..68e7dbc --- /dev/null +++ b/Dakota/Dakota.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Dakota/Dakota.sln.DotSettings.user b/Dakota/Dakota.sln.DotSettings.user new file mode 100644 index 0000000..28eba16 --- /dev/null +++ b/Dakota/Dakota.sln.DotSettings.user @@ -0,0 +1,12 @@ + + <SessionState ContinuousTestingMode="0" Name="All tests from &lt;DakotaTests&gt; #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="/Users/james/Desktop/Dakota/DakotaTests" Presentation="&lt;DakotaTests&gt;" /> +</SessionState> + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;DakotaTests&gt; #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="/Users/james/Desktop/Dakota/DakotaTests" Presentation="&lt;DakotaTests&gt;" /> +</SessionState> + <SessionState ContinuousTestingMode="0" Name="All tests from &lt;DakotaTests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Project Location="/Users/james/Desktop/Dakota/DakotaTests" Presentation="&lt;DakotaTests&gt;" /> +</SessionState> + True + True \ No newline at end of file diff --git a/Dakota/Dakota/Content/Content.mgcb b/Dakota/Dakota/Content/Content.mgcb new file mode 100644 index 0000000..ce06e48 --- /dev/null +++ b/Dakota/Dakota/Content/Content.mgcb @@ -0,0 +1,27 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin Sprites/Ship00.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Sprites/Ship00.png + diff --git a/Dakota/Dakota/Content/Sprites/Ship00.png b/Dakota/Dakota/Content/Sprites/Ship00.png new file mode 100644 index 0000000000000000000000000000000000000000..7041822716248fb8c15b601d1a76ec050baba193 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}i#=T&Ln2zQ zPCLlgV8G#g^vD1GJ>vWsH*_|zzBs!?QYvKhwPH+;dm@D|}k$n3gTv@h?X~vXpa8Mt{muVGnzU zt6qQVXMW{)lVN|_Pu<~D + + WinExe + netcoreapp3.1 + false + false + + + app.manifest + Icon.ico + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dakota/Dakota/Data/GameConstants.cs b/Dakota/Dakota/Data/GameConstants.cs new file mode 100644 index 0000000..d1995dd --- /dev/null +++ b/Dakota/Dakota/Data/GameConstants.cs @@ -0,0 +1,31 @@ +using Microsoft.Xna.Framework; + +namespace Dakota.Data +{ + public static class GameConstants + { + public const int UiRenderLayerBg = 910; + public const int UiRenderLayerFg = 900; + public const int BackgroundRenderLayer = 50; + + public static Color ColorBlack = Color.Black; + + // Space UI + public static Color SpaceUiColor = Color.White; + public static Color SpaceHudFontColor = new Color(0, 255, 0); + + // Space Gameplay + public const float ShipAcceleration = 100; + public const float ShipTurnSpeed = 10; + + // Space Colors + public static Color ShipColor = Color.White; + + public static Color BoostColor = new Color(255, 128, 0); + public static Color AsteroidColor = new Color(128, 128, 128); + public static Color FriendlyWeaponColor = new Color(0, 255, 0); + public static Color HostileWeaponColor = new Color(255, 0, 255); + public static Color SpaceCommandColor = new Color(128, 128, 255); + public static Color NoFlyZoneColor = new Color(200, 0, 0); + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Data/PrimaryWeapon.cs b/Dakota/Dakota/Data/PrimaryWeapon.cs new file mode 100644 index 0000000..3e3dc6a --- /dev/null +++ b/Dakota/Dakota/Data/PrimaryWeapon.cs @@ -0,0 +1,24 @@ +using Dakota.Space; + +namespace Dakota.Data +{ + public enum ProjectileType + { + Bullet, + Beam + } + + public class PrimaryWeapon + { + public static PrimaryWeapon Gun = new PrimaryWeapon + {Name = "MASS DRIVER", Projectile = ProjectileType.Bullet, Speed = 200f, Duration = 2}; + + public static PrimaryWeapon Beam = new PrimaryWeapon + {Name = "PHASE BEAM", Projectile = ProjectileType.Beam, Speed = 100f, Duration = 0.2f}; + + public float Duration; + public string Name; + public ProjectileType Projectile; + public float Speed; + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Data/ZoneData.cs b/Dakota/Dakota/Data/ZoneData.cs new file mode 100644 index 0000000..7d48e1e --- /dev/null +++ b/Dakota/Dakota/Data/ZoneData.cs @@ -0,0 +1,58 @@ +namespace Dakota.Data +{ + public class Zone + { + public int Height; + public bool IsCurvedX; + + public bool IsCurvedY; + // a rectangular region in space + + public int UniversalX; + public int UniversalY; + public int Width; + + public Zone() + { + UniversalX = 0; + UniversalY = 0; + Width = 3000; + Height = 3000; + IsCurvedX = false; + IsCurvedY = false; + } + } + + public static class ZoneData + { + private static Zone[,] _zones = new Zone[26, 26]; + + public static Zone GetZone(int x, int y) + { + if (_zones[x, y] is null) + { + var zone = new Zone + { + UniversalX = x, + UniversalY = y, + Width = 3000, + Height = 3000 + }; + + // Act 1, Scene 1 + if (x == 16 && y == 13) + { + zone.Width = 1000; + zone.Height = 1000; + } + + if (x == 3 && y == 3) zone.IsCurvedX = true; + + if (x == 3 && y > 8 && y < 16) zone.IsCurvedY = true; + _zones[x, y] = zone; + } + + return _zones[x, y]; + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Game1.cs b/Dakota/Dakota/Game1.cs new file mode 100644 index 0000000..cc7001a --- /dev/null +++ b/Dakota/Dakota/Game1.cs @@ -0,0 +1,14 @@ +using Dakota.Space; +using Nez; + +namespace Dakota +{ + public class Game1 : Core + { + protected override void Initialize() + { + base.Initialize(); + Scene = new SpaceScene(); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Icon.bmp b/Dakota/Dakota/Icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b481653e241818d2894e4ef33234eaff9aad701 GIT binary patch literal 262282 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 RJoIeLznU7AhJ`o~_y6Yy51#-4 literal 0 HcmV?d00001 diff --git a/Dakota/Dakota/Missions/Mission1.cs b/Dakota/Dakota/Missions/Mission1.cs new file mode 100644 index 0000000..f79fbdd --- /dev/null +++ b/Dakota/Dakota/Missions/Mission1.cs @@ -0,0 +1,118 @@ +using Dakota.Data; +using Dakota.Space; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Missions +{ + public class Mission1 : SpaceMission + { + /* + * 1) fly into boundary: add asteroids & boundary + * 2) destroy one asteroid: OK 3 more + * 3) three more: zone map + * 4) fly to highlighted zone + * this is a metroidvania shooter? + */ + + private NoFlyZone _noFlyZone; + private string _status; + private const int SmallAsteroidGoal = 16; + + public override string GetMissionStatus() + { + return _status; + } + + public override void OnStart() + { + ZoneShiftDisabled = true; + SpaceScene.CurrentZone = ZoneData.GetZone(16, 13); + SpaceScene.PlayerWarpTo(new Vector2(500, 500)); + SpaceScene.Primary = PrimaryWeapon.Gun; + + _status = "EXPLORE AREA"; + + AddBoundary("inner", + new Rectangle(100, 100, SpaceScene.CurrentZone.Width - 100, SpaceScene.CurrentZone.Height - 100)); + _noFlyZone = SpaceScene.CreateEntity("no fly zone").AddComponent(new NoFlyZone(100)); + _noFlyZone.Enabled = false; + + Core.Schedule(0.1f, + timer => SpaceScene.DialogBox.ShowDialog( + "THIS IS YOUR FIRST TIME FLYING \nOUTSIDE SIMULATION.\nTRY AND FLY AROUND A BIT.", 10)); + } + + internal void AddEmitters() + { + SpaceScene.AsteroidEmitter.EmitSpeed = 5; + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(0, 0)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(1000, 1000)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(0, 1000)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(1000, 0)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(500, 0)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(0, 500)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(1000, 500)); + SpaceScene.AsteroidEmitter.AddEmitter(new Vector2(500, 1000)); + } + + public override void OnExitRegion(string name) + { + if (!_noFlyZone.Enabled) + { + SpaceScene.DialogBox.ShowDialog("IT IS EASY TO GET LOST IN SPACE\nLET'S STAY CLOSE FOR NOW", 3); + _noFlyZone.Enabled = true; + _status = "DESTROY ASTEROIDS"; + AddEmitters(); + } + + AutopilotReverse(150); + } + + public override void OnAsteroidDestroyed(AsteroidSize size) + { + base.OnAsteroidDestroyed(size); + + if (size == AsteroidSize.LARGE && AsteroidsDestroyed[AsteroidSize.LARGE] == 1) + { + SpaceScene.DialogBox.ShowDialog("GREAT JOB! BE SURE TO BREAK UP THE REST OF IT", 3); + } + else if (size == AsteroidSize.MEDIUM && AsteroidsDestroyed[AsteroidSize.MEDIUM] % 3 == 0) + { + SpaceScene.DialogBox.ShowDialog("KEEP IT UP!", 3); + } + else if (size == AsteroidSize.SMALL) + { + if (AsteroidsDestroyed[AsteroidSize.SMALL] >= 4 && + AsteroidsDestroyed[AsteroidSize.SMALL] < SmallAsteroidGoal) + { + var percent = (int) ((float) AsteroidsDestroyed[AsteroidSize.SMALL] / SmallAsteroidGoal * 100); + _status = $"DESTROY ASTEROIDS\n{percent}"; + } + + switch (AsteroidsDestroyed[AsteroidSize.SMALL]) + { + case 4: + SpaceScene.DialogBox.ShowDialog("OK ONE DOWN, PHEW!\nTAKE OUT 3 MORE", 3); + break; + case 10: + SpaceScene.DialogBox.ShowDialog("JUST A FEW MORE!", 3); + break; + case SmallAsteroidGoal: + SpaceScene.DialogBox.ShowDialog("ALL DONE! LET'S LOOK AT YOUR MAP.", 3); + break; + } + } + } + + public override void OnPlayerHit() + { + base.OnPlayerHit(); + + if (PlayerHitCount == 1) + SpaceScene.DialogBox.ShowDialog("WATCH OUT, THESE SHIPS ARE EXPENSIVE", 3); + else if (PlayerHitCount == 10) + SpaceScene.DialogBox.ShowDialog("YOU ARE TERRIBLE AT THIS", 3); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Missions/SpaceMission.cs b/Dakota/Dakota/Missions/SpaceMission.cs new file mode 100644 index 0000000..4d4b85e --- /dev/null +++ b/Dakota/Dakota/Missions/SpaceMission.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using Dakota.Data; +using Dakota.Space; +using Dakota.Utils; +using Microsoft.Xna.Framework; +using Nez; +using Random = Nez.Random; + +namespace Dakota.Missions +{ + public class SpaceMission : SceneComponent + { + protected SpaceScene SpaceScene => (SpaceScene) Scene; + protected int PlayerHitCount; + protected Dictionary AsteroidsDestroyed; + public bool ZoneShiftDisabled; + private readonly Dictionary _boundaries; + private readonly Dictionary _lastFrame; + private Vector2? _autopilotDestination; + + protected SpaceMission() + { + ZoneShiftDisabled = false; + AsteroidsDestroyed = new Dictionary(); + foreach (AsteroidSize size in Enum.GetValues(typeof(AsteroidSize))) AsteroidsDestroyed[size] = 0; + _boundaries = new Dictionary(); + _lastFrame = new Dictionary(); + } + + protected void AddBoundary(string name, Rectangle rect) + { + _boundaries[name] = rect; + _lastFrame[name] = false; + } + + protected void AutopilotTo(Vector2 to) + { + _autopilotDestination = to; + SpaceScene.CenterText.SetText("AUTOPILOT ENGAGED", 3); + } + + protected void AutopilotReverse(int magnitude) + { + var backwards = -Vector2.Normalize(SpaceScene.Player.GetComponent().Velocity); + AutopilotTo(SpaceScene.Player.Position + backwards * magnitude); + } + + #region Hooks + + // these methods can be overriden on missions to control behavior + + public virtual string GetMissionStatus() + { + return ""; + } + + public virtual void OnStart() + { + } + + public virtual void OnAsteroidDestroyed(AsteroidSize size) + { + AsteroidsDestroyed[size] += 1; + } + + public virtual void OnPlayerHit() + { + PlayerHitCount += 1; + } + + public virtual void OnEnterRegion(string name) + { + } + + public virtual void OnExitRegion(string name) + { + } + + #endregion + + public override void Update() + { + // check boundaries + foreach (var (name, rect) in _boundaries) + { + var thisFrame = rect.Contains(SpaceScene.Player.Position); + if (thisFrame && !_lastFrame[name]) + OnEnterRegion(name); + if (!thisFrame && _lastFrame[name]) + OnExitRegion(name); + _lastFrame[name] = thisFrame; + } + + // autopilot adjustments + if (_autopilotDestination != null) + { + var dist = SpaceScene.Player.Position - (Vector2) _autopilotDestination; + + if (dist.Length() < 35) + { + _autopilotDestination = null; + SpaceScene.CenterText.SetText("AUTOPILOT DISENGAGED", 3); + SpaceScene.Player.GetComponent().Velocity = Vector2.Zero; + SpaceScene.Player.GetComponent().Thrust(0); + } + else + { + var targetAngle = DakotaUtils.ToAngle(dist); + if (dist.Length() > 100) + targetAngle += (float) Math.PI; + + if (SpaceScene.Player.Rotation - targetAngle > 0.1f) + SpaceScene.Player.Rotation -= GameConstants.ShipTurnSpeed * Time.DeltaTime; + else if (targetAngle - SpaceScene.Player.Rotation > 0.1f) + SpaceScene.Player.Rotation += GameConstants.ShipTurnSpeed * Time.DeltaTime; + else + SpaceScene.Player.GetComponent().Thrust(.01f); + } + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Program.cs b/Dakota/Dakota/Program.cs new file mode 100644 index 0000000..45c4953 --- /dev/null +++ b/Dakota/Dakota/Program.cs @@ -0,0 +1,16 @@ +using System; + +namespace Dakota +{ + public static class Program + { + [STAThread] + private static void Main() + { + using (var game = new Game1()) + { + game.Run(); + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Renderables/DialogBox.cs b/Dakota/Dakota/Renderables/DialogBox.cs new file mode 100644 index 0000000..d4fbc70 --- /dev/null +++ b/Dakota/Dakota/Renderables/DialogBox.cs @@ -0,0 +1,58 @@ +using Dakota.Data; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Renderables +{ + public class DialogBox : RenderableComponent, IUpdatable + { + public override float Width => 1000; + public override float Height => 100; + + private const int Padding = 10; + private const int PhotoBoxSize = 72; + + private float _durationRemaining; + private VectorText _vt; + private StaticBox _sb; + + public override void OnAddedToEntity() + { + RenderLayer = GameConstants.UiRenderLayerBg; + _vt = Entity.AddComponent(new VectorText("", 20, GameConstants.SpaceUiColor)); + _vt.LocalOffset = new Vector2(Padding * 2 + PhotoBoxSize, Padding); + + _sb = Entity.AddComponent(new StaticBox(72, 72, GameConstants.SpaceCommandColor)); + _sb.LocalOffset = new Vector2(Padding, Padding); + } + + public override void Render(Batcher batcher, Camera camera) + { + batcher.DrawRect(Entity.Position, Width, Height, GameConstants.ColorBlack); + batcher.DrawHollowRect(Entity.Position, Width - 5, Height, GameConstants.SpaceUiColor); + batcher.DrawHollowRect(Entity.Position.X + Padding, Entity.Position.Y + Padding, + PhotoBoxSize, PhotoBoxSize, GameConstants.SpaceUiColor); + } + + public void Update() + { + _durationRemaining -= Time.DeltaTime; + if (_durationRemaining < 0) + { + Enabled = false; + _vt.Enabled = false; + _sb.Enabled = false; + } + } + + public void ShowDialog(string text, float duration) + { + _durationRemaining = duration; + _vt.Text = text; + _vt.Color = GameConstants.SpaceCommandColor; + Enabled = true; + _vt.Enabled = true; + _sb.Enabled = true; + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Renderables/StaticBox.cs b/Dakota/Dakota/Renderables/StaticBox.cs new file mode 100644 index 0000000..43159f0 --- /dev/null +++ b/Dakota/Dakota/Renderables/StaticBox.cs @@ -0,0 +1,35 @@ +using Dakota.Data; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Renderables +{ + public class StaticBox : RenderableComponent + { + public override float Width => _width; + public override float Height => _height; + + private int _width; + private int _height; + + public StaticBox(int width, int height, Color color) + { + _width = width; + _height = height; + Color = color; + + RenderLayer = GameConstants.UiRenderLayerBg; + } + + public override void Render(Batcher batcher, Camera camera) + { + for (var x = 0; x < Width; ++x) + for (var y = 0; y < Height; ++y) + if (Random.Chance(0.2f)) + batcher.DrawPixel( + Entity.Position.X + LocalOffset.X + x, + Entity.Position.Y + LocalOffset.Y + y, + Color); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Renderables/TimedVectorText.cs b/Dakota/Dakota/Renderables/TimedVectorText.cs new file mode 100644 index 0000000..8286f24 --- /dev/null +++ b/Dakota/Dakota/Renderables/TimedVectorText.cs @@ -0,0 +1,29 @@ +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Renderables +{ + public class TimedVectorText : VectorText, IUpdatable + { + private float _durationRemaining; + + public TimedVectorText(int size, Color color) : base("", size, color) + { + } + + public void Update() + { + if (Text.Length > 0) + { + _durationRemaining -= Time.DeltaTime; + if (_durationRemaining < 0) Text = ""; + } + } + + public void SetText(string text, float duration) + { + Text = text; + _durationRemaining = duration; + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Renderables/VectorSprite.cs b/Dakota/Dakota/Renderables/VectorSprite.cs new file mode 100644 index 0000000..f4d9b1c --- /dev/null +++ b/Dakota/Dakota/Renderables/VectorSprite.cs @@ -0,0 +1,37 @@ +using Microsoft.Xna.Framework; +using Nez; +using Nez.PhysicsShapes; + +namespace Dakota.Renderables +{ + public class VectorSprite : RenderableComponent, IUpdatable + { + private Vector2[] _polygon; + private Vector2[] _rotatedPolygon; + + public override float Width => 100; + public override float Height => 100; + + public VectorSprite(Vector2[] polygon, Color color) + { + _polygon = polygon; + _rotatedPolygon = new Vector2[polygon.Length]; + Color = color; + } + + public override void OnAddedToEntity() + { + Polygon.RotatePolygonVerts(Entity.Rotation, _polygon, _rotatedPolygon); + } + + public override void Render(Batcher batcher, Camera camera) + { + Polygon.RotatePolygonVerts(Entity.Rotation, _polygon, _rotatedPolygon); + batcher.DrawPolygon(Entity.Position, _rotatedPolygon, Color, true, 1); + } + + public void Update() + { + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Renderables/VectorText.cs b/Dakota/Dakota/Renderables/VectorText.cs new file mode 100644 index 0000000..9e90386 --- /dev/null +++ b/Dakota/Dakota/Renderables/VectorText.cs @@ -0,0 +1,458 @@ +using System; +using System.Collections.Generic; +using Dakota.Data; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Renderables +{ + public class VectorText : RenderableComponent + { + private const char Unprintable = '~'; + + private static Dictionary _font = new Dictionary(); + public int Size; + public string Text; + public float Thickness = 1.0f; + + static VectorText() + { + // each entry corresponds to the vectors for a given glyph + // lines are drawn from point to point the way one would if writing + // a null will "lift the pen" so to speak, starting a new set of + // lines not connected to the prior set + _font[Unprintable] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(1, 1), + null, + new Vector2(0, 1), + new Vector2(1, 0) + }; + _font['A'] = new Vector2?[] + { + new Vector2(0, 1), + new Vector2(0.5f, 0), + new Vector2(1, 1), + null, + new Vector2(0.2f, 0.5f), + new Vector2(0.7f, 0.5f) + }; + _font['B'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 0.8f), + new Vector2(1, 0.6f), + new Vector2(0, 0.5f), + new Vector2(1, 0.4f), + new Vector2(1, 0.2f), + new Vector2(0, 0) + }; + _font['C'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1) + }; + _font['D'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(0.5f, 1), + new Vector2(1, 0.6f), + new Vector2(1, 0.4f), + new Vector2(0.5f, 0), + new Vector2(0, 0) + }; + _font['E'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + null, + new Vector2(0, 0.5f), + new Vector2(1, 0.5f) + }; + _font['F'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + null, + new Vector2(0, 0.5f), + new Vector2(1, 0.5f) + }; + _font['G'] = new Vector2?[] + { + new Vector2(1, 0.2f), + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0.7f), + new Vector2(0.5f, 0.7f) + }; + _font['H'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + null, + new Vector2(1, 0), + new Vector2(1, 1), + null, + new Vector2(0, 0.5f), + new Vector2(1, 0.5f) + }; + _font['I'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + null, + new Vector2(0, 1), + new Vector2(1, 1), + null, + new Vector2(0.5f, 0), + new Vector2(0.5f, 1) + }; + _font['J'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(1, 1), + new Vector2(0.5f, 1), + new Vector2(0, 0.7f) + }; + _font['K'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0.5f), + new Vector2(1, 1), + null, + new Vector2(0, 0), + new Vector2(0, 1) + }; + _font['L'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1) + }; + _font['M'] = new Vector2?[] + { + new Vector2(0, 1), + new Vector2(0, 0), + new Vector2(0.5f, 0.2f), + new Vector2(1, 0), + new Vector2(1, 1) + }; + _font['N'] = new Vector2?[] + { + new Vector2(0, 1), + new Vector2(0, 0), + new Vector2(1, 1), + new Vector2(1, 0) + }; + _font['O'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0) + }; + _font['P'] = new Vector2?[] + { + new Vector2(0, 1), + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(1, 0.4f), + new Vector2(0, 0.4f) + }; + _font['Q'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0), + null, + new Vector2(1, 1), + new Vector2(0.5f, 0.6f) + }; + _font['R'] = new Vector2?[] + { + new Vector2(0, 1), + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(1, 0.4f), + new Vector2(0, 0.4f), + new Vector2(1, 1) + }; + _font['S'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 0.5f), + new Vector2(1, 0.5f), + new Vector2(1, 1), + new Vector2(0, 1) + }; + _font['T'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + null, + new Vector2(0.5f, 0), + new Vector2(0.5f, 1) + }; + _font['U'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0) + }; + _font['V'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0.5f, 1), + new Vector2(1, 0) + }; + _font['W'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(0.5f, 0.7f), + new Vector2(1, 1), + new Vector2(1, 0) + }; + _font['X'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 1), + null, + new Vector2(1, 0), + new Vector2(0, 1) + }; + _font['Y'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0.5f, 0.4f), + new Vector2(1, 0), + null, + new Vector2(0.5f, 0.4f), + new Vector2(0.5f, 1) + }; + _font['Z'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(0, 1), + new Vector2(1, 1) + }; + _font['0'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0), + new Vector2(0, 1) + }; + _font['1'] = new Vector2?[] + { + new Vector2(0.5f, 1), + new Vector2(0.5f, 0), + new Vector2(0.3f, 0.2f) + }; + _font['2'] = new Vector2?[] + { + new Vector2(0, 0.2f), + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(0, 1), + new Vector2(1, 1) + }; + _font['3'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(0, 0.5f), + new Vector2(1, 0.5f), + new Vector2(1, 1), + new Vector2(0, 1) + }; + _font['4'] = new Vector2?[] + { + new Vector2(0.8f, 1), + new Vector2(0.8f, 0), + new Vector2(0, 0.5f), + new Vector2(1, 0.5f) + }; + _font['5'] = new Vector2?[] + { + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 0.5f), + new Vector2(1, 0.5f), + new Vector2(0, 1) + }; + _font['6'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(1, 0.6f), + new Vector2(0, 0.6f) + }; + _font['7'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(0, 1) + }; + _font['8'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(0, 1), + new Vector2(1, 1), + new Vector2(0, 0) + }; + _font['9'] = new Vector2?[] + { + new Vector2(1, 1), + new Vector2(1, 0), + new Vector2(0, 0), + new Vector2(0, 0.4f), + new Vector2(1, 0.4f) + }; + _font[' '] = new Vector2?[] { }; + _font['.'] = new Vector2?[] + { + new Vector2(0.5f, 0.9f), + new Vector2(0.6f, 0.9f), + new Vector2(0.6f, 1), + new Vector2(0.5f, 1), + new Vector2(0.5f, 0.9f) + }; + _font['!'] = new Vector2?[] + { + new Vector2(0.55f, 0), + new Vector2(0.55f, 0.8f), + null, + new Vector2(0.5f, 0.9f), + new Vector2(0.6f, 0.9f), + new Vector2(0.6f, 1), + new Vector2(0.5f, 1), + new Vector2(0.5f, 0.9f) + }; + _font['?'] = new Vector2?[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(1, 0.4f), + new Vector2(0.55f, 0.4f), + new Vector2(0.55f, 0.8f), + null, + new Vector2(0.5f, 0.9f), + new Vector2(0.6f, 0.9f), + new Vector2(0.6f, 1), + new Vector2(0.5f, 1), + new Vector2(0.5f, 0.9f) + }; + _font[','] = new Vector2?[] + { + new Vector2(0.7f, 0.6f), + new Vector2(0.6f, 1) + }; + _font['\''] = new Vector2?[] + { + new Vector2(0.7f, 0), + new Vector2(0.6f, 0.3f) + }; + _font[':'] = new Vector2?[] + { + new Vector2(0.5f, 0.9f), + new Vector2(0.6f, 0.9f), + new Vector2(0.6f, 1), + new Vector2(0.5f, 1), + new Vector2(0.5f, 0.9f), + null, + new Vector2(0.5f, 0), + new Vector2(0.6f, 0), + new Vector2(0.6f, 0.1f), + new Vector2(0.5f, 0.1f), + new Vector2(0.5f, 0) + }; + _font['-'] = new Vector2?[] + { + new Vector2(0.2f, 0.5f), + new Vector2(0.8f, 0.5f) + }; + _font['+'] = new Vector2?[] + { + new Vector2(0.2f, 0.5f), + new Vector2(0.8f, 0.5f), + null, + new Vector2(0.5f, 0.2f), + new Vector2(0.5f, 0.8f) + }; + } + + public VectorText(string text, int size, Color color) + { + Text = text; + Size = size; + Color = color; + RenderLayer = GameConstants.UiRenderLayerFg; + } + + // approximate + public override float Width => Size * Text.Length; + public override float Height => Size; + + public override void Render(Batcher batcher, Camera camera) + { + var spacing = 5; + var curOffset = new Vector2(-Size, 0); + + foreach (var ch in Text) + { + Vector2?[] letter; + if (ch == '\n') + { + curOffset.X = -Size; + curOffset.Y += Size + spacing; + continue; + } + else if (_font.ContainsKey(ch)) + { + letter = _font[ch]; + curOffset.X += Size + spacing; + } + else + { + letter = _font[Unprintable]; + curOffset.X += Size + spacing; + Console.WriteLine($"UNPRINTABLE: {ch}"); + } + + for (var i = 0; i < letter.Length - 1; i++) + if (letter[i] != null && letter[i + 1] != null) + batcher.DrawLine( + (Vector2) letter[i] * Size + curOffset + Entity.Position + LocalOffset, + (Vector2) letter[i + 1] * Size + curOffset + Entity.Position + LocalOffset, + Color, + Thickness + ); + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/ScreenSpin.cs b/Dakota/Dakota/ScreenSpin.cs new file mode 100644 index 0000000..3099fb1 --- /dev/null +++ b/Dakota/Dakota/ScreenSpin.cs @@ -0,0 +1,28 @@ +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota +{ + public class ScreenSpin : Component, IUpdatable + { + private readonly Vector2 _around; + private float _speed; + + public ScreenSpin(Vector2 around, float speed) + { + _speed = speed; + _around = around; + } + + public void Update() + { + Entity.Scene.Camera.Rotation += _speed * Time.DeltaTime; + if (Entity.Scene.Camera.Rotation > 5) Entity.RemoveComponent(this); + } + + public override void OnAddedToEntity() + { + Entity.Scene.Camera.Position = _around; + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/Asteroid.cs b/Dakota/Dakota/Space/Asteroid.cs new file mode 100644 index 0000000..c409a14 --- /dev/null +++ b/Dakota/Dakota/Space/Asteroid.cs @@ -0,0 +1,87 @@ +using System; +using Dakota.Data; +using Dakota.Renderables; +using Dakota.Utils; +using Nez; +using Random = Nez.Random; +using Vector2 = Microsoft.Xna.Framework.Vector2; + +namespace Dakota.Space +{ + public enum AsteroidSize + { + SMALL, + MEDIUM, + LARGE, + HUGE + } + + public class Asteroid : Component, ITriggerListener + { + public AsteroidSize Size; + + public Asteroid(AsteroidSize size = AsteroidSize.LARGE) + { + Size = size; + } + + void ITriggerListener.OnTriggerEnter(Collider other, Collider self) + { + if (other.Entity.Name == "bullet" && !other.Entity.IsDestroyed) + { + var scene = (SpaceScene) Entity.Scene; + scene.HitAsteroid(self.Entity, other.Entity.Position); + other.Entity.Destroy(); + } + + // need to only process A-A collisions once, so use ID comparison + if (other.Entity.Name == "asteroid" && other.Entity.Id > self.Entity.Id) + { + other.CollidesWith(self, out var collisionResult); + self.Entity.Position += collisionResult.MinimumTranslationVector; + var p1 = self.Entity.Position; + var v1 = self.Entity.GetComponent().Velocity; + var p2 = other.Entity.Position; + var v2 = other.Entity.GetComponent().Velocity; + var diff1 = p1 - p2; + var diff2 = p2 - p1; + var newV1 = v1 - Vector2.Dot(v1 - v2, diff1) / diff1.LengthSquared() * diff1; + var newV2 = v2 - Vector2.Dot(v2 - v1, diff2) / diff2.LengthSquared() * diff2; + self.Entity.GetComponent().Velocity = newV1; + other.Entity.GetComponent().Velocity = newV2; + } + } + + void ITriggerListener.OnTriggerExit(Collider other, Collider self) + { + } + + public override void OnAddedToEntity() + { + var numPoints = Random.NextInt(3) + 7; + var polygon = new Vector2[numPoints]; + for (var i = 0; i < numPoints; i++) + { + var angle = (float) ((float) i / numPoints * 2 * Math.PI); + var radius = Radius() + Random.MinusOneToOne() * Radius() * 0.2f; + polygon[i] = radius * DakotaUtils.VecFromAngle(angle); + } + + // need to clone polygon here so original is intact for drawing rotations + Entity.AddComponent(new PolygonCollider((Vector2[]) polygon.Clone())).IsTrigger = true; + Entity.AddComponent(new VectorSprite(polygon, GameConstants.AsteroidColor)); + } + + public float Radius() + { + return Size switch + { + AsteroidSize.HUGE => 128, + AsteroidSize.LARGE => 72, + AsteroidSize.MEDIUM => 48, + AsteroidSize.SMALL => 24, + _ => 256 + }; + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/BaddieEmitter.cs b/Dakota/Dakota/Space/BaddieEmitter.cs new file mode 100644 index 0000000..f691ab7 --- /dev/null +++ b/Dakota/Dakota/Space/BaddieEmitter.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using Dakota.Utils; +using Microsoft.Xna.Framework; +using Nez; +using Random = Nez.Random; + +namespace Dakota.Space +{ + public class BaddieEmitter : SceneComponent, IUpdatable + { + private List _emitterPoints; + private float _nextEmission; + public float EmitSpeed; + + public BaddieEmitter() + { + _emitterPoints = new List(); + EmitSpeed = 0; + _nextEmission = 0; + } + + public void AddEmitter(Vector2 pos) + { + _emitterPoints.Add(pos); + } + + public void Emit() + { + var scene = (SpaceScene) Scene; + var posIndex = Random.NextInt(_emitterPoints.Count); + var pos = _emitterPoints[posIndex]; + var angle = DakotaUtils.ToAngle(new Vector2(scene.CurrentZone.Width / 2 - pos.X, + scene.CurrentZone.Height / 2 - pos.Y)); + var vel = DakotaUtils.VecFromAngle(angle) * (Random.NextFloat(30) + 30); + scene.AddAsteroid(pos, AsteroidSize.LARGE, vel); + } + + public override void Update() + { + if (EmitSpeed > 0) + { + if (_nextEmission == 0) + _nextEmission = EmitSpeed; + _nextEmission -= Time.DeltaTime; + if (_nextEmission < 0) + { + _nextEmission += EmitSpeed; + Emit(); + } + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/GalacticMap.cs b/Dakota/Dakota/Space/GalacticMap.cs new file mode 100644 index 0000000..dd076e4 --- /dev/null +++ b/Dakota/Dakota/Space/GalacticMap.cs @@ -0,0 +1,68 @@ +using Dakota.Data; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Space +{ + public class GalacticMap : RenderableComponent + { + private const float GridSize = 25f; + public override float Width => 500; + public override float Height => 520; + + public GalacticMap() + { + RenderLayer = GameConstants.UiRenderLayerBg; + } + + public void FillBox(Batcher batcher, int x, int y, Color color) + { + batcher.DrawRect( + GridSize * x + 1 + Entity.Position.X, + GridSize * y + 2 + Entity.Position.Y, + GridSize - 3, + GridSize - 3, + color); + } + + public override void Render(Batcher batcher, Camera camera) + { + var xSectors = 26; + var ySectors = 26; + + // draw horizontal lines + for (var i = 0; i <= ySectors; i++) + { + batcher.DrawLine( + 0 + Entity.Position.X, + i * GridSize + Entity.Position.Y, + xSectors * GridSize + Entity.Position.X, + i * GridSize + Entity.Position.Y, + GameConstants.SpaceUiColor); + + // draw vertical lines + for (var j = 0; j <= xSectors; j++) + { + batcher.DrawLine( + j * GridSize + Entity.Position.X, + 0 + Entity.Position.Y, + j * GridSize + Entity.Position.X, + ySectors * GridSize + Entity.Position.Y, + GameConstants.SpaceUiColor); + + if (i < ySectors && j < xSectors) + { + if (ZoneData.GetZone(j, i).IsCurvedX) + FillBox(batcher, j, i, Color.Purple); + if (ZoneData.GetZone(j, i).IsCurvedY) + FillBox(batcher, j, i, Color.Brown); + } + } + } + + // color current zone + var scene = (SpaceScene) Entity.Scene; + FillBox(batcher, scene.CurrentZone.UniversalX, scene.CurrentZone.UniversalY, Color.White); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/NoFlyZone.cs b/Dakota/Dakota/Space/NoFlyZone.cs new file mode 100644 index 0000000..c207398 --- /dev/null +++ b/Dakota/Dakota/Space/NoFlyZone.cs @@ -0,0 +1,36 @@ +using Dakota.Data; +using Nez; + +namespace Dakota.Space +{ + public class NoFlyZone : RenderableComponent + { + public override float Width => 800; + public override float Height => 800; + + private int _padding; + private RectangleF _innerRect; + private RectangleF _outerRect; + + public NoFlyZone(int padding) + { + _padding = padding; + } + + public override void OnAddedToEntity() + { + var scene = (SpaceScene) Entity.Scene; + _innerRect = new RectangleF(_padding, _padding, scene.CurrentZone.Width - _padding, + scene.CurrentZone.Height - _padding); + _outerRect = new RectangleF(_padding - 10, _padding - 10, scene.CurrentZone.Width - _padding + 20, + scene.CurrentZone.Height - _padding + 20); + } + + public override void Render(Batcher batcher, Camera camera) + { + // terrible looking, what should this look like? + batcher.DrawHollowRect(_innerRect, GameConstants.NoFlyZoneColor); + batcher.DrawHollowRect(_outerRect, GameConstants.NoFlyZoneColor); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/PlayerShip.cs b/Dakota/Dakota/Space/PlayerShip.cs new file mode 100644 index 0000000..9ad91d5 --- /dev/null +++ b/Dakota/Dakota/Space/PlayerShip.cs @@ -0,0 +1,159 @@ +using System; +using Dakota.Data; +using Dakota.Renderables; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using Nez; +using Nez.Particles; + +namespace Dakota.Space +{ + public enum ShipUpgrades + { + InertialThrusters, // reverse-thrust to create false feeling of inertia + HyperspaceStabilizer // reduce likelihood of hyperspace malfunction + } + + public enum ShipWeapons + { + PhaseBlaster, // simple blaster + LaserBeam, // steady pulsed beam + RailGun // unstable spray + } + + // one idea is to have these enter R&D and you can get an imperfect version that gets better as you use it + // if you're gonna have all these sweet upgrades, better have fun shit to fly near + + public class PlayerShip : Component, IUpdatable, ITriggerListener + { + private VirtualButton _fireInput; + private VirtualButton _secondaryFireInput; + private VirtualIntegerAxis _rotateInput; + private VirtualButton _thrustInput; + private VirtualButton _warpInput; + private float _emitCounter; + private ParticleEmitter _emitter; + private ParticleEmitterConfig _emitterConfig; + private VectorSprite _booster; + private float _autothrustTime; + + void ITriggerListener.OnTriggerEnter(Collider other, Collider self) + { + if (other.Entity.Name == "asteroid") + { + var scene = (SpaceScene) Entity.Scene; + scene.PlayerHit(); + scene.HitAsteroid(other.Entity, self.Entity.Position); + } + } + + void ITriggerListener.OnTriggerExit(Collider other, Collider self) + { + } + + private bool ThrustOn => _thrustInput.IsDown || _autothrustTime > 0; + + public void Thrust(float time) + { + _autothrustTime = time; + } + + public void Update() + { + var obj = Entity.GetComponent(); + if (_fireInput.IsPressed) ((SpaceScene) Entity.Scene).FirePrimary(); + if (_warpInput.IsPressed) ((SpaceScene) Entity.Scene).Warp(); + + obj.SetAcceleration(ThrustOn ? GameConstants.ShipAcceleration : 0); + _booster.Enabled = ThrustOn; + + if (ThrustOn) + _emitCounter += Time.DeltaTime; + while (_emitCounter > 0.01f) + { + _emitter.Emit(1); + _emitCounter -= 0.01f; + } + + obj.TurnVelocity = _rotateInput.Value * GameConstants.ShipTurnSpeed; + + _emitterConfig.Angle = MathHelper.ToDegrees(Entity.Rotation) + 90; + _emitter.LocalOffset = new Vector2(-16 * (float) Math.Sin(Entity.Rotation), + 16 * (float) Math.Cos(Entity.Rotation)); + } + + public override void OnAddedToEntity() + { + Entity.AddComponent(new SpaceObject()); + + var shipPolygon = new[] + { + new Vector2(0, -16), + new Vector2(-16, 16), + new Vector2(0, 10), + new Vector2(16, 16) + }; + Entity.AddComponent(new VectorSprite(shipPolygon, GameConstants.ShipColor)); + + var boostPoly = new[] + { + new Vector2(-16, 16), + new Vector2(-8, 24), + new Vector2(0, 12), + new Vector2(8, 24), + new Vector2(16, 16) + }; + _booster = Entity.AddComponent(new VectorSprite(boostPoly, GameConstants.BoostColor)); + + SetupInput(); + + // don't need indent for collider, also need own copy + // since PolygonCollider modifies the array for rotations + var colliderPolygon = new[] + { + new Vector2(0, -16), + new Vector2(-16, 16), + new Vector2(16, 16) + }; + Entity.AddComponent(new PolygonCollider(colliderPolygon)).IsTrigger = true; + + var ec = new ParticleEmitterConfig(); + ec.SourcePositionVariance = new Vector2(2, 2); + ec.Speed = 30; + ec.SpeedVariance = 1; + ec.ParticleLifespan = 3; + ec.ParticleLifespanVariance = 1; + ec.Angle = 180; + ec.AngleVariance = 0; + ec.StartColor = Color.Orange; + ec.FinishColor = Color.Transparent; + ec.StartParticleSize = 4; + ec.FinishParticleSize = 0; + ec.MaxParticles = 200; + ec.EmitterType = ParticleEmitterType.Gravity; + _emitterConfig = ec; + _emitter = Entity.AddComponent(new ParticleEmitter(ec)); + _emitter.LocalOffset = new Vector2(0, 16); + } + + private void SetupInput() + { + _fireInput = new VirtualButton(); + _fireInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.Space)); + _fireInput.SetRepeat(0.5f, 0.5f); + + _secondaryFireInput = new VirtualButton(); + _secondaryFireInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.LeftShift)); + + _thrustInput = new VirtualButton(); + _thrustInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.Up)); + + _warpInput = new VirtualButton(); + _warpInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.X)); + + _rotateInput = new VirtualIntegerAxis(); + _rotateInput.Nodes.Add(new VirtualAxis.KeyboardKeys(VirtualInput.OverlapBehavior.TakeNewer, Keys.Left, + Keys.Right)); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/Projectile.cs b/Dakota/Dakota/Space/Projectile.cs new file mode 100644 index 0000000..44301ad --- /dev/null +++ b/Dakota/Dakota/Space/Projectile.cs @@ -0,0 +1,71 @@ +using Dakota.Data; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Space +{ + public class Projectile : RenderableComponent, IUpdatable + { + private float _duration; + + private bool _playerFired; + private ProjectileType _type; + private Vector2 _velocity; + + public Projectile(ProjectileType type, bool playerFired, float duration) + { + _type = type; + _playerFired = playerFired; + _duration = duration; + } + + public override float Width => 2; + public override float Height => 2; + + public void Update() + { + _duration -= Time.DeltaTime; + if (_duration <= 0) + Entity.Destroy(); + } + + public override void OnAddedToEntity() + { + _velocity = Entity.GetComponent().Velocity; + + Collider collider; + switch (_type) + { + case ProjectileType.Bullet: + collider = Entity.AddComponent(new CircleCollider(1)); + break; + case ProjectileType.Beam: + collider = Entity.AddComponent(new PolygonCollider(new[] + { + new Vector2(0, 0), + _velocity * 2 + })); + break; + default: + collider = Entity.AddComponent(new CircleCollider(1)); + break; + } + + collider.IsTrigger = true; + } + + public override void Render(Batcher batcher, Camera camera) + { + var color = _playerFired ? GameConstants.FriendlyWeaponColor : GameConstants.HostileWeaponColor; + switch (_type) + { + case ProjectileType.Bullet: + batcher.DrawCircle(Entity.Position, 1, color); + break; + case ProjectileType.Beam: + batcher.DrawLine(Entity.Position, Entity.Position + _velocity * 2, color); + break; + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/SpaceControls.cs b/Dakota/Dakota/Space/SpaceControls.cs new file mode 100644 index 0000000..c0a200c --- /dev/null +++ b/Dakota/Dakota/Space/SpaceControls.cs @@ -0,0 +1,47 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using Nez; + +namespace Dakota.Space +{ + public class SpaceControls : SceneComponent, IUpdatable + { + private VirtualButton _pauseInput; + + private VirtualButton _debugInput; + + public SpaceControls() + { + _pauseInput = new VirtualButton(); + _pauseInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.P)); + + _debugInput = new VirtualButton(); + _debugInput.Nodes.Add(new VirtualButton.KeyboardKey(Keys.A)); + } + + public override void Update() + { + if (Scene is SpaceScene ss) + { + if (_pauseInput.IsPressed) ss.TogglePause(); + + if (_debugInput.IsPressed) + { + // horizontal + //ss.AddAsteroid(new Vector2(400, 400), AsteroidSize.LARGE, new Vector2(100, 0)); + //ss.AddAsteroid(new Vector2(900, 400), AsteroidSize.LARGE, new Vector2(-100, 0)); + + // vertical + //ss.AddAsteroid(new Vector2(400, 0), AsteroidSize.LARGE, new Vector2(0, 100)); + //ss.AddAsteroid(new Vector2(400, 900), AsteroidSize.LARGE, new Vector2(0, -100)); + + ss.AddAsteroid(new Vector2(0, 0), AsteroidSize.LARGE, new Vector2(100, 100)); + //ss.AddAsteroid(new Vector2(300, 300), AsteroidSize.LARGE, new Vector2(100, 100)); + ss.AddAsteroid(new Vector2(300, 0), AsteroidSize.LARGE, new Vector2(-100, 100)); + + //ss.AsteroidEmitter.Emit(); + } + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/SpaceHud.cs b/Dakota/Dakota/Space/SpaceHud.cs new file mode 100644 index 0000000..31317e0 --- /dev/null +++ b/Dakota/Dakota/Space/SpaceHud.cs @@ -0,0 +1,96 @@ +using Dakota.Data; +using Dakota.Renderables; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Space +{ + public class SpaceHud : RenderableComponent, IUpdatable + { + private const int FrameSize = 30; + private VectorText _acceleration; + private VectorText _primary; + private VectorText _spaceCoords; + private VectorText _velocity; + private VectorText _objective; + + private VectorText _zoneCoords; + public override float Width => 280; + public override float Height => 720; + + public void Update() + { + var scene = (SpaceScene) Entity.Scene; + var obj = scene.Player.GetComponent(); + _primary.Text = scene.Primary.Name; + _objective.Text = scene.CurrentMission.GetMissionStatus(); + _zoneCoords.Text = $"Z: {scene.CurrentZone.UniversalX} {scene.CurrentZone.UniversalY}"; + _spaceCoords.Text = $"P: {scene.Player.Position.X:0} {scene.Player.Position.Y:0}"; + _velocity.Text = $"V: {obj.Velocity.X:0} {obj.Velocity.Y:0}"; + _acceleration.Text = $"A: {obj.Acceleration.X:0} {obj.Acceleration.Y:0}"; + } + + public override void OnAddedToEntity() + { + RenderLayer = GameConstants.UiRenderLayerBg; + + var map = Entity.AddComponent(new SpaceHudMap()); + map.LocalOffset = new Vector2(40, 500); + map.RenderLayer = GameConstants.UiRenderLayerBg; + + var line = Entity.AddComponent(new VectorText("PRIMARY", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 40); + _primary = line = Entity.AddComponent(new VectorText("", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 60); + line = Entity.AddComponent(new VectorText("SECONDARY", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 100); + line = Entity.AddComponent(new VectorText("SMART BOMB", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 120); + line = Entity.AddComponent(new VectorText("WARP CORE", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 200); + line = Entity.AddComponent(new VectorText("DISABLED", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 220); + line = Entity.AddComponent(new VectorText("OBJECTIVE", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 250); + _objective = line = Entity.AddComponent(new VectorText("", 6, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 270); + + line = Entity.AddComponent(new VectorText("COORDINATES", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 350); + _zoneCoords = line = Entity.AddComponent(new VectorText("", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 370); + _spaceCoords = line = Entity.AddComponent(new VectorText("", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 390); + _velocity = line = Entity.AddComponent(new VectorText("", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 410); + _acceleration = line = Entity.AddComponent(new VectorText("", 12, GameConstants.SpaceHudFontColor)); + line.LocalOffset = new Vector2(40, 430); + } + + public override void Render(Batcher batcher, Camera camera) + { + batcher.DrawRect(Entity.Position, Width, Height, GameConstants.ColorBlack); + batcher.DrawHollowRect(Entity.Position, Width, Height, GameConstants.SpaceUiColor); + batcher.DrawHollowRect( + Entity.Position + new Vector2(FrameSize, FrameSize), + Width - FrameSize * 2, Height - FrameSize * 2, + GameConstants.SpaceUiColor); + batcher.DrawLine( + Entity.Position, + Entity.Position + new Vector2(FrameSize, FrameSize), + GameConstants.SpaceUiColor); + batcher.DrawLine( + Entity.Position + new Vector2(0, Height), + Entity.Position + new Vector2(FrameSize, Height - FrameSize), + GameConstants.SpaceUiColor); + batcher.DrawLine( + Entity.Position + new Vector2(Width, Height), + Entity.Position + new Vector2(Width - FrameSize, Height - FrameSize), + GameConstants.SpaceUiColor); + batcher.DrawLine( + Entity.Position + new Vector2(Width, 0), + Entity.Position + new Vector2(Width - FrameSize, FrameSize), + GameConstants.SpaceUiColor); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/SpaceHudMap.cs b/Dakota/Dakota/Space/SpaceHudMap.cs new file mode 100644 index 0000000..18dd72e --- /dev/null +++ b/Dakota/Dakota/Space/SpaceHudMap.cs @@ -0,0 +1,79 @@ +using System.Reflection; +using Dakota.Data; +using Nez; + +namespace Dakota.Space +{ + public class SpaceHudMap : RenderableComponent, IUpdatable + { + private const int Regions = 5; + private int[,] _regionCount; + private int _playerRegionX; + private int _playerRegionY; + public override float Width => 100; + public override float Height => 100; + + // Rudimentary + // System Scanner + // System Scanner+ + + public void Update() + { + var scene = (SpaceScene) Entity.Scene; + _regionCount = new int[Regions, Regions]; + + var xDiv = scene.CurrentZone.Width / Regions; + var yDiv = scene.CurrentZone.Height / Regions; + + _playerRegionX = (int) (scene.Player.Position.X / xDiv); + _playerRegionY = (int) (scene.Player.Position.Y / yDiv); + + // reset asteroid counts + for (var i = 0; i < Regions; i++) + for (var j = 0; j < Regions; j++) + _regionCount[i, j] = 0; + // count asteroids + foreach (var asteroid in scene.FindComponentsOfType()) + { + var x = (int) (asteroid.Entity.Position.X / xDiv); + var y = (int) (asteroid.Entity.Position.Y / yDiv); + if (x < 0 || x >= Regions || y < 0 || y >= Regions) + continue; + _regionCount[x, y] += 1; + } + } + + public override void Render(Batcher batcher, Camera camera) + { + var unitSize = (Height - 1) / Regions; + + batcher.DrawHollowRect(Entity.Position.X + LocalOffset.X, + Entity.Position.Y + LocalOffset.Y, + Width, + Height, + GameConstants.SpaceHudFontColor); + + + for (var i = 0; i < Regions; i++) + for (var j = 0; j < Regions; j++) + { + var color = GameConstants.ColorBlack; + if (_regionCount[i, j] >= 1) + color = GameConstants.AsteroidColor; + batcher.DrawRect( + Entity.Position.X + LocalOffset.X + unitSize * i, + Entity.Position.Y + LocalOffset.Y + unitSize * j, + unitSize, + unitSize, + color); + } + + batcher.DrawRect( + Entity.Position.X + LocalOffset.X + unitSize * _playerRegionX + 5, + Entity.Position.Y + LocalOffset.Y + unitSize * _playerRegionY + 5, + unitSize - 10, + unitSize - 10, + GameConstants.ShipColor); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/SpaceObject.cs b/Dakota/Dakota/Space/SpaceObject.cs new file mode 100644 index 0000000..e4eecc1 --- /dev/null +++ b/Dakota/Dakota/Space/SpaceObject.cs @@ -0,0 +1,77 @@ +using System; +using Dakota.Utils; +using Microsoft.Xna.Framework; +using Nez; + +namespace Dakota.Space +{ + public class SpaceObject : Component, IUpdatable + { + private Mover _mover; + public float TurnVelocity; + public Vector2 Velocity; + public Vector2 Acceleration; + + public SpaceObject() + { + Velocity = new Vector2(0, 0); + Acceleration = new Vector2(0, 0); + } + + public void Update() + { + var scene = (SpaceScene) Entity.Scene; + + Entity.Rotation += Time.DeltaTime * TurnVelocity; + Velocity += Time.DeltaTime * Acceleration; + _mover.Move( + Time.DeltaTime * Velocity + 0.5f * Time.DeltaTime * Time.DeltaTime * Acceleration, + out _ + ); + + // enforce zone boundaries + var newPos = Entity.Position; + if (Entity.Position.X < 0) + { + if (scene.CurrentZone.IsCurvedX) + newPos.X += scene.CurrentZone.Width; + else if (Entity.Name == "player") + newPos = scene.ZoneShift(-1, 0); + } + else if (Entity.Position.X > scene.CurrentZone.Width) + { + if (scene.CurrentZone.IsCurvedX) + newPos.X -= scene.CurrentZone.Width; + else if (Entity.Name == "player") + newPos = scene.ZoneShift(1, 0); + } + + if (Entity.Position.Y < 0) + { + if (scene.CurrentZone.IsCurvedY) + newPos.Y += scene.CurrentZone.Height; + else if (Entity.Name == "player") + newPos = scene.ZoneShift(0, -1); + } + else if (Entity.Position.Y > scene.CurrentZone.Height) + { + if (scene.CurrentZone.IsCurvedY) + newPos.Y -= scene.CurrentZone.Height; + else if (Entity.Name == "player") + newPos = scene.ZoneShift(0, 1); + } + + Entity.Position = newPos; + } + + public override void OnAddedToEntity() + { + _mover = Entity.AddComponent(new Mover()); + } + + public void SetAcceleration(float accel) + { + Acceleration = accel * DakotaUtils.VecFromAngle(Entity.Rotation); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/SpaceScene.cs b/Dakota/Dakota/Space/SpaceScene.cs new file mode 100644 index 0000000..f2eb180 --- /dev/null +++ b/Dakota/Dakota/Space/SpaceScene.cs @@ -0,0 +1,226 @@ +using System; +using Dakota.Data; +using Dakota.Missions; +using Dakota.Renderables; +using Dakota.Utils; +using Microsoft.Xna.Framework; +using Nez; +using Nez.Particles; +using Random = Nez.Random; + +namespace Dakota.Space +{ + internal enum GameMode + { + Active, + Paused + } + + public class SpaceScene : Scene + { + private const int HudWidth = 280; + + private const int PauseFreezeTag = 100; + + // entities + private Entity _galacticMap; + public Entity Player; + public DialogBox DialogBox; + public TimedVectorText CenterText { get; } + public BaddieEmitter AsteroidEmitter; + + // game data + private GameMode _mode; + public SpaceMission CurrentMission; + public Zone CurrentZone; + public PrimaryWeapon Primary; + + public SpaceScene() + { + // set up screen & camera + SetDesignResolution(1280, 720, SceneResolutionPolicy.ShowAllPixelPerfect); + Screen.SetSize(1280, 720); + ClearColor = GameConstants.ColorBlack; + // render background, then everything else, then UI layers + AddRenderer(new ScreenSpaceRenderer(0, GameConstants.BackgroundRenderLayer)); + AddRenderer(new RenderLayerExcludeRenderer(10, GameConstants.UiRenderLayerBg, + GameConstants.BackgroundRenderLayer, GameConstants.UiRenderLayerFg)); + AddRenderer(new ScreenSpaceRenderer(100, GameConstants.UiRenderLayerBg, GameConstants.UiRenderLayerFg)); + Core.ExitOnEscapeKeypress = false; + if (Environment.GetEnvironmentVariable("DEBUG_RENDERING") != null) + Core.DebugRenderEnabled = true; + + // create player and focus camera + Player = CreateEntity("player"); + Player.SetTag(PauseFreezeTag); + Player.AddComponent(new PlayerShip()); + var follow = Camera.Entity.AddComponent(new FollowCamera(Player)); + follow.FocusOffset = new Vector2(-100, 0); + + AddSceneComponent(new SpaceControls()); + AsteroidEmitter = AddSceneComponent(new BaddieEmitter()); + + var starField = CreateEntity("StarField").AddComponent(new StarField()); + starField.RenderLayer = GameConstants.BackgroundRenderLayer; + + _galacticMap = CreateEntity("GalacticMap", new Vector2(10, 10)); + _galacticMap.AddComponent(); + + // create UI + var hud = CreateEntity("HUD"); + hud.Position = new Vector2(Screen.Width - HudWidth, 0); + hud.AddComponent(new SpaceHud()); + DialogBox = CreateEntity("dialog box", new Vector2(1, Screen.Height - 100)) + .AddComponent(new DialogBox()); + CenterText = CreateEntity("center text", new Vector2(300, 300)) + .AddComponent(new TimedVectorText(20, GameConstants.SpaceHudFontColor)); + + CurrentMission = new Mission1(); + AddSceneComponent(CurrentMission); + CurrentMission.OnStart(); + + _mode = GameMode.Paused; + TogglePause(); + } + + public void TogglePause() + { + if (_mode == GameMode.Active) + { + _mode = GameMode.Paused; + _galacticMap.Enabled = true; + + foreach (var e in FindEntitiesWithTag(PauseFreezeTag)) + e.Enabled = false; + } + else + { + _mode = GameMode.Active; + _galacticMap.Enabled = false; + + foreach (var e in FindEntitiesWithTag(PauseFreezeTag)) + e.Enabled = true; + } + } + + public void PlayerHit() + { + CurrentMission.OnPlayerHit(); + } + + public void PlayerWarpTo(Vector2 pos) + { + Player.Position = pos; + Camera.Position = Player.Position - Camera.Entity.GetComponent().FocusOffset; + } + + public Vector2 ZoneShift(int xShift, int yShift) + { + if (CurrentMission.ZoneShiftDisabled) + return Player.Position; + + var newZone = ZoneData.GetZone( + CurrentZone.UniversalX + xShift, + CurrentZone.UniversalY + yShift + ); + + var newPos = Player.Position; + + // wrap around or scale to new area (so middle = middle) + newPos.X = xShift switch + { + -1 => newZone.Width, + 1 => 0, + _ => newPos.X / CurrentZone.Width * newZone.Width + }; + newPos.Y = yShift switch + { + -1 => newZone.Height, + 1 => 0, + _ => newPos.Y / CurrentZone.Height * newZone.Height + }; + + CurrentZone = newZone; + return newPos; + } + + public Entity AddAsteroid(Vector2 position, AsteroidSize size, Vector2? velocity = null) + { + var asteroid = CreateEntity("asteroid", position); + asteroid.SetTag(PauseFreezeTag); + asteroid.AddComponent(new Asteroid(size)); + var so = asteroid.AddComponent(new SpaceObject()); + so.TurnVelocity = Random.MinusOneToOne() * 3; + so.Velocity = velocity ?? Random.NextUnitVector() * 20; + return asteroid; + } + + public void HitAsteroid(Entity asteroid, Vector2 hitLocation) + { + var cur = asteroid.GetComponent(); + var angle = DakotaUtils.ToAngle(asteroid.Position - hitLocation) + (float) Math.PI; + // ~ 135 degrees + var angle1 = DakotaUtils.VecFromAngle(angle - 2.4f) * 50; + var angle2 = DakotaUtils.VecFromAngle(angle + 2.4f) * 50; + + switch (cur.Size) + { + case AsteroidSize.HUGE: + AddAsteroid(asteroid.Position, AsteroidSize.LARGE, angle1); + AddAsteroid(asteroid.Position + angle2, AsteroidSize.LARGE, angle2); + break; + case AsteroidSize.LARGE: + AddAsteroid(asteroid.Position, AsteroidSize.MEDIUM, angle1); + AddAsteroid(asteroid.Position + angle2, AsteroidSize.MEDIUM, angle2); + break; + case AsteroidSize.MEDIUM: + AddAsteroid(asteroid.Position, AsteroidSize.SMALL, angle1); + AddAsteroid(asteroid.Position + angle2, AsteroidSize.SMALL, angle2); + break; + case AsteroidSize.SMALL: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var ec = new ParticleEmitterConfig(); + ec.SourcePositionVariance = new Vector2(cur.Radius(), cur.Radius()); + ec.Speed = 30; + ec.SpeedVariance = 1; + ec.ParticleLifespan = 10; + ec.ParticleLifespanVariance = 1; + ec.Angle = 90; + ec.AngleVariance = 360; + ec.StartColor = GameConstants.AsteroidColor; + ec.FinishColor = Color.Transparent; + ec.StartParticleSize = 4; + ec.FinishParticleSize = 0; + ec.MaxParticles = 200; + ec.EmitterType = ParticleEmitterType.Gravity; + var ee = CreateEntity("dust emitter"); + ee.Position = asteroid.Position; + ee.AddComponent(new ParticleEmitter(ec)).Emit(100); + // TODO: ensure this gets removed? + + asteroid.Destroy(); + CurrentMission.OnAsteroidDestroyed(cur.Size); + } + + public void FirePrimary() + { + var playerHeading = DakotaUtils.VecFromAngle(Player.Rotation); + var bullet = CreateEntity("bullet", Player.Position + playerHeading * 20); + bullet.SetTag(PauseFreezeTag); + bullet.AddComponent(new Projectile(Primary.Projectile, true, Primary.Duration)); + var so = bullet.AddComponent(new SpaceObject()); + so.Velocity = playerHeading * Primary.Speed; + if (Primary.Projectile != ProjectileType.Beam) + so.Velocity += Player.GetComponent().Velocity; + } + + public void Warp() + { + // TODO + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Space/StarField.cs b/Dakota/Dakota/Space/StarField.cs new file mode 100644 index 0000000..15c3680 --- /dev/null +++ b/Dakota/Dakota/Space/StarField.cs @@ -0,0 +1,46 @@ +using Microsoft.Xna.Framework; +using Nez; +using Random = Nez.Random; + +namespace Dakota.Space +{ + public class Star + { + public Vector2 Position; + public Color StarColor; + + public Star() + { + Position = new Vector2(Random.NextFloat() * 2, Random.NextFloat() * 2); + var intensity = Random.NextFloat(0.5f) + 0.7f; + StarColor = new Color(intensity, intensity, intensity); + } + } + + public class StarField : RenderableComponent + { + private readonly Star[] _stars; + public override float Width => 1000; + public override float Height => 720; + + public StarField() + { + _stars = new Star[500]; + for (int i = 0; i < _stars.Length; i++) + _stars[i] = new Star(); + } + + public override void Render(Batcher batcher, Camera camera) + { + var pos = ((SpaceScene) (Entity.Scene)).Player.Position; + // TODO: scale by zone size + pos /= 3000; + // TODO: consider more parallax + + foreach (var star in _stars) + { + batcher.DrawPixel((star.Position.X - pos.X) * Width, (star.Position.Y - pos.Y) * Height, star.StarColor); + } + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/Utils/DakotaUtils.cs b/Dakota/Dakota/Utils/DakotaUtils.cs new file mode 100644 index 0000000..9158c52 --- /dev/null +++ b/Dakota/Dakota/Utils/DakotaUtils.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.Xna.Framework; + +namespace Dakota.Utils +{ + public static class DakotaUtils + { + public static float ToAngle(Vector2 v) + { + return (float) Math.Atan2(v.Y, v.X) + (float) (Math.PI / 2); + } + + public static Vector2 VecFromAngle(float angle) + { + return new Vector2((float) Math.Sin(angle), (float) -Math.Cos(angle)); + } + } +} \ No newline at end of file diff --git a/Dakota/Dakota/app.manifest b/Dakota/Dakota/app.manifest new file mode 100644 index 0000000..9450150 --- /dev/null +++ b/Dakota/Dakota/app.manifest @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + + diff --git a/Dakota/DakotaTests/DakotaTests.csproj b/Dakota/DakotaTests/DakotaTests.csproj new file mode 100644 index 0000000..919a289 --- /dev/null +++ b/Dakota/DakotaTests/DakotaTests.csproj @@ -0,0 +1,23 @@ + + + + net5.0 + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/Dakota/DakotaTests/TestDakotaUtils.cs b/Dakota/DakotaTests/TestDakotaUtils.cs new file mode 100644 index 0000000..4937a46 --- /dev/null +++ b/Dakota/DakotaTests/TestDakotaUtils.cs @@ -0,0 +1,48 @@ +using System; +using Dakota.Utils; +using Microsoft.Xna.Framework; +using NUnit.Framework; + +namespace DakotaTests +{ + public class DakotaUtilTests + { + [SetUp] + public void Setup() + { + } + + private Vector2 UpVector = new(0, -1); + private Vector2 RightVector = new(1, 0); + private Vector2 DownVector = new(0, 1); + private Vector2 LeftVector = new(-1, 0); + private const float Epsilon = 0.00001f; + private const float UpAngle = 0f; + private const float RightAngle = (float) Math.PI / 2f; + private const float DownAngle = (float) Math.PI; + private const float LeftAngle = (float) Math.PI * 1.5f; + + [Test] + public void TestToAngle() + { + // going around the clock + Assert.AreEqual(UpAngle, DakotaUtils.ToAngle(UpVector)); + Assert.AreEqual(RightAngle, DakotaUtils.ToAngle(RightVector)); + Assert.AreEqual(DownAngle, DakotaUtils.ToAngle(DownVector)); + Assert.AreEqual(LeftAngle, DakotaUtils.ToAngle(LeftVector)); + } + + [Test] + public void TestFromAngle() + { + Assert.AreEqual(UpVector.X, DakotaUtils.VecFromAngle(UpAngle).X, Epsilon); + Assert.AreEqual(UpVector.Y, DakotaUtils.VecFromAngle(UpAngle).Y, Epsilon); + Assert.AreEqual(RightVector.X, DakotaUtils.VecFromAngle(RightAngle).X, Epsilon); + Assert.AreEqual(RightVector.Y, DakotaUtils.VecFromAngle(RightAngle).Y, Epsilon); + Assert.AreEqual(DownVector.X, DakotaUtils.VecFromAngle(DownAngle).X, Epsilon); + Assert.AreEqual(DownVector.Y, DakotaUtils.VecFromAngle(DownAngle).Y, Epsilon); + Assert.AreEqual(LeftVector.X, DakotaUtils.VecFromAngle(LeftAngle).X, Epsilon); + Assert.AreEqual(LeftVector.Y, DakotaUtils.VecFromAngle(LeftAngle).Y, Epsilon); + } + } +} \ No newline at end of file diff --git a/Dakota/Sprites/Ship00.aseprite b/Dakota/Sprites/Ship00.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..a58165331c87974ced09cb9a2bcd22e287116ecc GIT binary patch literal 665 zcmcJM%S%*I9DskL4o38tl^{tTE2I>097Gu$GlC!-8Do?Yb)n&l89`B0e1MA(f{8FF z%GW{{DpGAkv~i(}S?Dl|Sx}4-l^Eor!jKj@dHm*5Qv+ + + + \ No newline at end of file diff --git a/Inky/.idea/.idea.Inky/.idea/indexLayout.xml b/Inky/.idea/.idea.Inky/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/Inky/.idea/.idea.Inky/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Inky/.idea/.idea.Inky/.idea/vcs.xml b/Inky/.idea/.idea.Inky/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/Inky/.idea/.idea.Inky/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Inky/Inky.sln b/Inky/Inky.sln new file mode 100644 index 0000000..dd6bd5b --- /dev/null +++ b/Inky/Inky.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inky", "Inky\Inky.csproj", "{807420D8-9B51-4BB4-9C79-B07CF1FB27A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nez.MG38", "Nez\Nez.Portable\Nez.MG38.csproj", "{4DEC583E-A7C7-4671-B977-EA999AE58C50}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {807420D8-9B51-4BB4-9C79-B07CF1FB27A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {807420D8-9B51-4BB4-9C79-B07CF1FB27A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {807420D8-9B51-4BB4-9C79-B07CF1FB27A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {807420D8-9B51-4BB4-9C79-B07CF1FB27A4}.Release|Any CPU.Build.0 = Release|Any CPU + {4DEC583E-A7C7-4671-B977-EA999AE58C50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DEC583E-A7C7-4671-B977-EA999AE58C50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DEC583E-A7C7-4671-B977-EA999AE58C50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DEC583E-A7C7-4671-B977-EA999AE58C50}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Inky/Inky/Content/Content.mgcb b/Inky/Inky/Content/Content.mgcb new file mode 100644 index 0000000..ddc4c36 --- /dev/null +++ b/Inky/Inky/Content/Content.mgcb @@ -0,0 +1,15 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:DesktopGL +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + diff --git a/Inky/Inky/DemoControls.cs b/Inky/Inky/DemoControls.cs new file mode 100644 index 0000000..af32872 --- /dev/null +++ b/Inky/Inky/DemoControls.cs @@ -0,0 +1,48 @@ +using Microsoft.Xna.Framework.Input; +using Nez; + +namespace Inky +{ + public class DemoControls : SceneComponent, IUpdatable + { + private VirtualButton _left; + private VirtualButton _right; + private float _eyeDirection = 1; + + public DemoControls() + { + _left = new VirtualButton(); + _left.Nodes.Add(new VirtualButton.KeyboardKey(Keys.Left)); + _right = new VirtualButton(); + _right.Nodes.Add(new VirtualButton.KeyboardKey(Keys.Right)); + } + + public override void Update() + { + var lanky = Scene.Entities.FindEntity("inky").GetComponent(); + if (_left.IsDown) + { + lanky.LegAlignment = Alignment.Left; + lanky.ArmAlignment = Alignment.Left; + lanky.HeadShift -= 1f * Time.DeltaTime; + } + else if (_right.IsDown) + { + lanky.LegAlignment = Alignment.Right; + lanky.ArmAlignment = Alignment.Right; + lanky.HeadShift += 1f * Time.DeltaTime; + } + else + { + lanky.EyeShift += _eyeDirection * Time.DeltaTime; + if (lanky.EyeShift < -0.99) _eyeDirection = 1; + else if (lanky.EyeShift > 0.99) _eyeDirection = -1; + lanky.MouthShift += _eyeDirection * Time.DeltaTime; + lanky.LegWobble += _eyeDirection * Time.DeltaTime; + + lanky.LegAlignment = Alignment.Center; + lanky.ArmAlignment = Alignment.Center; + } + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Game1.cs b/Inky/Inky/Game1.cs new file mode 100644 index 0000000..21a5db7 --- /dev/null +++ b/Inky/Inky/Game1.cs @@ -0,0 +1,13 @@ +using Nez; + +namespace Inky +{ + public class Game1 : Core + { + protected override void Initialize() + { + base.Initialize(); + Scene = new InkyScene(); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/GameConstants.cs b/Inky/Inky/GameConstants.cs new file mode 100644 index 0000000..d3825e2 --- /dev/null +++ b/Inky/Inky/GameConstants.cs @@ -0,0 +1,11 @@ +using Microsoft.Xna.Framework; + +namespace Inky +{ + public class GameConstants + { + public const int UiRenderLayerBg = 910; + public const int UiRenderLayerFg = 900; + public const int BackgroundRenderLayer = 50; + } +} \ No newline at end of file diff --git a/Inky/Inky/Icon.bmp b/Inky/Inky/Icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b481653e241818d2894e4ef33234eaff9aad701 GIT binary patch literal 262282 zcmeI5U#w--UB}l@N{bdGr9PBcCDGJ$nrI`=WH|TUUY#_NDiB0LqCT{g1`;L3KcJoR zB$bk?iHSS_+EQP9@eOEV)mmCZn@Z7_(T?-rw9}b;?{o%38)$^4jK5EtQ;%oXIrpry z_dfUcTkAXFaPC=W?{(JteZQZz*4}&VwGZF$(R6X*-$(6rKY#4|-fLgFI=zO^_WhMV zn7;e^?#J)_?|e$%|1TZ>g`0nG;=6_ZylA1V+Surdw0YuCTHCoTt#2Pm>pPF8(e{&R zbo%pYee0N|owd&|q_xxM?Qgx}#Q^Ql5G~OZZP6I5(H!mZKpSpvyOwWCo2PF~qn(eK z51uk#oHQSZ>3B>Jq1o5c+BTlR8+Zh-v`tI=+Fzayp1V1%ZPDJ-{tw|BoxR(1d)k=)wvF|~XT5x`Qd_))r|=dYtDmGeHKc96)7t2NHf>%_ z>tf#Guu3*s`z<_%*X~LqjSKd$nHZ&wGmqOb+e`Ahc;6!r;Jr`S3oq^=3eEiV#cA{W zuh}^4**(&`wZ2aEU3@HsC-Ej8Z3f5{RrHdy|0v_FuZVl~_0BzjNAW72T@fDD{tRvD z9cg1n*XkDUhty~Lh!vj2yBZHH*F4=|*ItfWJ1Rf>@UwJSP6s@UmzSfUy}jOUvHFzr zF`ighj)j)vhNtm%dqgi*@oD}`&Jf2KZPV;)`K$$9`bYMn2@ zIt!n+`TDK2>8hv3FUxoUMrvD=17VdzN3+J-mCWz52_t z{<6k+!QiUb4`9ub=QO`Z?5pp0;Q?4&#v?y@n2+Td<& zu;Qoq?WG4`cp3gVcKC|g{gCbcUiwYH?W?^zvE(`dy0_Ptj^avsEbD<~?h$@geZTsC zd7ugpz;ekk0P74d+t$>u-#Lrd({}FUQG4BIf4|pWx7h35X>IH0?3}7zeq(?}XoY6? z+H)w-5G~QP25&A~hmy6!KW;Hx_ID!g$Guamr*@CMembq4*_YD!lJ-*D`i}sbqAePu zb!+#q!t^34U7ik}d#RDO^0dv9K9bgV-jX&>EfO1TQ%?lY9PRNy#6GLE%rFhxyD4~Q z1AYHjEf!oC>2hs!w>awi7!kk&cmYq8G8Jum*xpT*&s7|Um#uExA9-mybaJ;i==-)J zfG6+<9*M^FqUD9{d8%HOMrUh1E@F+ex2AQC^XC~r-z7X!>)MvE4db(9zPr-4?_sO= zkn5t%il@?JMgXtinMM26NZY~qELFHp5~(h`mGwKO;rs11tG-HC1m;Bm@8F@`d=}|@ z7|+%DG8-4vw7$MW8eScrHTUIID~>Po1wwn>o;Nq0jw&XyQ1gtESpt5h#fO-kZf%gml=yE%0@J?q|~4 z9VL#{wtYt6M}w~;1p6Nhd@l1dn(Nc2ot4)T5x|2H`ubcMVW0I8bKgZsV=cEOB-1X6 zKyMNFoxsNcu>WM>a~SVPqwmjQ>P=8(qzDv501rm!D|2Oh!rHE9?B1r_ciz*Uw_}%! zv`%k^1=SGpk@E8=KBp{XXUv>1n^>xFYjrve>Ujx|6}5R38!k8 zAtHbmy|bPxAMC#nbnY+Dm3`uRyZZl9n`--kB7hgYvtC>I zc*fg6aw=OzpeO=*R`%-0KRoBLNF;5n2n-YfyjWZR>b*WZgLfb~m8~LB6al>GotN6m zC;p2ps*MMXfcRhi_+R`FXlP|!6an!s{)-HwjjM`)_!s{}8Jc@cxlXH>-x$0Y_#T?5 z=LK3B|6iRpb#5u>{FW)WIbSHP4PS`=Hgp!x(mCe=@h@NGe4(^9 zd?EhZ&{;f7=bQ(`zkHGNh0@yah4^nnXYnkZa~=@?@2A# zEUJnQ7=Z|^=gKJl2Q-DUZYBabwj!j7|7I+ziVhfo2(0JIDE2A# zEUJnQ7=Z|^=gKJl2Q-DUZYBabwj!j7|7I+ziVhfo2(0JIDE2A# zEUJnQ7=Z|^=gKJl2Q-DUZYBabwj!j7|7I+ziVhfo2(0JIDE2A# zEUJnQ7=Z|^=gKJl2Q-DUZYBabwj!jp!v7o7p_6}}*0&Sa{X{x+CZax+IRfjs zGPc71)oF7(SN{lUl)VN1@nQr$YAU1n_rETz`1h|-)HXr<`}dak_pg!o_dP<@_}{la zluqOS)wgr%|5qRL(!N6dzkiKNHTAN+`1kKE@$X+FFFco)L;U;qmiYItQ7Ly`wio~Y zy(RwrYvhIJ(sGD@|K1Y+{xvG)&dc`V-@muSzkiLq@LXCB@$cVT;@`hUrQCVhUi|y_ zmiYItkr$pz%OU>#drSQL*Qk^`FWZZM|K1Y+{x$N#b7?umzkhFufBzbla_41x@$cVT z;@`hUUU)7ohxqsJE%EPPqf+j?Y%l)(drSQL*T@UcrR5O+{=Fss{cBXpotN#!zkhFu zfBzbJ;kmRN;@`iw#J_)yO1bm0z4-U9<<86Y;@`iw#J_)yyzpFF4)O2bTjJlpMy1?&*Er|77UyC45_Y=T>QKylhX| zyGOu(4tUW^%v&kPEopt*zcad*@IbmYthWxPjV3g@HLY#8!kw3T-fl7PPM7~2em;%D zdh2i_{NqJ0T$GmMg|v3MbRQ4f-`7b0AGvR#?EYKW?j^*F9+)UC1MHs-+P^&3h_^l% zxNkX<)_1zoT+s0uUi`NP_{W2Iv6MXz+r$17X+7-u<#(jfDc|&VE8QE@p_8ws z^^oUh@Hb_r4&9>Qdv84Gf#qe&0Q-*y?qBY)^B2C|(XDhh1!jNj$6u~oBRn{+YqWRR zk9>aVGsZ_Z!hG-jvA|=qF$<4Z&OaU;*EQNZ?B5pj*o9a#zI;pCsPY)#QX4maGqAVD zEKmpP;=83?u5@@W^6?q$A4+RGW33{-f5`gC<<#@N*RKcHWztK(kw#UH)9~J8J)`{% z`&XpR6Vb{&S>7%4)+K5GQQtI|m+slX-$a*{4$tN4871wj6K(QSfzM$QZ`!X~jxqPy_2e%G*CWTk zznD(ct&B3CTjRCpeJ0G0qx)b5{mlat?b*0q5#QezbS_zr`RTMf(nb~Yf$`dO-J*T| zYd?FPkQBP3xhr1zed9oH|#5w`Q?G-Kb;4c6nT8Jcie1)iFvs%#Z7YCQ-^g zubj-4eOKkjuJ-l?hIC-yKtE3S9j_tW~+@R{>AUakwUUc~*CjVHT$ z1jdL7v6-hn1P8(nU`wHe-O~$p) zzljYon&r<`q~noVuJKK41KTh@FOE+KzZYP-&Xb>+xA8~zTJ>Dvi__-$57@m{HQdiT z8P6g9CPu`Hn9cKP-{0X0yivp4o>{uU_HJ>z)5c-5_OB)Vgw1{C{*9sU8)Q7oJ=XtT zahp1nCx{ub8~T_458wqnF;rc=OyO%N#S&may&m*=*^^c^@uk99> zlJ9@m#<3h{9XDSMS^gICCWge4n3ia;+qP(q_IRL$IKnh+?-m!n|5#(ks2mr4+S-5C za{DRs#4__!Ic}sMdBS4X(p;WZu|rIWEiqo^JPw+oEgGYBt>?LB=>gM=u+KKk%b1l< z3+dda!m$$TBi?JzuzHWZe%0c4qs4{m!F>K*`}`O7`bYC)^mQO2YD&zBJsO||nxG9D zp%tIe4h_)~O(WK`uQHd;wc>v5zJ6QzD3PoUQ_J-#eSLR zFmnB+g>$0vt3SU=lm0Y;;bpI-a;`;M^rwYlsaUSs128-u)0e(;Z8Z8iXRD64eou^h z@c`_0o#XOeod?u;PZGb$mCmw*kWx4HZzt^N&&a)0v&26me)^7jmReP9&y}mf^lFtvNcm224xE3Q` zS*fjDum*EonC*MtA4}J6ZR>pCo{R%)^SA0VyDxz;SnHeLKJvT9u0gi^JX`s8wr>@i z?y-R_80&-8q4GFn&u-FlZM$nNUKGPg55N>`4aIGr`5v}=kJSdqzmxve-^=>}mSCz+ zMu*NzZNTz+uZk7sjO$(w-#SHPf84e(UeWmgpZ_i#! zkC~r-=X`~(3oNerD){)O`D$`J$={@{;I?<@b^eE=(7ukp=de7AJwD`=kJ?%EH@2lt6xl}2Z0X~lPJGh`W* z_x$+6h_|^tv;0z;&U{7PFEC&Kq4@Y4^VJWeYcA;br!{07wjHV*%6GQ@u;FD+@3b%a z{LDQ|x_5ZC5Bg-}WsCK1FR>o%Z`nTV+hkVo1OjrHL z<1sz^p?7`oJlTX;-k&0ajVW&QQ_7R$bMB2V&GAAVsUUK$g;zN)o4 z`}V<5`LS;C0~QyaUr-uPZNI)vfAX-sE>8!~4W$uz^BWqkE!PI%Z9G2o8r?(bzlwZ$ zcFCtKMq+yL7(8q7>T;Z4sw=<6Z%U)ZV}PfJdM;0?ei63k`Gv=I9cnIPJm38{?HF=j zN-JLnhTr73DTCU9+3f%x?)zDs5%}$+j6A>ic02bZkNtmtowL5=4=gU1rb8$Dh;x@b zCxY z#+Eh0qj(k1_8sHa^P?@j)jZbPvFA}c=XOWh*m-^0JmK8~l##Mh=9bz4Jc&2e->=4@ zqqK46<93bvrAo)0%)R?t^V7T1`p#Y)-*;7!GE;WyQ0qKGyoU$zV%K-@P`pZRZ^?Om557=4|)Ghj$0FU7{JU8UE*43*N{};J` z?QYZNY4cf>c?9_B3--Ft%KhVMRDC}}9q+YkJJdN!dySXy6yCyP@kyz_2A7#mH<`yC zG0k``x~G1Jb3~8Z>ozObmFd8#`qjwSkh&8CVnIwiX@qC+4j#fw`5GuSN(2~#-k3%^ z585^Br_5`sx~9lFF8^TXga5?5#9CW#P8(m360=p8lh_a=VnxhWwQdw%z!M)aZ{U%d zj=ff`VWi)?!p>E*z8Zbd5j$7)sQKVY*SP*sNDHl0|CFz!@@Z9>Xho|^ zX)(&oghZPy%>SM@9z%l}GjCAu{C;oVGIu%KJ@?!l!{o4r7=Rq6i}`C}*jDuH?#_Sj z(ohc5(!wx%d;WXyUJM)FNe&w{sQCXO7?yEW4l^<;{=XB3wJty@=@!4Yz_1(bYu7&fPuJk|nzhy3Q(L-FbInCE!&koEwT+Nz3QDC=VaTw4ZBvK3)IcJncsYp*{AE`PBW!L95G))yFul z?-lXE>GGkuvm7^!8Eg5kO`8KcYn3~kT{rcB*41tf3mQ(_$ow+s`gZsB5p?cbUK`a+ z`Ct9!apHIbdPgqFJ8)o*OST*5@OS#-<p*PFK#mSvsSY!GY0>*?L8*+c77CPyq^ zCT{lK<=XN4?8ECWe>}f_dZ(0MI*T2O)JJlZ9kk`vX68qPZtJh5q_r!r!}n<35|8WW z%rg97OhCw^urtQU36TO}q9-UB7zw675d(-VV88jUEVAImQ2I{}I6|S;V zJHsBkVXEpiwT|;uZ*Tm(mNRzzlf^{?ls(OLJLz-kHG28KIh{m&V!{$F%wlxxhYJP$3YNw# zw?1dLXX_+A`(X=P4D6V6ug$k_t?nhoyqWD;kYI4uYh=0|Jg9y+PMd&rLm z=8D|W#{>0x8+Nzxx#D70=u4Y3bQ^Eq(fo@G*K2F+yOVU$sd4jkC%be!tx{UY>Ks}1(Cz5IL~uSVWfSUzH)msw)peeJ{a zV-I?cV%x1VpIqP*8*6egz0u%^@MhY6sS#_f8u%Q_=)UFtu2~NcjyP{($!=%M>=gi=|`wP@s4m%;wbQ-d5<*7be&59;Y*FI-w5p;ht_gqhpHc{Ue&zRMqn~Gf4x7E*; zbhiE*aI0yoORGtK`gZajHb;!VsK40JJ(0cQ^2tU+- zEsq`%H*nU4@DB#_R$?m_eKqagPe z4O32>v7@hT$llD9^k?mJJs)y+?7YXjADhp&nBCesZB4H2&?dbSw*)tEU!|G0 z%wws?!fT-^szKKoN;!8wT)F=IfA#Iw<}BHBwYS;YgQq={X6>n8>(O(S`&7&HH?H#CVQ>bw9RM0=&;}inb_uM+8 z;bz+jJ2AlZromy;-SwS%?2Mc-yP$XDbECpp-m^Az^}ibww&Ly9G=|6HZefPkJEcd> z3wo1nAH;JRuE3=0TzD}cZttlnR%?bh9pTRMzHF9%q{&y88JSKd&DS0rd?mqn|8cwJ z&91FnA>TXBln$AkIZg6 zhHgvx(I-STwabo=p>bvvZPpSqD)0b>k#fl~mPdrrgpo>Rzep@Sz%UN!t9>?)ar!F`X z60~Pk_{*5?dofmwh1KL^TL!&WU@pmCdvbi2DI1P|d%b{hc=DGogQhARJs+*KdeeIJ z?R!q6`;FI58?dUWUw5ov==!27Id9)svagoIk+AU43@Vw z&<@v|z&4&5oPuRpWInw3p=UtyNUH%wZ9F_19JWkzjP9g3C{#H~?s10RGiRsv9_ReN z$#tsl?lviqQ83|`jqd50YI~Qpp2@2YAN)saet0nUN#okD`mSsCXV+`_bg|z;?M?slYP0NmFm}K1hqm9GZ)97k zSe{!v$|39G#+$B8Iabnzd+E2mt~)CF-Y7Wav)|ZTfthx~8tqHQhKY6_SC$%CO;_>{)ajB*f2(kp-drVd zc!Em8aC6o7cbpr~yWYKbgSICkBOZKxl1dBqPZ*lQ{ngILBjxLO*Cgd{#zP_&cfUJ) z!HP@Hjd+htGgBWf+IhvIXy$2`r~5wS@9d~#5fgv4r-$B%Ia#~A+Rhx{T!eL1X>sbo zrhcPUZ~wPCW`%2O^Q9}Z)*hJp-J!nQ@mF$24s#Fo-D%t_^8rKtkzXgC)u~OZ?!D|B zZ%RLKYj{M_y30N6-+8JhTKXJjyqg&Dsqy@MJy?obJqsLob3?i%Y#ZbLMK5sev3j@E z<{UGzUfceE<8DSLq|gg)8ZaYw)nC2n>cCZVTHbxm{_2?bw#WV01{tX#?w8XOebw^} z`mN+8FP3}acem5Cb=RC&fo)BS0xUnySou|pQ$t{?W|TENhgY7e^i z$y}m-)}1e2!GtibeM>(o!KY0&$0;dcKOHmCG#oLA)pW~gFUTLtXyadULUt6jPJ zzv8^j*QLAHe>Lr2(=?q4cJZELzc=WgrTTXL_a%1P-<=DCjlX=(?DKNjw4QPv=^w6W zycv1zbV8B#kmTb7eD8F9=N3K@3o&c=`iuRfN!dGX4Pxl~RaRu(UY5%p){$mX;Ir9GmE-$;Cq#W%)nu8YZ3nYQJkI$AZ}Ot2j`sf?snY#Q*Emkm zpu7$}3DRKJHiwdoQ`*LC6h0x?W%sv(ZI@*}q2G; z*Wy=Z&zePCYJr_~WPDIxH!LEUX&ds{dFlL47Mab)9@F1-SN?#Go?nsUWY@(j8t81F z74J4>OZ-+X$A5>f4jGpaovAU+$aSrn?v~b;0sD*Yns{kA`X1@po0F5ca_^pypYuBP zUTqcOz3ksjUwcg%<>eYO_2uT7Yg4{%b?G}ePhsfw3(6m(uAjE>=+irJ&$f>j)4S~1 z5_W!4Yvn92?9xp{Iu8Aw_eMr{WGd6j#@fGU`KV669YqqMP zZxFY~_O%Uc_x+RYfswueTW^M+{5t0zD*}Sg^7B0#7kyW!W6w?B487azxzi+Z<>ukn z3zSnJ#D_f6c_>S45r3g-KE zmF12unJ4zWe0}lR7%zv6JG)%_jP2}l&_!$b@(Yi`PhNQ1^A$1!*xdUs*{T`sqf}GZ z_Q_sqacx2Sv*G&enI~Pe?kqF8p1Mlzxa;Ce+q@i-51zFf@#@4Dm($@-0-l6+XyFmx z@_UDAYn3~mI29BA{*>Lw7EhbGXDEyw7;HD}6*!umCttC2`k}sVki71}i)!s+&W7J_ z;Le>nAuQ;c;r`UoZyp%G&0Y1tQ}^+$+?4n&z9&ylv(cE>rsa|3hu6FoVxwN18l%SQ zn$auD&ne0{<-)iK??JPSjh^mcC(hYyHVr#9stId!25pl)X6-F!Z`#1UK?C))O;OOC3D;!J4L6Y)`DqIO--I3of2q>dv?xY^V71*3h?0&-=Ty z8Ek}YAG3Gt$5!f$e^C)@iXB(!iS;qU?yEG#nl;73>M0D`2xY2%WJ0J%aQB`6?YPB2 zrDFR+Y-*!zWJ zCmj3TC7&28{*90T8^*t-R-k?1$ZHB2}4^=Qdrvzo@gm}LZUOh)4d(7PWw9V&v z1)3cue2{yTHo|eZLi9zauGd_Yrp6zPvb}f90U7)I5yo@JsrHzkF!-X=q{bufX%!1irU1xMV~V&VrRUaXg#|4!ZG!fej$AeZh|Ya9D7Xlvk) z8KFP=+zBzCy(wha*G@v9jTM~^bp0xjk)^>@*- zq9;z+zgV+ie1tcuvd1|r9n=mx(c}B}*;&dN2ge-P-&}2*_Ib?a;@D$pmuJa0NEl$P z!QOc?J$Y5L?)z;UVyUrjJ6eRkG(6o=`{sf9w^k<38UHgjC@5)(^I7B6a^E^P&TN5?&6Ss{2Iqk*vp|hA{zlBea@Y}uGcx1o*%bUoZPph|M zd$TAS%AGUoDd*TIt#4f}Ir`U6}J3Amcf0fS+j>gx2 z7@8;TtpBxTJ=)sTFh11dw1rcLFV5~`k=l54@@(21_rTnG002&+M!H$l?-fO5(-wKQ0C>4V{;D zG(^YSr8^sZLI;Jsw!FLuejne)X5JoGTsF=*c)xk0jZNCfTm2KdkEa&KNpMV<(7b4k zW#+8g8>TMIo{_nt`EtW8r#gN-J$F~LRlls|;$LeW)h(DC$#^@dyG@b2+C$IJycyTS zmt%cCczw$<$cqJJl!J?pnC&e7C46 z{eM2XbNsqTSB8aq{siMknX}CMYNox}=k_zC5AWWn?~#k{J$A8q8q_)~oH6dXVZV+= z^@mvjMBiz5#RfGxmwNV`-;q>9wZwk)jZ&ix6`q;{Ri|L?<03%oJlO=5#=u`Awlai7ekXV0P(Uwu~N3Y^O zGWQ5vHr_WeY(e{MpS8KG993s@(;qw`BB1f=h)^w0-^a5i=ICU@oaJRZqz#u z?gEQe%FSNA_H3%1nrWsmu;rK`te&p9=d-PpEhCF`^?7X+Tt6}uba&=l)t(;gr|hx) zv~T+HiPzFIE#{tIt((~+&33+H{kOVG*Yg_q-dMc%Ebphn`sJ2#b2Q|X>&xp&cKh5)v#?t=^J~Ykn z?0h_fVSn<+uXCt7M!r7u9Zpt?+O$Bk|IRMUYz;JSG;0|*WJRlAXEq-CdS-&L)dtsN zAMJZ&r0SiX6zX-^A>HlmegnS-d4;?4zg~CPKWeIue}3~}59r@I)?2*KBJZEksHADZ&OV4|n9`WIkO2?-quUXx`&Am2-N79o5!!%=TpuMo7y?&s} zm29=4RzDfnznN?Pt8nPTh37uq-drfKZF%>qtAVq-<+Y^dmOqzm>}%e%rOHF=>E4g^ z(;qMvb=Axqu-bNreQ?a>p;mVs*hvnyhXdk1U_r|*}Z@EyJ%;5}&v|oDuhNpxp5fdFwa0?ti|kQKq5Yu^CSjE+U((t);7B?6xFw zY0s;N=>=;h_>NsuICS%;+l&CcL47do>4Rg^0R(WzYu@ZfI)Hg-d4^#CN~?F#wZw&IdjaD^t`b_w9r7)u-_n`NbQ6ewdJjs3>@;}ex&2JA3x{aY&<4c^3B_&ECPb^V^h54ibHpY6OQBzN+A+k`|cE05bOWa(F( z>Dn_qJPI$Sx6?3E(>H6Hq|RpD+s6o;BUe$i-G zC8trI zFVNW7h2|%074=-Ty;m?a?+o>_V$IwauFh-!?!LdK+T)b;U5n=5rDcS5J2qGTs9uq7 zClC74{qu~>Z*I>S6S+9VpyB9`_M5M38`^m)hYmh6`@E0KSt~|D^dXfWS}H%%=Zw8M zn3K82G3N4vg7tROc|)gdi@kjJ!ifnUOJ05AVcs_yb+me8G!P9qVxxzf4+|Y)zo`p) zc83pWG!lj@aX>?&wE|31v@jFSM2ybeh%q?97@hq9J%7X)+-!95{_rasrxj?%X-7N& zUI0)2R51R~2+7zI(R&!lHu(>iuO#}5c!PKZyaJxpb)d0L5G{cijs1g?xtMm1&$FY5 zcP4d94S4cM*4D_zJ)&eTrpzm<`}n-H#xTU&+6;8g1SI#Dl-#8x_wxDxyagWDHlU^G zAv=GAk~>Lq|4k1NuLrQf#jjyN_K411jnTNdlj2n%s@zM`uPfuYxQ&)hU4Z*~RDEBz zeANMa13sFhJ|W)5qx}3jCwsylfHqpS+lHubhic!;*lv?xS3ri<=_7)^B|?v=d)?Lp zyq{2vMgjEaQ+>L1Tjqjg7Gw!B6&0wloIgkQgdTuQMFD)C4hi)Tfd_T3Io$%;Rtord z9yNadr|KxoAE?Y1gn2uED(w_qgxx{&h$;XY_aP$g<5x=WfO(@f2#1 z+qE-m;0f?XR(3$JEy!P&Ba1p{QSD@YEy9YyT_hVFK*?iGt521ipvhW9pb-1L;ANjVFuP3_fiIBiEvfBkEYp_XdhtjzR36iM0 zm>dA!ky$p0_4nlF@%Ogx#T55%!3S^+NR>aa_X271NBw#fNX7vg=P7AAP`d91S#7@^ zk`YhEMzTl!;w072r0=90Ai|4&d9-8w1Ymd&= zMEN)V&=&1=ZA4un@_4$-6wYl>Ezc zj|{7I|RsQO9XdeS|BCIDsQ*y*UsO)={88Gn2J+8;Bk>Ht8aV%v z!HpwN{wQr(1KH<4^3MPztIm+^`LErcH$bu{QJ+!TkcTXR?DJpY3}CV9Kyl`a%J}?e zI(HCxv=q}#;|`Xk_%R0JAzAXlWzPvFEq%b7&XwKy?{v-~Qfa~IL3zjufOllc9#0p^ zp3dD!Y94g%L7A}2l-&x^@sm_q__D~xATL<}@QzIRgX~Q>6G_d3!Oq79u)C1XE;?s7 zxpWBBdyp5|06YWUktu(Wy-A7|sd)hYG%gdH#8V)RWejeAjK(e?oen}4{Ji>;Mi1}` zct+$$)*WOIz~Cm6o)?|FnG}98IIS@{JBf7s%gX@I6Xi_?0I$mDC;j*264V)-!!pPZ z+L((<@|4B{BUu@xNoIS$s&+BT8{r^L03JzS)C_TPso!;a-V~)uQJ28LfI2O zA>e{=!VqrN0pNu!>U)9?kTu9$5a{ec+42+lwlwYmk^2WmB`PBPtkLyYws;aU7U>SS zSz~~!C;;ty-~riwJ|TDF4zex}bT*^r_=@*&?Ld89wix}BF-A}Af$!gF!XAlmT8zF2 zBR*x;9DX9nzO;_TamD-97GppI>_-VS0d4&Le4v%Qemz3Y72QGB!hptoSZ8=#5pC4> zQau#L3y^U|z_+iJ#}}3+^{p=P%(^1+VGM^j8>Xr1s9%^%=|COT0gxTYP!fPXD;hV4(t$dr10XYyoiu>Ku|j>+ zg_I7|6&(OsA(=@NOg=eK@~s52kSyg-x=YQy1uDxSMKM|i+W4(FxMJ5TtLLAwwClcS*0I$XE z&lj0^2?D~L(%LeA0M=;GIRU7iZ(D7OATFdK%e5ty*&X1m;Jn>x6Gmo47&kz3AZuza z8Q$-O)@T~1c9a^GkQS^ht*LeopOeCqH124hbbK>AA zPFQ@c5#+&*A)OxJ8St*6xtikOUDILl_7$u#!Pgl{Ypq!}M&n%P*V8GTWlkak^7XJmI@qcGu!&pwFX z^HIB#F7tgjDq{z}?+C&P#|_~KxB|`$?q?x(A{(cb*n5FkgH5c(tSlcA{^Rj@xkR!$ z=mmj0@h*qI77?!)<^~_KO5c^>omKj-2=DHt?~s5%bUcBA(!VjxR3LZwu2Uv?3_}^P z1jC~Yn1b&LWwOICl%e3?sKP9hJwl*N_6ULU?#NPxjW$<2xt0+I}9RyblGT=uU5tqK8Cxn9x-jTdPtN8?@2kKoiey}hMY;g%c z%e=ecMiu>ETK<{1l_`EV#f>boI>q)v2RE|Fw1O>k$L$MxKx%>B!Tu=m6LKT$kpe%W zDVS>bUyfg#m7r#jL*Fnr_`wu#dxt;5D<2eC#1sFnSk-$OOM!4HZ?DV0m$&!j--~%b zVBuE2J`t?mfVAK0qcR91CYM1FDLEe@;0pr8T0R1bEO-S|%|}3qAI*(dT*2w!(V#x% z;UV4dU?5{W1N4VGijLO|@DKU}g1N!}@Q*2;4;~#bz#rTpcOGoaVvjB+y5ulj^owM? z7r&AG!;`}Xa1D?zCfeP1><5sGab=h6YW{pQ-Q%* zK+R!Avcc!^B3_WW50jwH8^GyXV|~D4+lvZxmyC3vFBf=28i0-GtU&Vq_1D=NQRM}^ z0Uk-ao*vF^g6vHg#dlH56XF%{Od5dC<&r_ayr?=>cfz5u3<>u!!^cIab>r18_xPB3 zHD!R?97^x2&HK`F8Y8|fLUk==@1@u_NA-a?bu!f9rSg*^9-`J?e_%M=FA||b#{GNB z$CSGMfp1z>`v7m(*66;Me|lMqtF)aAeF~J_Az23ykA(sBBT;LTN#;GO*MP^u`s|=D zkJ5ee`42n>UY7yr+aaqD8Lb_oac?8p?!fak;o4)h`Qq#04nEE#tw{u43;LrBjwuPc zkLEmawqvw(jWVLHOD}*s&>+2ea02BE@tgo@?^8}=-$y#$j!3@F-+>mONvif4jiccJ zZCz=O8KSnHsQcvBCBfkP6ic%Y81Woz5R5BJORk_Je~9;gOWKS)#kZ?+)zFBw*bFR7NEV_{3GV)NSGG*n046C5m#?;zepOs zDLWFCB{a{d+VbPe4gCAR4ft&u3H287UOMyAWP2WJZICscyHjGll@9NPw5MwWgGk1q zHucv)f5P7eK(nNFTUzlc{!3a9NEV+g{?j?SrtFR2dI<@l8TQ~F<*^;c5dDgCGPUs4^a%ke4wr}V!r>#wA^ zQ~FQozoa@;m*Z3V|A*>7*9^@MaQ>q&X_Py%^#5-dQ>OS|vr?w`Pw~HIIZ(5?Q~FQw zzh*g5v$@x1{fF}w=v;S+E=Yq6prsf|gcm_qZRWo;=3hY?WB~Zw(%_vm(jd$Kr}!^g zyPD#^Xnc}}t_w?p;y=ZIVR`}IDEnXi+K;mTqLnMk{)on>ipr>@-zol6{I5udDEm*@ z|B7@&THh)AL)o8-bcwP*)we$k&IQT)SWp^d0F?QPWG?IPzwKuxC4w2;C@IhwB_(Xi zCL^u&*V0)7PtN`W3@%Fw{AZPAoRr>RKRZeFyyTppTDbp)Nm?6;=_B|9X-ONTwf_bK z-vdR$`kRFPXQWlWK?c&ckJ8OW4S^!6W4DzTBkhT9L;8Gj*Up4t;f%g3ULL%?^GJrfFFL8j>{a11R zo034cX!{S-KN;7|Stn>ORmi(Ckk$UfV1s0{|2CY7Iuq>^x)O~G9zu5SEeUcbY>kL_ zgahCLI00^ae?+$XZ-Wj<&kqujZfW?ubu8s{MhAk=fMij6t{ZW_Rk?G1*!jg{jcASunxDx(%f=crO;slgtCDmf5B|Dt zbbby#zchi53&M$zP0748b|m`dkMMKCX(EB=JYE(bH6)zYg3?`Q=6jZuk%A82*eaxgwN2B_6JO3UE^d zodjJ)HwDmDcf=120pbNb6ClSVo=`Z!XIANVMZssmGKKOdpiH^L+0OsRv{X-#Jc!V! zEaT<@#{`upCi(-<9hE1@K#_%hP#J;@@RW7R#Z(;eB@%f&ttniD~SFH(7$*?8VK4hR>hJIVAFVXiNovjyeD zt^ry_^W6Y9z)_m>l6$ioBbw29+uWSmOLsZl!*NA81MZRl?b856voz-FizvTxbX2bY zqNK^ffQIUT$tQHNrcTv-OZ1#l9V4rnt$J5HQpEd`a$1nE8u<6B9?!k8QRV?_Ir`#5B@_tQji60XxDQeb>1S}Bh-_!Xk_lO~3QJc4`BU^$ z@~^deElk&ieWUch+UB1s|5W*>%DAx85UrPTe{jVkmDEq&Q#Bqk|kAKlPi6Y|#VXF(`kAI0X z23WoEPg7n!vE~=S{2Yi=9T0c?Qx@ajCfp$+%bxH@A&$6Eo$+s&13=^{%Dp=CA873V z#L5HkfH>kpb>=^iJ^zKqogy~vgd8BQs8HScFE9s)$WPR}sPms_pUhF(;@Fq+ZP9Er zU)8)y7DzJxNs{?*49;4i^I_<0Z8WdyAex_7BsOnmTXdTbN0TR!Mg74BsTB_4oMIU;juT$6x=tO)7dylZkEH8g2_C>*qV>=0=Sa4TMA}mZ_ML&S3_MQx_COWyB&>fHwkp843PE4-Z9k4QPKL{$6=rNbm0;oh7eMgS7d1puAwuK1ue)1H3uY5x%d` zRfT;a?f+Le4&u@Q=cn-bP*Zr9%@=S1oJ0XScM!5^nKj7svbYnn2b=)6%7Bl1Zmq+; zl!n|2X~56isfBzea3}5{hu?GwFRNf%gv+kBeTVWOSf=@Qrf%R)@Dgyw0r6fO8;Sd% zuvaAP11I9VpQ^&)`?{et#cMOZ!exNBwM5bz+?{~r{s9g8yoaIu*Pn5(U&)@MaizmO1t=yjhovevH zGa=8a%R}Y35OuM4hnzC zb2nF6mZXWlDE=FS5fz~9RfS(w|BF+8Dg4VyhT^~U{Rdg=KeXXQ1!?QQG}@2Qh87j* zZ09QN1%we5L~FlS-UlI0{|SS8x=Q>Zj3^*#|A|QBET~VfDu6z1k@jswdprKt|1BhO z=&P*SwnLw}JJ~v0{p!3&6>PH2CItGI{6@fVYhZ6PQLf`eLVQ5U`r5L#L+XHwU z6j!hxzdHT55TC(C=c0(nJG@U1m1#Qr1{(M0h#aq?^dSvM3(9KM{e^;Up@b6J>-6{q zC}ABRKLQf!^!ObRfS@jqpYkanXpqwQHD@atb3&JhwxP0cqKsb?G=MINRIgQl7tB?o zb0!eU;m?xHUjtl3sq;!Qga^D#i#dOT{SEPZyU;TS;jBUYKL}F+-M@dY-u#X7bnxpq zVyvvxU*gvVaK;$gKjza^k&}eSpjg z(gU>r2K$+4zGp>xk9<-*4U`t7Sry>tc5?qGMmZAnJF0%BL)sOb8)}@|5tZXNq~%>= z15h52mmu(Mhz#;3se-YN3S6@(6Vm(7#{@mo!GX=}$Rb0jd^DTK^BvxLP1;%kGNw<{O6_sz` zn&H9l%wsupWc|gh6HXFJG61=pb5CB@E~s&kFO0 z|AaxM{s(Fs2rYxqc0lKf6MhJ+O%rAB4G2T5V}rPmM#cT-KKOboq4^ivMZVtx{jj3U zTP64iVbOWgRrsHrrRHYLv`9;H10#hyNAN{M_9cE--NWkAb$YhgDA3~ zu?tW-yYTtD{J#BNNY?0EMR^js1nn$=b{9X_-+zgAiJ*VZ?~g%aDI7PUXVSyj^7wyf z45g&+2Feh$;VW)`qkb737V1%8qTU5})Kvf%27p$3!8yTjq19hZ1r$)|g?dR~iJn5P z4hmEDlFL$ZhYK4dk0M<$;dg2^W$~^0`=6LTgVPQ1r_^T`@3}0` zgz-mY1ridlynKYEZ~Q^!w{iyG$^+D;5MS^TC4FJ~1K-L6q+33{^6$d%uifdFFT=mv z2L^YX5W2^eXAcVVyM5r>pi&(wZ{NUIWw3Alo<90U_`r}+h4xLzcp%>n0xpZu75jVI zCH@Nh2Vp9<&t%sxIBncXmEc&)yYjkL`TL|Y@?oGnDyt{?ejM=3rBa`cpGMiViHwN) zlBjneO};GrF-A&fA{hS6;QHhL@$;?FKVe~t7!y;#+>6~aNJ9ZF&R682f83EXRsKR- z6DV#(|CPHGP$~pKT9777T@eF642Fkv(M9-SgX*CdVSwu@@JEC_O8H@2N^}^H%ef}L zoKYMiUqN}(xM=M%>iZCO!GD5bahVF>4Exx@{u~tn{3p2AoglrCc4cviG$G9Ic%b=@ z<@#$xY1<0YBK`QQh4QdGUkMz_=TFdE)xDzlu-{TiUg%j67~JHN|0<5}ULtGYr654M zL_FeNUVdfgyLpvFFE2kq9U$}v_bb?+O7nnc5Xl^+`W2eMf| zO7SSM@5pu*mmeUEC`SY>d|P%J8_4O3UoaPvu#x4;ue)4llmP!yWOYWP&7Cn#^iEtl RJoIeLznU7AhJ`o~_y6Yy51#-4 literal 0 HcmV?d00001 diff --git a/Inky/Inky/Inky.csproj b/Inky/Inky/Inky.csproj new file mode 100644 index 0000000..d085378 --- /dev/null +++ b/Inky/Inky/Inky.csproj @@ -0,0 +1,33 @@ + + + WinExe + netcoreapp3.1 + false + false + + + app.manifest + Icon.ico + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Inky/Inky/InkyScene.cs b/Inky/Inky/InkyScene.cs new file mode 100644 index 0000000..437c2a6 --- /dev/null +++ b/Inky/Inky/InkyScene.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.Xna.Framework; +using Nez; + +namespace Inky +{ + public class InkyScene : Scene + { + public InkyScene() + { + // set up screen & camera + SetDesignResolution(1280, 720, SceneResolutionPolicy.ShowAllPixelPerfect); + Screen.SetSize(1280, 720); + ClearColor = Color.White; + // render background, then everything else, then UI layers + AddRenderer(new ScreenSpaceRenderer(0, GameConstants.BackgroundRenderLayer)); + AddRenderer(new RenderLayerExcludeRenderer(10, GameConstants.UiRenderLayerBg, + GameConstants.BackgroundRenderLayer, GameConstants.UiRenderLayerFg)); + AddRenderer(new ScreenSpaceRenderer(100, GameConstants.UiRenderLayerBg, GameConstants.UiRenderLayerFg)); + Core.ExitOnEscapeKeypress = false; + if (Environment.GetEnvironmentVariable("DEBUG_RENDERING") != null) + Core.DebugRenderEnabled = true; + + var inky1 = CreateEntity("inky"); + inky1.AddComponent().Color = Color.Black; + inky1.Position = new Vector2(300, 300); + + var worm = CreateEntity("worm"); + //worm.AddComponent().Color = Color.Black; + worm.AddComponent(); + worm.Position = new Vector2(600, 400); + + AddSceneComponent(); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Lanky.cs b/Inky/Inky/Lanky.cs new file mode 100644 index 0000000..9ab1cb4 --- /dev/null +++ b/Inky/Inky/Lanky.cs @@ -0,0 +1,156 @@ +using System; +using Nez; +using Random = Nez.Random; + +namespace Inky +{ + public enum Alignment + { + Left, + Right, + Center + } + + public class Lanky : RenderableComponent + { + public override float Height => BodyHeight + HeadHeight + LegLength; + public override float Width => BodyWidth; + + public readonly int LegLength; + public readonly int ArmLength; + public readonly int BodyWidth; + public readonly int BodyHeight; + public readonly int HeadWidth; + public readonly int HeadHeight; + + public Alignment LegAlignment; + public Alignment ArmAlignment; + + private float _headShift; + public float HeadShift + { + get => _headShift; + set => _headShift = Math.Clamp(value, -1, 1); + } + + private float _eyeShift; + public float EyeShift + { + get => _eyeShift; + set => _eyeShift = Math.Clamp(value, -1, 1); + } + + private float _mouthShift; + public float MouthShift + { + get => _mouthShift; + set => _mouthShift = Math.Clamp(value, -1, 1); + } + + private float _mouthWidth; + public float MouthWidth + { + get => _mouthWidth; + set => _mouthWidth = Math.Clamp(value, 0, 1); + } + + private float _legWobble; + + public float LegWobble + { + get => _legWobble; + set => _legWobble = Math.Clamp(value, -1, 1); + } + + public Lanky() + { + // readonly + BodyHeight = 100; + LegLength = (int)((Random.NextFloat(0.6f) + 0.6f) * BodyHeight); + ArmLength = (int)((Random.NextFloat(0.3f) + 0.3f) * BodyHeight); + BodyWidth = (int)((Random.NextFloat(0.5f) + 0.5f) * BodyHeight); + HeadWidth = 40; + HeadHeight = 40; + + LegAlignment = Alignment.Center; + ArmAlignment = Alignment.Center; + MouthWidth = 0.5f; + } + + public override void Render(Batcher batcher, Camera camera) + { + var thickness = 3f; + // body + batcher.DrawHollowRect( + Entity.Position.X - BodyWidth/2f, + Entity.Position.Y - BodyHeight/2f, + BodyWidth, BodyHeight, Color, thickness + ); + + // legs + var legInset = BodyWidth * 0.15f; + var legBodyY = Entity.Position.Y + BodyHeight / 2f; + // center positions + var leftLegX = Entity.Position.X - BodyWidth / 2f + legInset; + var rightLegX = Entity.Position.X + BodyWidth / 2f - legInset; + + if (LegAlignment == Alignment.Left) + rightLegX = leftLegX + legInset; + else if (LegAlignment == Alignment.Right) + leftLegX = rightLegX - legInset; + + var leftWobble = LegWobble * LegLength * 0.1f; + var rightWobble = -LegWobble * LegLength * 0.1f; + + batcher.DrawLine(leftLegX, legBodyY, leftLegX, legBodyY + LegLength + leftWobble, Color); + batcher.DrawLine(rightLegX, legBodyY, rightLegX, legBodyY + LegLength + rightWobble, Color); + + // arms + var armShift = 10; + var armY = Entity.Position.Y - BodyHeight / 2f + armShift; + float leftArmX = 0; + float rightArmX = 0; + switch (ArmAlignment) + { + case Alignment.Left: + leftArmX = Entity.Position.X - BodyWidth / 2f + armShift; + rightArmX = leftArmX + armShift; + break; + case Alignment.Right: + leftArmX = Entity.Position.X + BodyWidth / 2f - armShift * 2; + rightArmX = leftArmX + armShift; + break; + case Alignment.Center: + leftArmX = Entity.Position.X - armShift * 2; + rightArmX = Entity.Position.X + armShift * 2; + break; + } + batcher.DrawLine(leftArmX, armY, leftArmX, armY + ArmLength, Color); + batcher.DrawLine(rightArmX, armY, rightArmX, armY + ArmLength, Color); + + // head + var headX = Entity.Position.X + (BodyWidth / 2f) * HeadShift - HeadWidth/2f; + var headY = Entity.Position.Y - BodyHeight / 2f - HeadHeight; + batcher.DrawHollowRect( + headX, + Entity.Position.Y - BodyHeight/2f - HeadHeight, + HeadWidth, HeadHeight, Color, thickness + ); + var eyeInset = EyeShift * (HeadWidth * 0.3f); + var eyeSpace = HeadWidth * 0.15f; + var eyeWidth = 2; + batcher.DrawHollowRect(headX + HeadWidth/2f + eyeInset - eyeSpace - eyeWidth, headY + eyeSpace, eyeWidth, eyeWidth, Color, thickness); + batcher.DrawHollowRect(headX + HeadWidth/2f + eyeInset + eyeSpace - eyeWidth, headY + eyeSpace, eyeWidth, eyeWidth, Color, thickness); + + var mouthCenterX = HeadWidth / 2f + MouthShift * HeadWidth / 2f; + var halfMouth = (HeadWidth * MouthWidth) / 2f; + batcher.DrawLine( + Math.Max(headX + mouthCenterX - halfMouth, headX), + headY + HeadHeight * 0.8f , + Math.Min(headX + mouthCenterX + halfMouth, headX + HeadWidth), + headY + HeadHeight * 0.8f , + Color + ); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Lanky2.cs b/Inky/Inky/Lanky2.cs new file mode 100644 index 0000000..ec64726 --- /dev/null +++ b/Inky/Inky/Lanky2.cs @@ -0,0 +1,89 @@ +using System; +using Inky.Skeletron; +using Microsoft.Xna.Framework; +using Nez; +using Edge = Inky.Skeletron.Edge; +using Random = Nez.Random; + +namespace Inky +{ + public class Lanky2 : RenderableComponent + { + public override float Height => 100; + public override float Width => 100; + + public readonly int LegLength; + public readonly int ArmLength; + public readonly int BodyWidth; + public readonly int BodyHeight; + public readonly int HeadWidth; + public readonly int HeadHeight; + + public Alignment LegAlignment; + public Alignment ArmAlignment; + + private float _headShift; + public float HeadShift + { + get => _headShift; + set => _headShift = Math.Clamp(value, -1, 1); + } + + private float _eyeShift; + public float EyeShift + { + get => _eyeShift; + set => _eyeShift = Math.Clamp(value, -1, 1); + } + + private float _mouthShift; + public float MouthShift + { + get => _mouthShift; + set => _mouthShift = Math.Clamp(value, -1, 1); + } + + private float _mouthWidth; + public float MouthWidth + { + get => _mouthWidth; + set => _mouthWidth = Math.Clamp(value, 0, 1); + } + + private float _legWobble; + + public float LegWobble + { + get => _legWobble; + set => _legWobble = Math.Clamp(value, -1, 1); + } + + public Skeletron.Rectangle _body; + + public Lanky2() + { + BodyHeight = (int) ((Random.NextFloat(0.5f) + 0.5f) * 100); + _body = new Skeletron.Rectangle(100, BodyHeight); + var _head = new Skeletron.Rectangle(40, 40); + _body.Attach(Edge.Top, _head); + + // readonly + LegLength = (int)((Random.NextFloat(0.6f) + 0.6f) * BodyHeight); + _body.Attach(Edge.Bottom, new Line(LegLength, true), 0.4f); + _body.Attach(Edge.Bottom, new Line(LegLength, true), -0.4f); + + ArmLength = (int)((Random.NextFloat(0.3f) + 0.3f) * BodyHeight); + _body.Attach(Edge.Left, new Line(ArmLength, true), 0.4f); + _body.Attach(Edge.Right, new Line(ArmLength, true), -0.4f); + + LegAlignment = Alignment.Center; + ArmAlignment = Alignment.Center; + MouthWidth = 0.5f; + } + + public override void Render(Batcher batcher, Camera camera) + { + _body.Draw(batcher, Entity.Position.X, Entity.Position.Y, Color.Black); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Program.cs b/Inky/Inky/Program.cs new file mode 100644 index 0000000..2ba94f4 --- /dev/null +++ b/Inky/Inky/Program.cs @@ -0,0 +1,14 @@ +using System; + +namespace Inky +{ + public static class Program + { + [STAThread] + static void Main() + { + using (var game = new Game1()) + game.Run(); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Skeletron/Head.cs b/Inky/Inky/Skeletron/Head.cs new file mode 100644 index 0000000..2dead67 --- /dev/null +++ b/Inky/Inky/Skeletron/Head.cs @@ -0,0 +1,7 @@ +namespace Inky.Skeletron +{ + public class Head + { + + } +} \ No newline at end of file diff --git a/Inky/Inky/Skeletron/Line.cs b/Inky/Inky/Skeletron/Line.cs new file mode 100644 index 0000000..b5a1eb9 --- /dev/null +++ b/Inky/Inky/Skeletron/Line.cs @@ -0,0 +1,29 @@ +using Microsoft.Xna.Framework; +using Nez; + +namespace Inky.Skeletron +{ + public class Line : Shape + { + public override int HalfHeight => IsVertical ? Length / 2 : 1; + public override int HalfWidth => IsVertical ? 1 : Length / 2; + + public int Length; + public bool IsVertical; + + public Line(int length, bool isVertical) + { + Length = length; + IsVertical = true; + } + + public override void Draw(Batcher batcher, float x, float y, Color color) + { + if(IsVertical) + batcher.DrawLine(x, y-Length/2f, x, y+Length/2f, color); + else + batcher.DrawLine(x-Length/2f, y, x+Length/2f, y, color); + DrawChildren(batcher, x, y, Color.Black); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Skeletron/Rectangle.cs b/Inky/Inky/Skeletron/Rectangle.cs new file mode 100644 index 0000000..14c5e1b --- /dev/null +++ b/Inky/Inky/Skeletron/Rectangle.cs @@ -0,0 +1,28 @@ +using Microsoft.Xna.Framework; +using Nez; + +namespace Inky.Skeletron +{ + + public class Rectangle : Shape + { + public override int HalfHeight => Height/2; + public override int HalfWidth => Width/2; + + public int Width; + public int Height; + + public Rectangle(int width, int height) + { + Width = width; + Height = height; + } + + public override void Draw(Batcher batcher, float x, float y, Color color) + { + batcher.DrawHollowRect(x - Width/2f,y - Height/2f, Width, Height, color, 2f); + DrawChildren(batcher, x, y, Color.Black); + } + + } +} \ No newline at end of file diff --git a/Inky/Inky/Skeletron/Shape.cs b/Inky/Inky/Skeletron/Shape.cs new file mode 100644 index 0000000..0455105 --- /dev/null +++ b/Inky/Inky/Skeletron/Shape.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Nez; + +namespace Inky.Skeletron +{ + public enum Edge + { + Top, + Bottom, + Left, + Right + } + + public class EdgeAttachment + { + public Edge OnEdge; + public float XOffset; + public float YOffset; + public Shape Shape; + } + + public abstract class Shape + { + public abstract int HalfHeight { get; } + public abstract int HalfWidth { get; } + + private List _attachments; + + protected Shape() + { + _attachments = new List(); + } + + public abstract void Draw(Batcher batcher, float x, float y, Color color); + + public EdgeAttachment Attach(Edge on, Shape shape, float xOffset = 0, float yOffset = 0) + { + var ea = new EdgeAttachment() { OnEdge = on, Shape = shape, XOffset = xOffset, YOffset = yOffset}; + _attachments.Add(ea); + return ea; + } + + protected void DrawChildren(Batcher batcher, float x, float y, Color color) + { + foreach (var att in _attachments) + { + var childX = x; + var childY = y; + + switch (att.OnEdge) + { + case Edge.Top: + childY -= att.Shape.HalfHeight + HalfHeight; + break; + case Edge.Bottom: + childY += att.Shape.HalfHeight + HalfHeight; + break; + case Edge.Left: + childX -= att.Shape.HalfWidth + HalfWidth; + break; + case Edge.Right: + childX += att.Shape.HalfWidth + HalfWidth; + break; + } + att.Shape.Draw(batcher, childX + att.XOffset * HalfWidth, childY + att.YOffset * HalfHeight, color); + } + } + } +} \ No newline at end of file diff --git a/Inky/Inky/Skeletron/Skeletron.cs b/Inky/Inky/Skeletron/Skeletron.cs new file mode 100644 index 0000000..8e7916b --- /dev/null +++ b/Inky/Inky/Skeletron/Skeletron.cs @@ -0,0 +1,37 @@ +using Microsoft.Xna.Framework; +using Nez; + +namespace Inky.Skeletron +{ + public class Skeletron : RenderableComponent, IUpdatable + { + public override float Height => 100; + public override float Width => 100; + + public Shape _body; + private EdgeAttachment _neckJoint; + + public Skeletron() + { + _body = new Rectangle(80, 200); + (_body as Rectangle).Attach(Edge.Left, new Rectangle(30, 10)); + (_body as Rectangle).Attach(Edge.Right, new Rectangle(60, 10)); + (_body as Rectangle).Attach(Edge.Right, new Rectangle(60, 10), 0, 0.3f); + var head = new Rectangle(80, 80); + head.Attach(Edge.Top, new Rectangle(100, 5), 0, 0); + _neckJoint = (_body as Rectangle).Attach(Edge.Top, head, -1, 0); + (_body as Rectangle).Attach(Edge.Bottom, new Line(40, true), -0.5f, 0); + (_body as Rectangle).Attach(Edge.Bottom, new Line(40, true), 0.5f, 0); + } + + public void Update() + { + _neckJoint.XOffset += Time.DeltaTime; + } + + public override void Render(Batcher batcher, Camera camera) + { + _body.Draw(batcher, Entity.Position.X, Entity.Position.Y, Color.Black); + } + } +} \ No newline at end of file diff --git a/Inky/Inky/app.manifest b/Inky/Inky/app.manifest new file mode 100644 index 0000000..4c75233 --- /dev/null +++ b/Inky/Inky/app.manifest @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + diff --git a/Inky/newproj.txt b/Inky/newproj.txt new file mode 100644 index 0000000..819616f --- /dev/null +++ b/Inky/newproj.txt @@ -0,0 +1,20 @@ +- new monogame cross platform +- add Nez as submodule + git submodule add git@github.com:prime31/Nez.git Nez +- SLN -> add existing projeectt -> Nez.Portable.MG38 +- Proj -> add reference -> Nez +- change Game1.cs to + +using Nez; + +namespace Dakota +{ + public class Game1 : Core + { + protected override void Initialize() + { + base.Initialize(); + Scene = new SomeScene(); + } + } +} diff --git a/UPWG/.gitignore b/UPWG/.gitignore new file mode 100755 index 0000000..add57be --- /dev/null +++ b/UPWG/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/UPWG/Content/jason.jpg b/UPWG/Content/jason.jpg new file mode 100755 index 0000000000000000000000000000000000000000..0904f9e495dc7acd5d12deaa72279f6ff2e0c84a GIT binary patch literal 73512 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 literal 0 HcmV?d00001 diff --git a/UPWG/Data/moves.csv b/UPWG/Data/moves.csv new file mode 100755 index 0000000..08ff511 --- /dev/null +++ b/UPWG/Data/moves.csv @@ -0,0 +1,39 @@ +Name,Type,Rank,PartsUtilized,PartsRestrained,BaseChance,BaseOffense,BasePopularity +Chop 1,Strike,1,Arms,0,0.8,3,1 +Chop 2,Strike,2,Arms,0,0.85,6,2 +Chop 3,Strike,3,Arms,0,0.9,9,3 +Kick 1,Strike,1,Legs,0,0.7,4,1 +Kick 2,Strike,2,Legs,0,0.8,8,3 +Kick 3,Strike,3,Legs,0,0.85,12,5 +Headbutt 1,Strike,1,Head,0,0.8,5,1 +Headbutt 2,Strike,2,Head,0,0.9,9,1 +Headbutt 3,Strike,3,Head,0,1.0,12,3 +Bodystrike 1,Strike,1,All,0,0.75,6,1 +Bodystrike 2,Strike,2,All,0,0.85,8,2 +Bodystrike 3,Strike,3,All,0,0.9,10,3 +Arm Hold 1,Hold,1,All,Arms,0.4,5,1 +Arm Hold 2,Hold,2,All,Arms,0.5,8,2 +Arm Hold 3,Hold,3,All,Arms,0.6,10,3 +Leg Hold 1,Hold,1,All,Legs,0.4,5,1 +Leg Hold 2,Hold,2,All,Legs,0.5,8,2 +Leg Hold 3,Hold,3,All,Legs,0.6,10,3 +Head Hold 1,Hold,1,All,Head,0.3,5,1 +Head Hold 2,Hold,2,All,Head,0.5,8,2 +Head Hold 3,Hold,3,All,Head,0.6,10,3 +Body Hold 1,Hold,1,All,Body,0.5,5,1 +Body Hold 2,Hold,2,All,Body,0.6,8,2 +Body Hold 3,Hold,3,All,Body,0.7,10,3 +Aerial Arm 1,Aerial,1,All,0,0.5,8,3 +Aerial Arm 2,Aerial,2,All,0,0.6,12,4 +Aerial Arm 3,Aerial,3,All,0,0.7,14,5 +Aerial Leg 1,Aerial,1,All,0,0.4,8,3 +Aerial Leg 2,Aerial,2,All,0,0.5,12,4 +Aerial Leg 3,Aerial,3,All,0,0.6,14,5 +Aerial Body 1,Aerial,1,All,0,0.4,9,4 +Aerial Body 2,Aerial,2,All,0,0.5,14,5 +Aerial Body 3,Aerial,3,All,0,0.7,17,6 +Pin,Pin,1,All,0,0.3,0,1 +Arms Pin,Pin,2,All,Arms,0.4,0,3 +Legs Pin,Pin,2,All,Legs,0.4,0,3 +Body Pin,Pin,2,All,Body,0.4,0,3 +Super Pin,Pin,3,All,All,0.5,0,5 \ No newline at end of file diff --git a/UPWG/Elite/Elite.csproj b/UPWG/Elite/Elite.csproj new file mode 100755 index 0000000..f0a8082 --- /dev/null +++ b/UPWG/Elite/Elite.csproj @@ -0,0 +1,11 @@ + + + + net5.0 + + + + + + + diff --git a/UPWG/Elite/Engine.cs b/UPWG/Elite/Engine.cs new file mode 100755 index 0000000..eadb57a --- /dev/null +++ b/UPWG/Elite/Engine.cs @@ -0,0 +1,81 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Elite +{ + public class Engine : Game + { + public static Engine Instance; + + private GraphicsDeviceManager _graphicsDeviceManager; + private int _width; + private int _height; + private Color _clearColor; + + private Scene _currentScene; + private Scene _nextScene; + + private float _elapsed; + + public static Scene Scene + { + get => Instance._currentScene; + + set + { + if (Instance._currentScene != null) + Instance._nextScene = value; + else + Instance._currentScene = value; + } + } + + public static float Elapsed => Instance._elapsed; + + public Engine(int width, int height, Color clearColor) + { + Instance = this; + + _graphicsDeviceManager = new GraphicsDeviceManager(this); + Content.RootDirectory = "Content"; + IsMouseVisible = true; + IsFixedTimeStep = false; + _clearColor = clearColor; + _width = width; + _height = height; + } + + protected override void Initialize() + { + base.Initialize(); + _graphicsDeviceManager.PreferredBackBufferWidth = _width; + _graphicsDeviceManager.PreferredBackBufferHeight = _height; + _graphicsDeviceManager.ApplyChanges(); + } + + protected override void Update(GameTime gameTime) + { + base.Update(gameTime); + _elapsed = (float) gameTime.ElapsedGameTime.TotalSeconds; + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || + Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + _currentScene?.Update(_elapsed); + + if (_nextScene != null) + { + _currentScene = _nextScene; + _nextScene = null; + } + } + + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(_clearColor); + + _currentScene.Draw(_elapsed); + } + } +} \ No newline at end of file diff --git a/UPWG/Elite/Entity.cs b/UPWG/Elite/Entity.cs new file mode 100755 index 0000000..d86a100 --- /dev/null +++ b/UPWG/Elite/Entity.cs @@ -0,0 +1,23 @@ +namespace Elite +{ + public class Component + { + public Entity Entity; + } + + public interface IUpdateable + { + void Update(); + } + + public interface + + public class Entity + { + private static uint _nextId; + + public string Name; + public readonly int Id; + private readonly FastList + + WinExe + net5.0 + false + false + + + app.manifest + Icon.ico + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UPWG/Manhattan/Program.cs b/UPWG/Manhattan/Program.cs new file mode 100755 index 0000000..9ca5c37 --- /dev/null +++ b/UPWG/Manhattan/Program.cs @@ -0,0 +1,16 @@ +using System; +using Elite; +using Microsoft.Xna.Framework; + +namespace Manhattan +{ + public static class Program + { + [STAThread] + static void Main() + { + using (var game = new ManhattanGame(1280, 720, Color.Bisque)) + game.Run(); + } + } +} \ No newline at end of file diff --git a/UPWG/Manhattan/app.manifest b/UPWG/Manhattan/app.manifest new file mode 100755 index 0000000..3924952 --- /dev/null +++ b/UPWG/Manhattan/app.manifest @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + diff --git a/UPWG/UPWG.Core/Battle.cs b/UPWG/UPWG.Core/Battle.cs new file mode 100755 index 0000000..522084e --- /dev/null +++ b/UPWG/UPWG.Core/Battle.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; + +namespace UPWG.Core +{ + public enum WrestlerPosition + { + InRing, + OnRopes, + OutsideRing, + } + + public enum MomentumLevel + { + None = 0, + Low = 1, + Medium = 2, + High = 3, + } + + + public enum HitResult + { + Miss, + Hit, + Critical, + } + + public class MoveResult + { + public HitResult Result; + + // what did this do to the recipient? + public uint RecipientDamage; + public int RecipientMomentumDelta; + public BodyParts RecipientRestrained; + + // and what did this do to us? + public uint SelfDamage; + public int SelfMomentumDelta; + public int SelfPopularityDelta; + + public MoveResult( + HitResult result, + uint recipientDamage, + int recipientMomentumDelta = 0, + BodyParts restrained = 0, + uint selfDamage = 0, + int selfMomentumDelta = 0, + int selfPopularityDelta = 0 + ) + { + Result = result; + RecipientDamage = recipientDamage; + RecipientMomentumDelta = recipientMomentumDelta; + RecipientRestrained = restrained; + SelfDamage = selfDamage; + SelfMomentumDelta = selfMomentumDelta; + SelfPopularityDelta = selfPopularityDelta; + } + } + + interface IGoal + { + public void ProcessMove(Move move, MoveResult result); + public bool IsComplete(); + } + + class MaxMomentumGoal : IGoal + { + public int GoalMomentum { get; } + private int _maxMomentum; + private int _curMomentum; + + MaxMomentumGoal(int goalMomentum) + { + GoalMomentum = goalMomentum; + } + + public void ProcessMove(Move move, MoveResult result) + { + _curMomentum += result.SelfMomentumDelta; + if (_curMomentum > _maxMomentum) + _maxMomentum = _curMomentum; + } + + public bool IsComplete() + { + return _maxMomentum >= GoalMomentum; + } + } + + public enum MatchStatus + { + InProgress, + Pinfall, + Submission, + CountOut, + Disqualification, + } + + public class MoveOptions + { + // this class represents the available moves + public string Strike { get; set; } + public string Grapple { get; set; } + public string Other { get; set; } + public string Special { get; set; } + } + + public class Battle + { + private static Random _random = new Random(); + + public enum ParticipantRole + { + LeftSingle, + RightSingle, + LeftTag1, + LeftTag2, + RightTag1, + RightTag2, + } + + public struct Participant + { + public Wrestler Wrestler; + public WrestlerPosition Position; + public BodyParts Restrained; + public BodyParts Injured; + public MomentumLevel Momentum; + public uint MaxFatigue; + public uint CurFatigue; + + public Participant(Wrestler wrestler) + { + Wrestler = wrestler; + Position = WrestlerPosition.InRing; + Restrained = 0; + Injured = 0; + Momentum = MomentumLevel.None; + CurFatigue = MaxFatigue = wrestler.Fortitude * 10; + } + } + + private uint _turnClock; + private ParticipantRole _playerRole; + + public Dictionary Participants { get; } + public MatchStatus Status { get; private set; } + public ParticipantRole[] UpcomingTurns { get; set; } + public MoveOptions CurrentMoveOptions { get; private set; } + + private void InitializeState() + { + _turnClock = 1; + Status = MatchStatus.InProgress; + UpcomingTurns = new ParticipantRole[5]; + CalcUpcomingTurns(); + CurrentMoveOptions = new MoveOptions(); + CalcPlayerMoveOptions(); + } + + public Battle(Wrestler leftWrestler, Wrestler rightWrestler) + { + Participants = new Dictionary + { + [ParticipantRole.LeftSingle] = new Participant(leftWrestler), + [ParticipantRole.RightSingle] = new Participant(rightWrestler) + }; + _playerRole = ParticipantRole.LeftSingle; + InitializeState(); + } + + public Battle(Wrestler leftOne, Wrestler leftTwo, Wrestler rightOne, Wrestler rightTwo) + { + Participants = new Dictionary + { + [ParticipantRole.LeftTag1] = new Participant(leftOne), + [ParticipantRole.LeftTag2] = new Participant(leftTwo), + [ParticipantRole.RightTag1] = new Participant(rightOne), + [ParticipantRole.RightTag2] = new Participant(rightTwo) + }; + _playerRole = ParticipantRole.LeftTag1; + InitializeState(); + } + + // Player Interaction + + public void CalcPlayerMoveOptions() + { + // TODO: revisit for other match types + var player = Participants[_playerRole]; + var target = Participants[ParticipantRole.RightSingle]; + // TODO: revisit for other move types + CurrentMoveOptions.Strike = GetBestAvailableMove(player, MoveType.Strike, target); + CurrentMoveOptions.Grapple = GetBestAvailableMove(player, MoveType.Aerial, target); + CurrentMoveOptions.Special = "Pin"; + CurrentMoveOptions.Other = "Enzugiri **"; + } + + public MoveResult ApplyMove(string moveName) + { + var move = MoveDatabase.Lookup(moveName); + return ApplyMove(_playerRole, move, ParticipantRole.RightSingle); + } + + public bool IsPlayerTurn() + { + return UpcomingTurns[0] == _playerRole; + } + + public void EndTurn() + { + _turnClock += 1; + } + + // CPU Control + + public void GetCpuMove(out string move, out ParticipantRole target) + { + move = "Chop 1"; + target = ParticipantRole.LeftSingle; + } + + // Internals + + private MoveResult ApplyMove(ParticipantRole performerPos, Move move, ParticipantRole recipientPos) + { + var performer = Participants[performerPos]; + var recipient = Participants[recipientPos]; + + // can this move be done? + if (!MoveIsValid(performer, move, recipient)) + throw new InvalidMoveException(); + + var hitResult = CheckForHit(performer, move, recipient); + + // if move misses, no damage + if (hitResult == HitResult.Miss) + return new MoveResult(hitResult, 0); + + uint damage = 0; + uint selfDamage = 0; + switch (move.Type) + { + case MoveType.Strike: + damage = (uint) (move.BaseOffense * performer.Wrestler.DamageMult); + break; + case MoveType.Aerial: + damage = (uint) (move.BaseOffense * performer.Wrestler.DamageMult); + selfDamage = 5; + break; + case MoveType.Hold: + break; + case MoveType.Throw: + damage = (uint) (move.BaseOffense * performer.Wrestler.DamageMult); + break; + case MoveType.Pin: + break; + } + + // TODO: how does momentum/popularity work? + + return new MoveResult(hitResult, damage, 0, 0, selfDamage, 0, 0); + } + + private void CalcUpcomingTurns() + { + for (var i = 0; i < UpcomingTurns.Length; i++) + { + // TODO: use speed to determine turn order + if ((_turnClock + i) % 2 == 1) + { + UpcomingTurns[i] = ParticipantRole.LeftSingle; + } + else + { + UpcomingTurns[i] = ParticipantRole.RightSingle; + } + } + } + + private static readonly double[] HitMod = {0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4}; + private static readonly double[] EvadeMod = {-0.1, -0.1, -0.05, -0.05, 0, 0.05, 0.05, 0.1, 0.1, 0.15}; + + private static HitResult CheckForHit(Participant performer, Move move, Participant recipient) + { + var critChance = 0.05; + uint attackStat = 0; + + switch (move.Type) + { + case MoveType.Strike: + attackStat = performer.Wrestler.Strength; + break; + case MoveType.Aerial: + attackStat = performer.Wrestler.Agility; + break; + case MoveType.Pin: + attackStat = performer.Wrestler.Strength > performer.Wrestler.Agility + ? performer.Wrestler.Strength + : performer.Wrestler.Agility; + break; + case MoveType.Hold: + attackStat = performer.Wrestler.Grappling; + break; + case MoveType.Throw: + attackStat = performer.Wrestler.Grappling; + break; + } + + var hitChance = move.BaseChance * HitMod[attackStat] - EvadeMod[recipient.Wrestler.Agility]; + + var hitRoll = _random.NextDouble(); + if (hitRoll < critChance) + return HitResult.Critical; + else if (hitRoll <= hitChance) + return HitResult.Hit; + else + return HitResult.Miss; + } + + private static bool MoveIsValid(Participant performer, Move move, Participant recipient) + { + // TODO: check position of performer and recipient + + // is there an issue affecting the required body part + var requiredPartsRestrained = performer.Restrained & move.PartsUtilized; + if (requiredPartsRestrained != 0) + return false; + + // does wrestler have momentum? + if (performer.Momentum < (MomentumLevel) (move.Rank - 1)) + return false; + + return true; + } + + private static string GetBestAvailableMove(Participant participant, MoveType type, Participant recipient) + { + List best = new(); + var bestRank = -1; + foreach (var (moveName, expertise) in participant.Wrestler.MoveSet) + { + var move = MoveDatabase.Lookup(moveName); + // accumulate list of best valid moves of the type + if (move.Type == type && MoveIsValid(participant, move, recipient)) + { + // for now just use rank, eventually this may be dynamic + if (move.Rank > bestRank) + { + bestRank = (int) move.Rank; + best.Clear(); + best.Add(move); + } + else if (move.Rank == bestRank) + { + best.Add(move); + } + } + } + + return best[_random.Next(best.Count)].Name; + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Core/Moves.cs b/UPWG/UPWG.Core/Moves.cs new file mode 100755 index 0000000..0395ddd --- /dev/null +++ b/UPWG/UPWG.Core/Moves.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using CsvHelper; + +namespace UPWG.Core +{ + public class InvalidMoveException : Exception + { + public InvalidMoveException() + { + } + + public InvalidMoveException(string message) : base(message) + { + } + } + + public enum MoveType + { + Strike, + Aerial, + Hold, + Throw, + Pin, + } + + [Flags] + public enum BodyParts + { + Arms = 1, + Legs = 2, + Head = 4, + Body = 8, + All = Arms | Legs | Head | Body, + } + + public class Move + { + public string Name { get; set; } + public MoveType Type { get; set; } + + public uint Rank { get; set; } + + // can it be done? + public double BaseChance { get; set; } + + //public MomentumLevel RequiredMomentum { get; set; } + public BodyParts PartsRestrained { get; set; } + public BodyParts PartsUtilized { get; set; } + + // what does it do? + public int BasePopularity { get; set; } + public int BaseOffense { get; set; } + } + + public static class MoveDatabase + { + public static readonly Dictionary Moves = new(); + + public static void Initialize() + { + using var reader = new StreamReader("Data/moves.csv"); + using var csv = new CsvReader(reader, CultureInfo.InvariantCulture); + var records = csv.GetRecords(); + foreach (var record in records) + { + Moves[record.Name] = record; + } + } + + public static Move Lookup(string name) + { + return Moves[name]; + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Core/StateMachine.cs b/UPWG/UPWG.Core/StateMachine.cs new file mode 100755 index 0000000..43205f8 --- /dev/null +++ b/UPWG/UPWG.Core/StateMachine.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace UPWG.Core +{ + public class StateMachine + where T : Enum + { + public T CurrentState { get; private set; } + public double TimeInState { get; private set; } + private Dictionary _transitions; + + public StateMachine(T initial) + { + CurrentState = initial; + TimeInState = 0; + _transitions = new Dictionary(); + } + + public void AddTransition(T from, T to, double after = 0) + { + _transitions[from] = (after, to); + } + + public void SetState(T newState) + { + CurrentState = newState; + TimeInState = 0; + } + + public void Update(double elapsed) + { + TimeInState += elapsed; + + if (!_transitions.ContainsKey(CurrentState)) + return; + + var (maxTime, nextState) = _transitions[CurrentState]; + if (TimeInState > maxTime) + { + SetState(nextState); + } + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Core/UPWG.Core.csproj b/UPWG/UPWG.Core/UPWG.Core.csproj new file mode 100755 index 0000000..ed99cc1 --- /dev/null +++ b/UPWG/UPWG.Core/UPWG.Core.csproj @@ -0,0 +1,11 @@ + + + + net5.0 + + + + + + + diff --git a/UPWG/UPWG.Core/Wrestler.cs b/UPWG/UPWG.Core/Wrestler.cs new file mode 100755 index 0000000..989147d --- /dev/null +++ b/UPWG/UPWG.Core/Wrestler.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; + +namespace UPWG.Core +{ + public enum WeightClass + { + Light, + Normal, + Heavy, + } + + public class Wrestler + { + public string Name { get; } + public uint Strength { get; } // used + public uint Grappling { get; } + public uint Agility { get; } + public uint Fortitude { get; } + public uint Charisma { get; } + public WeightClass Weight { get; } + public Dictionary MoveSet; + + public Wrestler(string name, uint strength, uint grappling, uint agility, uint fortitude, uint charisma, + WeightClass weight + ) + { + Name = name; + Strength = strength; + Grappling = grappling; + Agility = agility; + Fortitude = fortitude; + Charisma = charisma; + Weight = weight; + MoveSet = new Dictionary(); + foreach (var (move, _) in MoveDatabase.Moves) + { + MoveSet[move] = 0; + } + } + + public double DamageMult + { + get + { + switch (Strength) + { + case <= 2: return 0.5; + case <= 4: return 1.0; + case <= 6: return 1.2; + case <= 8: return 1.4; + default: return 1.7; + } + } + } + + public uint GetExpertise(string moveName) + { + return MoveSet.ContainsKey(moveName) ? MoveSet[moveName] : 0; + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Main/Animation.cs b/UPWG/UPWG.Main/Animation.cs new file mode 100755 index 0000000..59989e8 --- /dev/null +++ b/UPWG/UPWG.Main/Animation.cs @@ -0,0 +1,160 @@ +using System; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Elite; + +namespace UPWG.Main +{ + internal enum MovementType + { + Run, + Jump, + Flip, + } + + internal enum RingPosition + { + // these use cardinal directions (picture a compass rose laid over the ring) + // they will be flipped when animations are reversed + SouthWest, + West, + NorthWest, + North, + NorthEast, + East, + SouthEast, + South, + Center, + } + + abstract class BattleAnimation + { + protected double ElapsedSeconds; + protected readonly double RunSeconds; + + protected BattleAnimation(double runSeconds) + { + ElapsedSeconds = 0; + RunSeconds = runSeconds; + } + + public bool Done() + { + return ElapsedSeconds > RunSeconds; + } + + public abstract void Update(double elapsed, ref Vector2 pos, ref float rotation); + } + + class Run : BattleAnimation + { + private Vector2 _start; + private Vector2 _end; + + public Run(double runSeconds, RingPosition from, RingPosition to) : base(runSeconds) + { + _start = RingPositionToVector2(from); + _end = RingPositionToVector2(to); + } + + public override void Update(double elapsed, ref Vector2 pos, ref float rotation) + { + ElapsedSeconds += elapsed; + pos = Vector2.Lerp(_start, _end, (float) (ElapsedSeconds / RunSeconds)); + } + + private const float EastRopesX = 200; + private const float WestRopesX = 500; + private const float CenterX = (EastRopesX + WestRopesX) / 2; + private const float NorthRopesY = 100; + private const float SouthRopesY = 300; + private const float CenterY = (NorthRopesY + SouthRopesY) / 2; + + public static Vector2 RingPositionToVector2(RingPosition position) + { + return position switch + { + RingPosition.North => new Vector2(CenterX, NorthRopesY), + RingPosition.NorthEast => new Vector2(EastRopesX, NorthRopesY), + RingPosition.East => new Vector2(EastRopesX, CenterY), + RingPosition.SouthEast => new Vector2(EastRopesX, SouthRopesY), + RingPosition.South => new Vector2(CenterX, SouthRopesY), + RingPosition.SouthWest => new Vector2(WestRopesX, SouthRopesY), + RingPosition.West => new Vector2(WestRopesX, CenterY), + RingPosition.NorthWest => new Vector2(WestRopesX, NorthRopesY), + RingPosition.Center => new Vector2(CenterX, CenterY), + _ => throw new ArgumentOutOfRangeException(nameof(position)) + }; + } + } + + class Spin : BattleAnimation + { + private float _angleFrom; + private float _angleTo; + + public Spin(double runSeconds, float angleFrom, float angleTo) : base(runSeconds) + { + _angleFrom = angleFrom; + _angleTo = angleTo; + } + + public override void Update(double elapsed, ref Vector2 pos, ref float rotation) + { + ElapsedSeconds += elapsed; + rotation = MathHelper.Lerp(_angleFrom, _angleTo, (float) (ElapsedSeconds / RunSeconds)); + } + } + + public class BattleSprite : IBatchDrawable + { + private const int Width = 128; + private const int Height = 128; + + private Texture2D _texture; + private Vector2 _origin; + private Vector2 _pos; + private float _rotation; + private BattleAnimation[] _animations; + private int _animationNum; + + public BattleSprite(GraphicsDevice graphicsDevice, string filename) + { + var fileStream = new FileStream("Content/jason.jpg", FileMode.Open); + _texture = Texture2D.FromStream(graphicsDevice, fileStream); + fileStream.Dispose(); + + _pos = new Vector2(100, 200); + _origin = new Vector2(_texture.Width / 2, _texture.Height / 2); + + _animations = new BattleAnimation[] + { + new Run(1, RingPosition.West, RingPosition.East), + new Run(1, RingPosition.East, RingPosition.West), + //new Run(1, RingPosition.West, RingPosition.East), + //new Run(1, RingPosition.East, RingPosition.Center), + //new Run(0.2, RingPosition.Center, RingPosition.Center), + new Spin(4, 0, 3.14f), + }; + _animationNum = 0; + } + + public void Update(double elapsedSeconds) + { + if (_animationNum < _animations.Length) + { + _animations[_animationNum].Update(elapsedSeconds, ref _pos, ref _rotation); + if (_animations[_animationNum].Done()) + _animationNum += 1; + } + } + + + public void Draw(SpriteBatch batch) + { + batch.Draw(_texture, new Rectangle((int) _pos.X, (int) _pos.Y, Width, Height), null, Color.White, + _rotation, _origin, SpriteEffects.None, 0); + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Main/BattleTest.cs b/UPWG/UPWG.Main/BattleTest.cs new file mode 100755 index 0000000..dd564a1 --- /dev/null +++ b/UPWG/UPWG.Main/BattleTest.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using UPWG.Core; +using Elite; + +namespace UPWG.Main +{ + public enum TurnState + { + WaitingForInput, + PlayerMoveAnimation, + PlayerMoveResult, + OpponentMoveAnimation, + OpponentMoveResult, + } + + public class BattleTest : Game + { + private GraphicsDeviceManager _graphicsDeviceManager; + private SpriteFont _spriteFont; + private Wrestler _p1; + private Wrestler _p2; + private Battle _match; + private StateMachine _turnState; + private BattleSprite _sprite1; + private BattleSprite _sprite2; + private ScaledRenderTarget _target; + private Sprite _ring; + + public BattleTest() + { + _graphicsDeviceManager = new GraphicsDeviceManager(this); + Content.RootDirectory = "Content"; + IsMouseVisible = true; + MoveDatabase.Initialize(); + + _turnState = new StateMachine(TurnState.WaitingForInput); + _turnState.AddTransition(TurnState.PlayerMoveAnimation, TurnState.PlayerMoveResult, 1); + _turnState.AddTransition(TurnState.PlayerMoveResult, TurnState.OpponentMoveAnimation, 1); + _turnState.AddTransition(TurnState.OpponentMoveAnimation, TurnState.OpponentMoveResult, 1); + _turnState.AddTransition(TurnState.OpponentMoveResult, TurnState.WaitingForInput, 1); + + _p1 = new Wrestler("CM Punk", 7, 9, 5, 8, 10, WeightClass.Normal); + _p2 = new Wrestler("John Cena", 8, 8, 5, 8, 8, WeightClass.Normal); + _match = new Battle(_p1, _p2); + } + + protected override void Initialize() + { + _graphicsDeviceManager.PreferredBackBufferWidth = 1281; + _graphicsDeviceManager.PreferredBackBufferHeight = 720; + _graphicsDeviceManager.ApplyChanges(); + base.Initialize(); + } + + protected override void LoadContent() + { + _spriteFont = Content.Load("Arial"); + _target = new ScaledRenderTarget(GraphicsDevice, 640, 360, 2.0f, _spriteFont); + _sprite1 = new BattleSprite(GraphicsDevice, ""); + _sprite2 = new BattleSprite(GraphicsDevice, ""); + _ring = new Sprite(GraphicsDevice, "Content/oga/ring_7.png"); + + _target.DrawList.Add(_ring); + _target.DrawList.Add(_sprite1); + _target.DrawList.Add(_sprite2); + } + + protected override void UnloadContent() + { + } + + protected override void Update(GameTime gameTime) + { + var keyboardState = Keyboard.GetState(); + MoveResult moveResult; + + _turnState.Update(gameTime.ElapsedGameTime.TotalSeconds); + _sprite1.Update(gameTime.ElapsedGameTime.TotalSeconds); + _sprite2.Update(gameTime.ElapsedGameTime.TotalSeconds); + + if (keyboardState.IsKeyDown(Keys.Escape)) + Exit(); + + if (_match.IsPlayerTurn() && _turnState.CurrentState == TurnState.WaitingForInput) + { + if (keyboardState.IsKeyDown(Keys.A)) + { + moveResult = _match.ApplyMove(_match.CurrentMoveOptions.Strike); + _turnState.SetState(TurnState.PlayerMoveAnimation); + } + else if (keyboardState.IsKeyDown(Keys.S)) + { + moveResult = _match.ApplyMove(_match.CurrentMoveOptions.Grapple); + _turnState.SetState(TurnState.PlayerMoveAnimation); + } + else if (keyboardState.IsKeyDown(Keys.D)) + { + moveResult = _match.ApplyMove(_match.CurrentMoveOptions.Other); + _turnState.SetState(TurnState.PlayerMoveAnimation); + } + else if (keyboardState.IsKeyDown(Keys.F)) + { + moveResult = _match.ApplyMove(_match.CurrentMoveOptions.Special); + _turnState.SetState(TurnState.PlayerMoveAnimation); + } + } + else + { + _match.GetCpuMove(out var move, out var target); + moveResult = _match.ApplyMove(move); + } + + base.Update(gameTime); + } + + private void DrawWrestler(Battle.ParticipantRole role, Battle.Participant participant) + { + int x, y; + + switch (role) + { + case Battle.ParticipantRole.LeftSingle: + x = 100; + y = 100; + break; + default: + x = 1280 - 300; + y = 100; + break; + } + + _target.DrawString( + _spriteFont, $"{participant.Wrestler.Name}", + new Vector2(x, y), Color.Pink + ); + _target.DrawString( + _spriteFont, $"Momentum: {participant.Momentum}", + new Vector2(x, y + 20), Color.Pink + ); + _target.DrawString( + _spriteFont, $"Restrained: {participant.Restrained} Injured: {participant.Injured}", + new Vector2(x, y + 40), Color.Pink + ); + _target.DrawString( + _spriteFont, $"{participant.CurFatigue} / {participant.MaxFatigue}", + new Vector2(x, y + 60), Color.Pink + ); + } + + private void DrawMoveOptions(MoveOptions moveOptions) + { + _target.DrawString(_spriteFont, $"A: {moveOptions.Strike}", + new Vector2(200, 200), Color.Yellow); + _target.DrawString(_spriteFont, $"S: {moveOptions.Grapple}", + new Vector2(200, 210), Color.Yellow); + _target.DrawString(_spriteFont, $"D: {moveOptions.Special}", + new Vector2(200, 220), Color.Yellow); + _target.DrawString(_spriteFont, $"F: {moveOptions.Other}", + new Vector2(200, 230), Color.Yellow); + } + + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.Red); + + const int x = 300; + var y = 300; + var n = 1; + + _target.Draw(gameTime); + _target.SpriteBatch.Begin(); + foreach (var (key, value) in _match.Participants) + { + DrawWrestler(key, value); + } + + _target.DrawString(_spriteFont, $"{_turnState.CurrentState}", new Vector2(_target.Width / 2, 0), + Color.White); + foreach (var turn in _match.UpcomingTurns) + { + _target.DrawString(_spriteFont, $"{n}: {turn}", new Vector2(x, y), Color.Green); + n += 1; + y += 12; + } + + if (_match.IsPlayerTurn()) + DrawMoveOptions(_match.CurrentMoveOptions); + _target.SpriteBatch.End(); + + base.Draw(gameTime); + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Main/Content/Arial.spritefont b/UPWG/UPWG.Main/Content/Arial.spritefont new file mode 100755 index 0000000..72b9cbb --- /dev/null +++ b/UPWG/UPWG.Main/Content/Arial.spritefont @@ -0,0 +1,60 @@ + + + + + + + Arial + + + 12 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + diff --git a/UPWG/UPWG.Main/Content/Content.mgcb b/UPWG/UPWG.Main/Content/Content.mgcb new file mode 100755 index 0000000..1f21e35 --- /dev/null +++ b/UPWG/UPWG.Main/Content/Content.mgcb @@ -0,0 +1,20 @@ +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin/$(Platform) +/intermediateDir:obj/$(Platform) +/platform:Windows +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin Arial.spritefont +/importer:FontDescriptionImporter +/processor:FontDescriptionProcessor +/processorParam:PremultiplyAlpha=True +/processorParam:TextureFormat=Compressed +/build:Arial.spritefont diff --git a/UPWG/UPWG.Main/Icon.bmp b/UPWG/UPWG.Main/Icon.bmp new file mode 100755 index 0000000000000000000000000000000000000000..2b481653e241818d2894e4ef33234eaff9aad701 GIT binary patch literal 262282 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 RJoIeLznU7AhJ`o~_y6Yy51#-4 literal 0 HcmV?d00001 diff --git a/UPWG/UPWG.Main/Program.cs b/UPWG/UPWG.Main/Program.cs new file mode 100755 index 0000000..662f87d --- /dev/null +++ b/UPWG/UPWG.Main/Program.cs @@ -0,0 +1,14 @@ +using System; + +namespace UPWG.Main +{ + public static class Program + { + [STAThread] + static void Main() + { + using (var game = new BattleTest()) + game.Run(); + } + } +} \ No newline at end of file diff --git a/UPWG/UPWG.Main/UPWG.Main.csproj b/UPWG/UPWG.Main/UPWG.Main.csproj new file mode 100755 index 0000000..c02d941 --- /dev/null +++ b/UPWG/UPWG.Main/UPWG.Main.csproj @@ -0,0 +1,36 @@ + + + WinExe + net5.0 + false + false + UPWG.Main + + + app.manifest + Icon.ico + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UPWG/UPWG.Main/app.manifest b/UPWG/UPWG.Main/app.manifest new file mode 100755 index 0000000..edbd3d8 --- /dev/null +++ b/UPWG/UPWG.Main/app.manifest @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + diff --git a/UPWG/UPWG.sln b/UPWG/UPWG.sln new file mode 100755 index 0000000..f0ecaf5 --- /dev/null +++ b/UPWG/UPWG.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UPWG.Core", "UPWG.Core\UPWG.Core.csproj", "{C8358627-AEAF-473B-B7B9-FD670A366095}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UPWG.Main", "UPWG.Main\UPWG.Main.csproj", "{4D7C34FD-5B50-454C-93ED-E805F261F48B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elite", "Elite\Elite.csproj", "{090DD346-EFA7-4907-8ACB-757D63F9F4B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manhattan", "Manhattan\Manhattan.csproj", "{9FD7026A-D69B-40D8-AC7B-B77023CF5444}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C8358627-AEAF-473B-B7B9-FD670A366095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8358627-AEAF-473B-B7B9-FD670A366095}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8358627-AEAF-473B-B7B9-FD670A366095}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8358627-AEAF-473B-B7B9-FD670A366095}.Release|Any CPU.Build.0 = Release|Any CPU + {4D7C34FD-5B50-454C-93ED-E805F261F48B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D7C34FD-5B50-454C-93ED-E805F261F48B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D7C34FD-5B50-454C-93ED-E805F261F48B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D7C34FD-5B50-454C-93ED-E805F261F48B}.Release|Any CPU.Build.0 = Release|Any CPU + {090DD346-EFA7-4907-8ACB-757D63F9F4B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {090DD346-EFA7-4907-8ACB-757D63F9F4B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {090DD346-EFA7-4907-8ACB-757D63F9F4B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {090DD346-EFA7-4907-8ACB-757D63F9F4B5}.Release|Any CPU.Build.0 = Release|Any CPU + {9FD7026A-D69B-40D8-AC7B-B77023CF5444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FD7026A-D69B-40D8-AC7B-B77023CF5444}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FD7026A-D69B-40D8-AC7B-B77023CF5444}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FD7026A-D69B-40D8-AC7B-B77023CF5444}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal