瀏覽代碼

CatapultWars updated to SDK project and MG 3.8.*

CartBlanche 2 周之前
父節點
當前提交
fa42a71e32
共有 84 個文件被更改,包括 4806 次插入0 次删除
  1. 27 0
      CatapultWars/.vscode/launch.json
  2. 33 0
      CatapultWars/.vscode/tasks.json
  3. 48 0
      CatapultWars/CatapaultWars.sln
  4. 584 0
      CatapultWars/Core/Catapult/Catapult.cs
  5. 83 0
      CatapultWars/Core/Catapult/CatapultGame.cs
  6. 13 0
      CatapultWars/Core/CatapultWars.Core.csproj
  7. 二進制
      CatapultWars/Core/Content/Background.png
  8. 二進制
      CatapultWars/Core/Content/Fonts/HUDFont.xnb
  9. 二進制
      CatapultWars/Core/Content/Fonts/MenuFont.xnb
  10. 二進制
      CatapultWars/Core/Content/Game.ico
  11. 二進制
      CatapultWars/Core/Content/Sounds/BoulderHit.wav
  12. 二進制
      CatapultWars/Core/Content/Sounds/BoulderHit.xnb
  13. 二進制
      CatapultWars/Core/Content/Sounds/CatapultExplosion.wav
  14. 二進制
      CatapultWars/Core/Content/Sounds/CatapultExplosion.xnb
  15. 二進制
      CatapultWars/Core/Content/Sounds/CatapultFire.wav
  16. 二進制
      CatapultWars/Core/Content/Sounds/CatapultFire.xnb
  17. 二進制
      CatapultWars/Core/Content/Sounds/Lose.wav
  18. 二進制
      CatapultWars/Core/Content/Sounds/Lose.xnb
  19. 二進制
      CatapultWars/Core/Content/Sounds/RopeStretch.wav
  20. 二進制
      CatapultWars/Core/Content/Sounds/RopeStretch.xnb
  21. 二進制
      CatapultWars/Core/Content/Sounds/Win.wav
  22. 二進制
      CatapultWars/Core/Content/Sounds/Win.xnb
  23. 二進制
      CatapultWars/Core/Content/Textures/Ammo/rock_ammo.xnb
  24. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/blank.xnb
  25. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/cloud1.xnb
  26. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/cloud2.xnb
  27. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/defeat.xnb
  28. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/gameplay_screen.xnb
  29. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/instructions.xnb
  30. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/mountain.xnb
  31. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/sky.xnb
  32. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/title_screen.xnb
  33. 二進制
      CatapultWars/Core/Content/Textures/Backgrounds/victory.xnb
  34. 63 0
      CatapultWars/Core/Content/Textures/Catapults/AnimationsDef.xml
  35. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Blue/blueDestroyed/blueCatapult_destroyed.xnb
  36. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Blue/blueFire/blueCatapult_fire.xnb
  37. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Blue/blueIdle/blueIdle.xnb
  38. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Blue/bluePullback/blueCatapult_Pullback.xnb
  39. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Fire_Miss/fire_miss.xnb
  40. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Hit_Smoke/smoke.xnb
  41. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Red/redDestroyed/redCatapult_destroyed.xnb
  42. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Red/redFire/redCatapult_fire.xnb
  43. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Red/redIdle/redIdle.xnb
  44. 二進制
      CatapultWars/Core/Content/Textures/Catapults/Red/redPullback/redCatapult_Pullback.xnb
  45. 二進制
      CatapultWars/Core/Content/Textures/HUD/Arrow.xnb
  46. 二進制
      CatapultWars/Core/Content/Textures/HUD/ammoType.xnb
  47. 二進制
      CatapultWars/Core/Content/Textures/HUD/hudBackground.xnb
  48. 二進制
      CatapultWars/Core/Content/Textures/HUD/windArrow.xnb
  49. 43 0
      CatapultWars/Core/Content/app.manifest
  50. 67 0
      CatapultWars/Core/Players/AI.cs
  51. 181 0
      CatapultWars/Core/Players/Human.cs
  52. 82 0
      CatapultWars/Core/Players/Player.cs
  53. 38 0
      CatapultWars/Core/Players/PlayerIndexEventArgs.cs
  54. 436 0
      CatapultWars/Core/ScreenManager/ScreenManager.cs
  55. 46 0
      CatapultWars/Core/Screens/BackgroundScreen.cs
  56. 380 0
      CatapultWars/Core/Screens/GameScreen.cs
  57. 483 0
      CatapultWars/Core/Screens/GameplayScreen.cs
  58. 174 0
      CatapultWars/Core/Screens/InstructionsScreen.cs
  59. 70 0
      CatapultWars/Core/Screens/MainMenuScreen.cs
  60. 173 0
      CatapultWars/Core/Screens/MenuEntry.cs
  61. 309 0
      CatapultWars/Core/Screens/MenuScreen.cs
  62. 107 0
      CatapultWars/Core/Screens/PauseScreen.cs
  63. 152 0
      CatapultWars/Core/Utility/Animation.cs
  64. 194 0
      CatapultWars/Core/Utility/AudioManager.cs
  65. 337 0
      CatapultWars/Core/Utility/InputState.cs
  66. 51 0
      CatapultWars/Core/Utility/MouseGestureType.cs
  67. 177 0
      CatapultWars/Core/Utility/Projectile.cs
  68. 30 0
      CatapultWars/Platforms/Android/Activity1.cs
  69. 18 0
      CatapultWars/Platforms/Android/AndroidManifest.xml
  70. 33 0
      CatapultWars/Platforms/Android/CatapultWars.Android.csproj
  71. 44 0
      CatapultWars/Platforms/Android/Resources/AboutResources.txt
  72. 73 0
      CatapultWars/Platforms/Android/Resources/Resource.designer.cs
  73. 二進制
      CatapultWars/Platforms/Android/Resources/drawable/Icon.png
  74. 8 0
      CatapultWars/Platforms/Android/Resources/layout/Main.axml
  75. 4 0
      CatapultWars/Platforms/Android/Resources/values/Strings.xml
  76. 5 0
      CatapultWars/Platforms/Android/Resources/values/styles.xml
  77. 36 0
      CatapultWars/Platforms/Desktop/CatapultWars.DesktopGL.csproj
  78. 15 0
      CatapultWars/Platforms/Desktop/Program.cs
  79. 38 0
      CatapultWars/Platforms/Windows/CatapultWars.Windows.csproj
  80. 16 0
      CatapultWars/Platforms/Windows/Program.cs
  81. 33 0
      CatapultWars/Platforms/iOS/CatapultWars.iOS.csproj
  82. 16 0
      CatapultWars/Platforms/iOS/Info.plist
  83. 15 0
      CatapultWars/Platforms/iOS/Program.cs
  84. 71 0
      CatapultWars/README.md

+ 27 - 0
CatapultWars/.vscode/launch.json

@@ -0,0 +1,27 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Launch Windows",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-windows",
+            "program": "${workspaceFolder}/Platforms/Windows/bin/Debug/net8.0-windows/CatapultWars.exe",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        },
+        {
+            "name": "Launch DesktopGL",
+            "type": "coreclr",
+            "request": "launch",
+            "preLaunchTask": "build-desktopgl",
+            "program": "${workspaceFolder}/Platforms/Desktop/bin/Debug/net8.0/CatapultWars",
+            "args": [],
+            "cwd": "${workspaceFolder}",
+            "console": "internalConsole",
+            "stopAtEntry": false
+        }
+    ]
+}

+ 33 - 0
CatapultWars/.vscode/tasks.json

@@ -0,0 +1,33 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "build-windows",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Platforms/Windows/CatapultWars.Windows.csproj"
+            ],
+            "group": "build",
+            "presentation": {
+                "reveal": "silent"
+            },
+            "problemMatcher": "$msCompile"
+        },
+        {
+            "label": "build-desktopgl",
+            "command": "dotnet",
+            "type": "process",
+            "args": [
+                "build",
+                "${workspaceFolder}/Platforms/Desktop/CatapultWars.DesktopGL.csproj"
+            ],
+            "group": "build",
+            "presentation": {
+                "reveal": "silent"
+            },
+            "problemMatcher": "$msCompile"
+        }
+    ]
+}

+ 48 - 0
CatapultWars/CatapaultWars.sln

@@ -0,0 +1,48 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatapultWars.Core", "Core\CatapultWars.Core.csproj", "{C0DEC0DE-0000-0000-0000-000000000001}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatapultWars.Android", "Platforms\Android\CatapultWars.Android.csproj", "{95A9F81E-6982-2B27-66B1-556CFDDAF0DC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatapultWars.iOS", "Platforms\iOS\CatapultWars.iOS.csproj", "{8345979F-82C7-60A9-0BA2-5DB78DE01656}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatapultWars.Windows", "Platforms\Windows\CatapultWars.Windows.csproj", "{A7F2983A-8B5C-4B5E-9D5F-1E2E3B4C5D6E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatapultWars.DesktopGL", "Platforms\Desktop\CatapultWars.DesktopGL.csproj", "{B8F3984B-9C6D-5C6F-AE6F-2F3F4C5D6E7F}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{FABE0725-4BAD-1B2A-FB57-364E3B5B3621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FABE0725-4BAD-1B2A-FB57-364E3B5B3621}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FABE0725-4BAD-1B2A-FB57-364E3B5B3621}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FABE0725-4BAD-1B2A-FB57-364E3B5B3621}.Release|Any CPU.Build.0 = Release|Any CPU
+		{95A9F81E-6982-2B27-66B1-556CFDDAF0DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{95A9F81E-6982-2B27-66B1-556CFDDAF0DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{95A9F81E-6982-2B27-66B1-556CFDDAF0DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{95A9F81E-6982-2B27-66B1-556CFDDAF0DC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8345979F-82C7-60A9-0BA2-5DB78DE01656}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8345979F-82C7-60A9-0BA2-5DB78DE01656}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8345979F-82C7-60A9-0BA2-5DB78DE01656}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8345979F-82C7-60A9-0BA2-5DB78DE01656}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A7F2983A-8B5C-4B5E-9D5F-1E2E3B4C5D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A7F2983A-8B5C-4B5E-9D5F-1E2E3B4C5D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A7F2983A-8B5C-4B5E-9D5F-1E2E3B4C5D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A7F2983A-8B5C-4B5E-9D5F-1E2E3B4C5D6E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B8F3984B-9C6D-5C6F-AE6F-2F3F4C5D6E7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B8F3984B-9C6D-5C6F-AE6F-2F3F4C5D6E7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B8F3984B-9C6D-5C6F-AE6F-2F3F4C5D6E7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B8F3984B-9C6D-5C6F-AE6F-2F3F4C5D6E7F}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {F113FF95-832F-4C26-902E-D5F569C42F6D}
+	EndGlobalSection
+EndGlobal

+ 584 - 0
CatapultWars/Core/Catapult/Catapult.cs

@@ -0,0 +1,584 @@
+//-----------------------------------------------------------------------------
+// Catapult.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Animation.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+//using Microsoft.Devices;
+using System.Xml.Linq;
+
+namespace CatapultGame
+{
+    [Flags]
+    public enum CatapultState
+    {
+        Idle = 0x0,
+        Aiming = 0x1,
+        Firing = 0x2,
+        ProjectileFlying = 0x4,
+        ProjectileHit = 0x8,
+        Hit = 0x10,
+        Reset = 0x20,
+        Stalling = 0x40
+    }
+
+    public class Catapult : DrawableGameComponent
+    {
+        // Hold what the game to which the catapult belongs
+        CatapultGame curGame = null;
+
+        SpriteBatch spriteBatch;
+        Random random;
+
+        public bool AnimationRunning { get; set; }
+        public string Name { get; set; }
+        public bool IsActive { get; set; }
+
+        // In some cases the game need to start second animation while first animation is still running;
+        // this variable define at which frame the second animation should start
+        Dictionary<string, int> splitFrames;
+
+        Texture2D idleTexture;
+        Dictionary<string, Animation> animations;
+
+        SpriteEffects spriteEffects;
+
+        // Projectile
+        Projectile projectile;
+
+        string idleTextureName;
+        bool isAI;
+
+        // Game constants
+        const float gravity = 500f;
+
+        // State of the catapult during its last update
+        CatapultState lastUpdateState = CatapultState.Idle;
+
+        // Used to stall animations
+        int stallUpdateCycles;
+
+        // Current state of the Catapult
+        CatapultState currentState;
+        public CatapultState CurrentState
+        {
+            get { return currentState; }
+            set { currentState = value; }
+        }
+
+        float wind;
+        public float Wind
+        {
+            set
+            {
+                wind = value;
+            }
+        }
+
+        Player enemy;
+        internal Player Enemy
+        {
+            set
+            {
+                enemy = value;
+            }
+        }
+
+        Player self;
+        internal Player Self
+        {
+            set
+            {
+                self = value;
+            }
+        }
+
+        Vector2 catapultPosition;
+        public Vector2 Position
+        {
+            get
+            {
+                return catapultPosition;
+            }
+        }
+
+        /// <summary>
+        /// Describes how powerful the current shot being fired is. The more powerful
+        /// the shot, the further it goes. 0 is the weakest, 1 is the strongest.
+        /// </summary>
+        public float ShotStrength { get; set; }
+
+        public float ShotVelocity { get; set; }
+
+        /// <summary>
+        /// Used to determine whether or not the game is over
+        /// </summary>
+        public bool GameOver { get; set; }
+
+        const int winScore = 5;
+
+        public Catapult(Game game)
+            : base(game)
+        {
+            curGame = (CatapultGame)game;
+        }
+
+        public Catapult(Game game, SpriteBatch screenSpriteBatch,
+          string IdleTexture,
+          Vector2 CatapultPosition, SpriteEffects SpriteEffect, bool IsAI)
+            : this(game)
+        {
+            idleTextureName = IdleTexture;
+            catapultPosition = CatapultPosition;
+            spriteEffects = SpriteEffect;
+            spriteBatch = screenSpriteBatch;
+            isAI = IsAI;
+
+            splitFrames = new Dictionary<string, int>();
+            animations = new Dictionary<string, Animation>();
+        }
+
+        /// <summary>
+        /// Function initializes the catapult instance and loads the animations from XML definition sheet
+        /// </summary>
+        public override void Initialize()
+        {
+            // Define initial state of the catapult
+            IsActive = true;
+            AnimationRunning = false;
+            currentState = CatapultState.Idle;
+            stallUpdateCycles = 0;
+
+            
+
+            // Load multiple animations form XML definition
+            XDocument doc = null;
+            using (var stream = TitleContainer.OpenStream("Content/Textures/Catapults/AnimationsDef.xml"))
+            {
+                doc = XDocument.Load(stream);
+            }
+
+			XName name = XName.Get("Definition");
+            var definitions = doc.Document.Descendants(name);
+
+            // Loop over all definitions in XML
+            foreach (var animationDefinition in definitions)
+            {
+                bool? toLoad = null;
+                bool val;
+                if (bool.TryParse(animationDefinition.Attribute("IsAI").Value, out val))
+                    toLoad = val;
+
+                // Check if the animation definition need to be loaded for current catapult
+                if (toLoad == isAI || null == toLoad)
+                {
+                    // Get a name of the animation
+                    string animatonAlias = animationDefinition.Attribute("Alias").Value;
+                    Texture2D texture =
+                        curGame.Content.Load<Texture2D>(animationDefinition.Attribute("SheetName").Value);
+
+                    // Get the frame size (width & height)
+                    Point frameSize = new Point();
+                    frameSize.X = int.Parse(animationDefinition.Attribute("FrameWidth").Value);
+                    frameSize.Y = int.Parse(animationDefinition.Attribute("FrameHeight").Value);
+
+                    // Get the frames sheet dimensions
+                    Point sheetSize = new Point();
+                    sheetSize.X = int.Parse(animationDefinition.Attribute("SheetColumns").Value);
+                    sheetSize.Y = int.Parse(animationDefinition.Attribute("SheetRows").Value);
+
+                    // If definition has a "SplitFrame" - means that other animation should start here - load it
+                    if (null != animationDefinition.Attribute("SplitFrame"))
+                        splitFrames.Add(animatonAlias,
+                            int.Parse(animationDefinition.Attribute("SplitFrame").Value));
+
+                    // Defing animation speed
+                    TimeSpan frameInterval = TimeSpan.FromSeconds((float)1 /
+                        int.Parse(animationDefinition.Attribute("Speed").Value));
+
+                    Animation animation = new Animation(texture, frameSize, sheetSize);
+
+                    // If definition has an offset defined - means that it should be rendered relatively
+                    // to some element/other animation - load it
+                    if (null != animationDefinition.Attribute("OffsetX") &&
+                      null != animationDefinition.Attribute("OffsetY"))
+                    {
+                        animation.Offset = new Vector2(int.Parse(animationDefinition.Attribute("OffsetX").Value),
+                            int.Parse(animationDefinition.Attribute("OffsetY").Value));
+                    }
+
+                    animations.Add(animatonAlias, animation);
+                }
+            }
+
+            // Load the textures
+            idleTexture = curGame.Content.Load<Texture2D>(idleTextureName);
+
+            // Initialize the projectile
+            Vector2 projectileStartPosition;
+            if (isAI)
+                projectileStartPosition = new Vector2(630, 340);
+            else
+                projectileStartPosition = new Vector2(175, 340);
+
+            projectile = new Projectile(curGame, spriteBatch, "Textures/Ammo/rock_ammo",
+              projectileStartPosition, animations["Fire"].FrameSize.Y, isAI, gravity);
+            projectile.Initialize();
+
+            // Initialize randomizer
+            random = new Random();
+
+            base.Initialize();
+        }
+
+        public override void Update(GameTime gameTime)
+        {
+            bool isGroundHit;
+            bool startStall;
+            CatapultState postUpdateStateChange = 0;
+
+            if (gameTime == null)
+                throw new ArgumentNullException("gameTime");
+
+            // The catapult is inactive, so there is nothing to update
+            if (!IsActive)
+            {
+                base.Update(gameTime);
+                return;
+            }
+                
+            switch (currentState)
+            {
+                case CatapultState.Idle:
+                    // Nothing to do
+                    break;
+                case CatapultState.Aiming:
+                    if (lastUpdateState != CatapultState.Aiming)
+                    {
+                        AudioManager.PlaySound("ropeStretch", true);
+
+                        AnimationRunning = true;
+                        if (isAI == true)
+                        {
+                            animations["Aim"].PlayFromFrameIndex(0);
+                            stallUpdateCycles = 20;
+                            startStall = false;
+                        }
+                    }
+
+                    // Progress Aiming "animation"
+                    if (isAI == false)
+                    {
+                        UpdateAimAccordingToShotStrength();
+                    }
+                    else
+                    {
+                        animations["Aim"].Update();
+                        startStall = AimReachedShotStrength();
+                        currentState = (startStall) ? 
+                            CatapultState.Stalling : CatapultState.Aiming;
+                    }
+                    break;
+                case CatapultState.Stalling:
+                    if (stallUpdateCycles-- <= 0)
+                    {
+                        // We've finished stalling, fire the projectile
+                        Fire(ShotVelocity);
+                        postUpdateStateChange = CatapultState.Firing;
+                    }
+                    break;
+                case CatapultState.Firing:
+                    // Progress Fire animation
+                    if (lastUpdateState != CatapultState.Firing)
+                    {
+                        AudioManager.StopSound("ropeStretch");
+                        AudioManager.PlaySound("catapultFire");
+                        StartFiringFromLastAimPosition();
+                    }
+
+                    animations["Fire"].Update();
+
+                    // If in the "split" point of the animation start 
+                    // projectile fire sequence
+                    if (animations["Fire"].FrameIndex == splitFrames["Fire"])
+                    {
+                        postUpdateStateChange = 
+                            currentState | CatapultState.ProjectileFlying;
+                        projectile.ProjectilePosition = 
+                            projectile.ProjectileStartPosition;
+                    }
+                    break;
+                case CatapultState.Firing | CatapultState.ProjectileFlying:
+                    // Progress Fire animation                    
+                    animations["Fire"].Update();
+
+                    // Update projectile velocity & position in flight
+                    projectile.UpdateProjectileFlightData(gameTime, wind, 
+                        gravity, out isGroundHit);
+
+                    if (isGroundHit)
+                    {
+                        // Start hit sequence
+                        postUpdateStateChange = CatapultState.ProjectileHit;
+                        animations["fireMiss"].PlayFromFrameIndex(0);
+                    }
+                    break;
+                case CatapultState.ProjectileFlying:
+                    // Update projectile velocity & position in flight
+                    projectile.UpdateProjectileFlightData(gameTime, wind, 
+                        gravity, out isGroundHit);
+                    if (isGroundHit)
+                    {
+                        // Start hit sequence
+                        postUpdateStateChange = CatapultState.ProjectileHit;
+                        animations["fireMiss"].PlayFromFrameIndex(0);
+                    }
+
+                    break;
+                case CatapultState.ProjectileHit:
+                    // Check hit on ground impact
+                    if (!CheckHit())
+                    {
+                        if (lastUpdateState != CatapultState.ProjectileHit)
+                        {
+//                            VibrateController.Default.Start(
+//                                TimeSpan.FromMilliseconds(100));
+                            // Play hit sound only on a missed hit,
+                            // a direct hit will trigger the explosion sound
+                            AudioManager.PlaySound("boulderHit");
+                        }
+
+                        // Hit animation finished playing
+                        if (animations["fireMiss"].IsActive == false)
+                        {
+                            postUpdateStateChange = CatapultState.Reset;
+                        }
+
+                        animations["fireMiss"].Update();
+                    }
+                    else
+                    {
+                        // Catapult hit - start longer vibration on any catapult hit 
+                        // Remember that the call to "CheckHit" updates the catapult's
+                        // state to "Hit"
+//                        VibrateController.Default.Start(
+//                            TimeSpan.FromMilliseconds(500));
+                    }
+
+                    break;
+                case CatapultState.Hit:
+                    // Progress hit animation
+                    if ((animations["Destroyed"].IsActive == false) &&
+                        (animations["hitSmoke"].IsActive == false))
+                    {
+                        if (enemy.Score >= winScore)
+                        {
+                            GameOver = true;
+                            break;
+                        }
+
+                        postUpdateStateChange = CatapultState.Reset;
+                    }
+
+                    animations["Destroyed"].Update();
+                    animations["hitSmoke"].Update();
+
+                    break;
+                case CatapultState.Reset:
+                    AnimationRunning = false;
+                    break;
+                default:
+                    break;
+            }
+
+            lastUpdateState = currentState;
+            if (postUpdateStateChange != 0)
+            {
+                currentState = postUpdateStateChange;
+            }
+
+            base.Update(gameTime);
+        }
+
+        /// <summary>
+        /// Used to check if the current aim animation frame represents the shot
+        /// strength set for the catapult.
+        /// </summary>
+        /// <returns>True if the current frame represents the shot strength,
+        /// false otherwise.</returns>
+        private bool AimReachedShotStrength()
+        {
+            return (animations["Aim"].FrameIndex ==
+                (Convert.ToInt32(animations["Aim"].FrameCount * ShotStrength) - 1));
+        }
+
+        private void UpdateAimAccordingToShotStrength()
+        {
+            var aimAnimation = animations["Aim"];
+            int frameToDisplay =
+                Convert.ToInt32(aimAnimation.FrameCount * ShotStrength);
+            aimAnimation.FrameIndex = frameToDisplay;
+        }
+
+        /// <summary>
+        /// Calculates the frame from which to start the firing animation, 
+        /// and activates it.
+        /// </summary>
+        private void StartFiringFromLastAimPosition()
+        {
+            int startFrame = animations["Aim"].FrameCount -
+                animations["Aim"].FrameIndex;
+            animations["Fire"].PlayFromFrameIndex(startFrame);
+        }
+
+        public override void Draw(GameTime gameTime)
+        {
+            if (gameTime == null)
+                throw new ArgumentNullException("gameTime");
+
+            // Using the last update state makes sure we do not draw
+            // before updating animations properly
+            switch (lastUpdateState)
+            {
+                case CatapultState.Idle:
+                    DrawIdleCatapult();
+                    break;
+                case CatapultState.Aiming:
+                case CatapultState.Stalling:
+                    animations["Aim"].Draw(spriteBatch, catapultPosition,
+                        spriteEffects);
+                    break;
+                case CatapultState.Firing:
+                    animations["Fire"].Draw(spriteBatch, catapultPosition,
+                        spriteEffects);
+                    break;
+                case CatapultState.Firing | CatapultState.ProjectileFlying:
+                case CatapultState.ProjectileFlying:
+                    animations["Fire"].Draw(spriteBatch, catapultPosition,
+                        spriteEffects);
+
+                    projectile.Draw(gameTime);
+                    break;
+                case CatapultState.ProjectileHit:
+                    // Draw the catapult
+                    DrawIdleCatapult();
+
+                    // Projectile Hit animation
+                    animations["fireMiss"].Draw(spriteBatch,
+                        projectile.ProjectileHitPosition, spriteEffects);
+                    break;
+                case CatapultState.Hit:
+                    // Catapult hit animation
+                    animations["Destroyed"].Draw(spriteBatch, catapultPosition,
+                        spriteEffects);
+
+                    // Projectile smoke animation
+                    animations["hitSmoke"].Draw(spriteBatch, catapultPosition,
+                        spriteEffects);
+                    break;
+                case CatapultState.Reset:
+                    DrawIdleCatapult();
+                    break;
+                default:
+                    break;
+            }
+
+            base.Draw(gameTime);
+        }
+
+        /// <summary>
+        /// Start Hit sequence on catapult - could be executed on self or from enemy in case of hit
+        /// </summary>
+        public void Hit()
+        {
+            AnimationRunning = true;
+            animations["Destroyed"].PlayFromFrameIndex(0);
+            animations["hitSmoke"].PlayFromFrameIndex(0);
+            currentState = CatapultState.Hit;
+        }
+
+        public void Fire(float velocity)
+        {
+            projectile.Fire(velocity, velocity);
+        }
+
+        /// <summary>
+        /// Check if projectile hit some catapult. The possibilities are:
+        /// Nothing hit, Hit enemy, Hit self
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckHit()
+        {
+            bool bRes = false;
+            // Build a sphere around a projectile
+            Vector3 center = new Vector3(projectile.ProjectilePosition, 0);
+            BoundingSphere sphere = new BoundingSphere(center,
+                Math.Max(projectile.ProjectileTexture.Width / 2,
+                projectile.ProjectileTexture.Height / 2));
+
+            // Check Self-Hit - create a bounding box around self
+            Vector3 min = new Vector3(catapultPosition, 0);
+            Vector3 max = new Vector3(catapultPosition +
+                new Vector2(animations["Fire"].FrameSize.X,
+                    animations["Fire"].FrameSize.Y), 0);
+            BoundingBox selfBox = new BoundingBox(min, max);
+
+            // Check enemy - create a bounding box around the enemy
+            min = new Vector3(enemy.Catapult.Position, 0);
+            max = new Vector3(enemy.Catapult.Position +
+                new Vector2(animations["Fire"].FrameSize.X,
+                    animations["Fire"].FrameSize.Y), 0);
+            BoundingBox enemyBox = new BoundingBox(min, max);
+
+            // Check self hit
+            if (sphere.Intersects(selfBox) && currentState != CatapultState.Hit)
+            {
+                AudioManager.PlaySound("catapultExplosion");
+                // Launch hit animation sequence on self
+                Hit();
+                enemy.Score++;
+                bRes = true;
+            }
+            // Check if enemy was hit
+            else if (sphere.Intersects(enemyBox)
+                && enemy.Catapult.CurrentState != CatapultState.Hit
+                && enemy.Catapult.CurrentState != CatapultState.Reset)
+            {
+                AudioManager.PlaySound("catapultExplosion");
+                // Launch enemy hit animaton
+                enemy.Catapult.Hit();
+                self.Score++;
+                bRes = true;
+                currentState = CatapultState.Reset;
+            }
+
+            return bRes;
+        }
+
+        /// <summary>
+        /// Draw catapult in Idle state
+        /// </summary>
+        private void DrawIdleCatapult()
+        {
+            spriteBatch.Draw(idleTexture, catapultPosition, null, Color.White,
+              0.0f, Vector2.Zero, 1.0f,
+              spriteEffects, 0);
+        }
+
+    }
+}

+ 83 - 0
CatapultWars/Core/Catapult/CatapultGame.cs

@@ -0,0 +1,83 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// CatapultGame.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Media;
+using CatapultGame;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// This is the main type for your game
+    /// </summary>
+    public class CatapultGame : Game
+    {
+        #region Fields
+        GraphicsDeviceManager graphics;
+        ScreenManager screenManager;
+        #endregion
+
+        #region Initialization Methods
+        public CatapultGame()
+        {
+            graphics = new GraphicsDeviceManager(this);
+			//graphics.SynchronizeWithVerticalRetrace = false;
+            Content.RootDirectory = "Content";
+
+            // Frame rate is 30 fps by default for Windows Phone.
+            TargetElapsedTime = TimeSpan.FromTicks(333333);
+
+            //Create a new instance of the Screen Manager
+            screenManager = new ScreenManager(this);
+            Components.Add(screenManager);
+			IsMouseVisible = true;
+#if ___MOBILE___
+            //Switch to full screen for best game experience
+            graphics.IsFullScreen = true;
+#endif
+
+            //Add two new screens
+            screenManager.AddScreen(new BackgroundScreen(), null);
+            screenManager.AddScreen(new MainMenuScreen(), null);
+
+            AudioManager.Initialize(this);
+        }
+
+        /// <summary>
+        /// Allows the game to perform any initialization it needs to before starting to run.
+        /// This is where it can query for any required services and load any non-graphic
+        /// related content.  Calling base.Initialize will enumerate through any components
+        /// and initialize them as well.
+        /// </summary>
+        protected override void Initialize()
+        {
+            base.Initialize();
+        }
+        #endregion
+
+        #region Loading
+        protected override void LoadContent()
+        {
+            AudioManager.LoadSounds();
+            base.LoadContent();
+        }
+        #endregion
+    }
+}

+ 13 - 0
CatapultWars/Core/CatapultWars.Core.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <RootNamespace>CatapultGame.Core</RootNamespace>
+    <AssemblyName>CatapultGame.Core</AssemblyName>
+    <GenerateAssemblyInfo>true</GenerateAssemblyInfo>
+  </PropertyGroup>
+  
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+  </ItemGroup>
+  
+</Project>

二進制
CatapultWars/Core/Content/Background.png


二進制
CatapultWars/Core/Content/Fonts/HUDFont.xnb


二進制
CatapultWars/Core/Content/Fonts/MenuFont.xnb


二進制
CatapultWars/Core/Content/Game.ico


二進制
CatapultWars/Core/Content/Sounds/BoulderHit.wav


二進制
CatapultWars/Core/Content/Sounds/BoulderHit.xnb


二進制
CatapultWars/Core/Content/Sounds/CatapultExplosion.wav


二進制
CatapultWars/Core/Content/Sounds/CatapultExplosion.xnb


二進制
CatapultWars/Core/Content/Sounds/CatapultFire.wav


二進制
CatapultWars/Core/Content/Sounds/CatapultFire.xnb


二進制
CatapultWars/Core/Content/Sounds/Lose.wav


二進制
CatapultWars/Core/Content/Sounds/Lose.xnb


二進制
CatapultWars/Core/Content/Sounds/RopeStretch.wav


二進制
CatapultWars/Core/Content/Sounds/RopeStretch.xnb


二進制
CatapultWars/Core/Content/Sounds/Win.wav


二進制
CatapultWars/Core/Content/Sounds/Win.xnb


二進制
CatapultWars/Core/Content/Textures/Ammo/rock_ammo.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/blank.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/cloud1.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/cloud2.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/defeat.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/gameplay_screen.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/instructions.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/mountain.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/sky.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/title_screen.xnb


二進制
CatapultWars/Core/Content/Textures/Backgrounds/victory.xnb


+ 63 - 0
CatapultWars/Core/Content/Textures/Catapults/AnimationsDef.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<Animations>
+  <!--Player animation definitions-->
+  <Definition FrameWidth="75" FrameHeight="60"
+              SheetRows="2" SheetColumns="15"
+              Speed="30" SplitFrame="20"
+              SheetName="Textures/Catapults/Blue/blueFire/blueCatapult_fire"
+              Alias="Fire" IsAI="false"/>
+  <Definition FrameWidth="75" FrameHeight="60"
+              SheetRows="1" SheetColumns="18"
+              Speed="30"
+              SheetName="Textures/Catapults/Blue/bluePullback/blueCatapult_Pullback"
+              Alias="Aim" IsAI="false"/>
+
+  <Definition FrameWidth="122" FrameHeight="62"
+              SheetRows="2" SheetColumns="15"
+              Speed="30" OffsetX="-40" OffsetY="0"
+              SheetName="Textures/Catapults/Blue/blueDestroyed/blueCatapult_destroyed"
+              Alias="Destroyed" IsAI="false"/>
+
+    <Definition FrameWidth="90" FrameHeight="80"
+           SheetRows="2" SheetColumns="15"
+           Speed="30" OffsetX="-50" OffsetY="0"
+           SheetName="Textures/Catapults/Fire_Miss/fire_miss"
+           Alias="fireMiss" IsAI="false"/>
+
+    <Definition FrameWidth="128" FrameHeight="128"
+           SheetRows="2" SheetColumns="15"
+           Speed="30" OffsetX="0" OffsetY="-64"
+           SheetName="Textures/Catapults/Hit_Smoke/smoke"
+           Alias="hitSmoke" IsAI="false"/>
+
+    <!--AI animation definitions-->
+  <Definition FrameWidth="75" FrameHeight="60"
+            SheetRows="2" SheetColumns="15"
+            Speed="30" SplitFrame="20"
+            SheetName="Textures/Catapults/Red/redFire/redCatapult_fire"
+            Alias="Fire" IsAI="true"/>
+  <Definition FrameWidth="122" FrameHeight="62"
+              SheetRows="2" SheetColumns="15"
+              Speed="30" OffsetX="-11" OffsetY="0"
+              SheetName="Textures/Catapults/Red/redDestroyed/redCatapult_destroyed"
+              Alias="Destroyed" IsAI="true"/>
+
+  <Definition FrameWidth="75" FrameHeight="60"
+            SheetRows="1" SheetColumns="18"
+            Speed="30"
+            SheetName="Textures/Catapults/Red/redPullback/redCatapult_Pullback"
+            Alias="Aim" IsAI="true"/>
+
+    <Definition FrameWidth="90" FrameHeight="80"
+           SheetRows="2" SheetColumns="15"
+           Speed="30" OffsetX="-50" OffsetY="0"
+           SheetName="Textures/Catapults/Fire_Miss/fire_miss"
+           Alias="fireMiss" IsAI="true"/>
+
+    <Definition FrameWidth="128" FrameHeight="128"
+           SheetRows="2" SheetColumns="15"
+           Speed="30" OffsetX="-30" OffsetY="-64"
+           SheetName="Textures/Catapults/Hit_Smoke/smoke"
+           Alias="hitSmoke" IsAI="true"/> 
+</Animations>
+

二進制
CatapultWars/Core/Content/Textures/Catapults/Blue/blueDestroyed/blueCatapult_destroyed.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Blue/blueFire/blueCatapult_fire.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Blue/blueIdle/blueIdle.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Blue/bluePullback/blueCatapult_Pullback.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Fire_Miss/fire_miss.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Hit_Smoke/smoke.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Red/redDestroyed/redCatapult_destroyed.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Red/redFire/redCatapult_fire.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Red/redIdle/redIdle.xnb


二進制
CatapultWars/Core/Content/Textures/Catapults/Red/redPullback/redCatapult_Pullback.xnb


二進制
CatapultWars/Core/Content/Textures/HUD/Arrow.xnb


二進制
CatapultWars/Core/Content/Textures/HUD/ammoType.xnb


二進制
CatapultWars/Core/Content/Textures/HUD/hudBackground.xnb


二進制
CatapultWars/Core/Content/Textures/HUD/windArrow.xnb


+ 43 - 0
CatapultWars/Core/Content/app.manifest

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="CatapultWars"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- A list of the Windows versions that this application has been tested on and is
+           is designed to work with. Uncomment the appropriate elements and Windows will 
+           automatically selected the most compatible environment. -->
+
+      <!-- Windows Vista -->
+      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+
+      <!-- Windows 7 -->
+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+
+      <!-- Windows 8 -->
+      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+
+      <!-- Windows 8.1 -->
+      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+
+    </application>
+  </compatibility>
+
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness>
+    </windowsSettings>
+  </application>
+
+</assembly>

+ 67 - 0
CatapultWars/Core/Players/AI.cs

@@ -0,0 +1,67 @@
+//-----------------------------------------------------------------------------
+// AI.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// AI.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace CatapultGame
+{
+    class AI : Player
+    {
+        Random random;
+
+        public AI(Game game)
+            : base(game)
+        {
+        }
+
+        public AI(Game game, SpriteBatch screenSpriteBatch)
+            : base(game, screenSpriteBatch)
+        {
+            Catapult = new Catapult(game, screenSpriteBatch,
+                            "Textures/Catapults/Red/redIdle/redIdle",
+                            new Vector2(600, 332), SpriteEffects.FlipHorizontally, true);
+        }
+
+        public override void Initialize()
+        {
+            //Initialize randomizer
+            random = new Random();
+
+            Catapult.Initialize();
+
+            base.Initialize();
+        }
+
+        public override void Update(GameTime gameTime)
+        {
+            // Check if it is time to take a shot
+            if (Catapult.CurrentState == CatapultState.Aiming &&
+                !Catapult.AnimationRunning)
+            {
+                // Fire at a random strength
+                float shotVelocity =
+                    random.Next((int)MinShotStrength, (int)MaxShotStrength);
+
+                Catapult.ShotStrength = (shotVelocity / MaxShotStrength);
+                Catapult.ShotVelocity = shotVelocity;
+            }
+            base.Update(gameTime);
+        }
+    }
+}

+ 181 - 0
CatapultWars/Core/Players/Human.cs

@@ -0,0 +1,181 @@
+//-----------------------------------------------------------------------------
+// Human.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Human.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Input;
+using GameStateManagement;
+
+
+
+namespace CatapultGame
+{
+	class Human : Player
+	{
+		// Drag variables to hold first and last gesture samples
+		GestureSample? prevSample;
+		GestureSample? firstSample;
+
+		public bool isDragging { get; set; }
+		// Constant for longest distance possible between drag points
+		readonly float maxDragDelta = (new Vector2 (480, 800)).Length ();
+		// Textures & position & spriteEffects used for Catapult
+		Texture2D arrow;
+		float arrowScale;
+		Vector2 catapultPosition = new Vector2 (140, 332);
+
+		public Human (Game game)
+            : base(game)
+		{
+		}
+
+		public Human (Game game, SpriteBatch screenSpriteBatch)
+            : base(game, screenSpriteBatch)
+		{
+			Catapult = new Catapult (game, screenSpriteBatch,
+                                    "Textures/Catapults/Blue/blueIdle/blueIdle",
+                                    catapultPosition, SpriteEffects.None, false);
+		}
+
+		public override void Initialize ()
+		{
+			arrow = curGame.Content.Load<Texture2D> ("Textures/HUD/Arrow");
+
+			Catapult.Initialize ();
+
+			base.Initialize ();
+		}
+
+		/// <summary>
+		/// Function processes the user input
+		/// </summary>
+		/// <param name="gestureSample"></param>
+		public void HandleInput (GestureSample gestureSample)
+		{
+			// Process input only if in Human's turn
+			if (IsActive) {
+				// Process any Drag gesture
+				if (gestureSample.GestureType == GestureType.FreeDrag) {
+					// If drag just began save the sample for future
+					// calculations and start Aim "animation"
+					if (null == firstSample) {
+						firstSample = gestureSample;
+						Catapult.CurrentState = CatapultState.Aiming;
+					}
+
+					// save the current gesture sample
+					prevSample = gestureSample;
+
+					// calculate the delta between first sample and current
+					// sample to present visual sound on screen
+					Vector2 delta = prevSample.Value.Position - firstSample.Value.Position;
+					Catapult.ShotStrength = delta.Length () / maxDragDelta;
+					float baseScale = 0.001f;
+					arrowScale = baseScale * delta.Length ();
+					isDragging = true;
+				} else if (gestureSample.GestureType == GestureType.DragComplete) {
+					// calc velocity based on delta between first and last
+					// gesture samples
+					if (null != firstSample) {
+						Vector2 delta = prevSample.Value.Position - firstSample.Value.Position;
+						Catapult.ShotVelocity = MinShotStrength + Catapult.ShotStrength *
+									(MaxShotStrength - MinShotStrength);
+						Catapult.Fire (Catapult.ShotVelocity);
+						Catapult.CurrentState = CatapultState.Firing;
+					}
+
+					// turn off dragging state
+					ResetDragState ();
+				}
+			}
+		}
+
+		Vector2? firstMouseSample = null;
+		Vector2? prevMouseSample = null;
+
+		public void HandleInput (InputState input)
+		{
+			// Process input only if in Human's turn
+			if (IsActive) {
+
+				if (input.MouseGesture.HasFlag(MouseGestureType.FreeDrag)) {
+					// If drag just began save the sample for future
+					// calculations and start Aim "animation"
+					if (null == firstMouseSample) {
+						firstMouseSample = input.MouseDragStartPosition;
+						Catapult.CurrentState = CatapultState.Aiming;
+					}
+
+					// save the current gesture sample
+					prevMouseSample = input.CurrentMousePosition;
+
+					// calculate the delta between first sample and current
+					// sample to present visual sound on screen
+					Vector2 delta = (Vector2)prevMouseSample - (Vector2)firstMouseSample;
+					Catapult.ShotStrength = delta.Length () / maxDragDelta;
+					float baseScale = 0.001f;
+					arrowScale = baseScale * delta.Length ();
+					isDragging = true;
+				} else if (input.MouseGesture.HasFlag(MouseGestureType.DragComplete)) {
+					// calc velocity based on delta between first and last
+					// gesture samples
+					if (null != firstMouseSample) {
+						Vector2 delta = (Vector2)prevMouseSample - (Vector2)firstMouseSample;
+						Catapult.ShotVelocity = MinShotStrength + Catapult.ShotStrength *
+						    (MaxShotStrength - MinShotStrength);
+						Catapult.Fire (Catapult.ShotVelocity);
+						Catapult.CurrentState = CatapultState.Firing;
+					}
+
+					ResetDragState ();
+				}
+			}
+		}
+
+
+		public override void Draw (GameTime gameTime)
+		{
+			if (isDragging)
+				DrawDragArrow (arrowScale);
+
+			base.Draw (gameTime);
+		}
+
+		public void DrawDragArrow (float arrowScale)
+		{
+			spriteBatch.Draw (arrow, catapultPosition + new Vector2 (0, -40),
+              null, Color.Blue, 0,
+              Vector2.Zero, new Vector2 (arrowScale, 0.1f), SpriteEffects.None, 0);
+		}
+
+		/// <summary>
+		/// Turn off dragging state and reset drag related variables
+		/// </summary>
+		public void ResetDragState ()
+		{
+			firstSample = null;
+			prevSample = null;
+			firstMouseSample = null;
+			prevMouseSample = null;
+			isDragging = false;
+			arrowScale = 0;
+			Catapult.ShotStrength = 0;
+		}
+	}
+}

+ 82 - 0
CatapultWars/Core/Players/Player.cs

@@ -0,0 +1,82 @@
+//-----------------------------------------------------------------------------
+// Player.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Player.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace CatapultGame
+{
+    internal class Player : DrawableGameComponent
+    {
+        protected CatapultGame curGame;
+        protected SpriteBatch spriteBatch;
+
+        // Constants used for calculating shot strength
+        public const float MinShotStrength = 150;
+        public const float MaxShotStrength = 400;
+
+        // Public variables used by Gameplay class
+        public Catapult Catapult { get; set; }
+        public int Score { get; set; }
+        public string Name { get; set; }
+
+        public Player Enemy
+        {
+            set
+            {
+                Catapult.Enemy = value;
+                Catapult.Self = this;
+            }
+        }
+
+        public bool IsActive { get; set; }
+
+        public Player(Game game)
+            : base(game)
+        {
+            curGame = (CatapultGame)game;
+        }
+
+        public Player(Game game, SpriteBatch screenSpriteBatch)
+            : this(game)
+        {
+            spriteBatch = screenSpriteBatch;
+        }
+
+        public override void Initialize()
+        {
+            Score = 0;
+
+            base.Initialize();
+        }
+
+        public override void Update(GameTime gameTime)
+        {
+            // Update catapult related to the player
+            Catapult.Update(gameTime);
+            base.Update(gameTime);
+        }
+
+        public override void Draw(GameTime gameTime)
+        {
+            // Draw related catapults
+            Catapult.Draw(gameTime);
+            base.Draw(gameTime);
+        }
+    }
+}

+ 38 - 0
CatapultWars/Core/Players/PlayerIndexEventArgs.cs

@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------------
+// PlayerIndexEventArgs.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Custom event argument which includes the index of the player who
+    /// triggered the event. This is used by the MenuEntry.Selected event.
+    /// </summary>
+    class PlayerIndexEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public PlayerIndexEventArgs(PlayerIndex playerIndex)
+        {
+            this.playerIndex = playerIndex;
+        }
+
+
+        /// <summary>
+        /// Gets the index of the player who triggered this event.
+        /// </summary>
+        public PlayerIndex PlayerIndex
+        {
+            get { return playerIndex; }
+        }
+
+        PlayerIndex playerIndex;
+    }
+}

+ 436 - 0
CatapultWars/Core/ScreenManager/ScreenManager.cs

@@ -0,0 +1,436 @@
+//-----------------------------------------------------------------------------
+// ScreenManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+using System.IO;
+using System.IO.IsolatedStorage;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// The screen manager is a component which manages one or more GameScreen
+    /// instances. It maintains a stack of screens, calls their Update and Draw
+    /// methods at the appropriate times, and automatically routes input to the
+    /// topmost active screen.
+    /// </summary>
+    public class ScreenManager : DrawableGameComponent
+    {
+
+        List<GameScreen> screens = new List<GameScreen>();
+        List<GameScreen> screensToUpdate = new List<GameScreen>();
+
+        InputState input = new InputState();
+
+        SpriteBatch spriteBatch;
+        SpriteFont font;
+        Texture2D blankTexture;
+
+        bool isInitialized;
+
+        bool traceEnabled;
+
+
+
+
+        /// <summary>
+        /// A default SpriteBatch shared by all the screens. This saves
+        /// each screen having to bother creating their own local instance.
+        /// </summary>
+        public SpriteBatch SpriteBatch
+        {
+            get { return spriteBatch; }
+        }
+
+
+        /// <summary>
+        /// A default font shared by all the screens. This saves
+        /// each screen having to bother loading their own local copy.
+        /// </summary>
+        public SpriteFont Font
+        {
+            get { return font; }
+        }
+
+
+        /// <summary>
+        /// If true, the manager prints out a list of all the screens
+        /// each time it is updated. This can be useful for making sure
+        /// everything is being added and removed at the right times.
+        /// </summary>
+        public bool TraceEnabled
+        {
+            get { return traceEnabled; }
+            set { traceEnabled = value; }
+        }
+
+
+
+
+
+        /// <summary>
+        /// Constructs a new screen manager component.
+        /// </summary>
+        public ScreenManager(Game game)
+            : base(game)
+        {
+            // we must set EnabledGestures before we can query for them, but
+            // we don't assume the game wants to read them.
+            TouchPanel.EnabledGestures = GestureType.None;
+        }
+
+
+        /// <summary>
+        /// Initializes the screen manager component.
+        /// </summary>
+        public override void Initialize()
+        {
+            base.Initialize();
+
+            isInitialized = true;
+        }
+
+
+        /// <summary>
+        /// Load your graphics content.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            // Load content belonging to the screen manager.
+            ContentManager content = Game.Content;
+
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+            font = content.Load<SpriteFont>("Fonts/MenuFont");
+            blankTexture = content.Load<Texture2D>("Textures/Backgrounds/blank");
+
+            // Tell each of the screens to load their content.
+            foreach (GameScreen screen in screens)
+            {
+                screen.LoadContent();
+            }
+        }
+
+
+        /// <summary>
+        /// Unload your graphics content.
+        /// </summary>
+        protected override void UnloadContent()
+        {
+            // Tell each of the screens to unload their content.
+            foreach (GameScreen screen in screens)
+            {
+                screen.UnloadContent();
+            }
+        }
+
+
+
+
+
+        /// <summary>
+        /// Allows each screen to run logic.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            // Read the keyboard and gamepad.
+            input.Update();
+
+            // Make a copy of the master screen list, to avoid confusion if
+            // the process of updating one screen adds or removes others.
+            screensToUpdate.Clear();
+
+            foreach (GameScreen screen in screens)
+                screensToUpdate.Add(screen);
+
+            bool otherScreenHasFocus = !Game.IsActive;
+            bool coveredByOtherScreen = false;
+
+            // Loop as long as there are screens waiting to be updated.
+            while (screensToUpdate.Count > 0)
+            {
+                // Pop the topmost screen off the waiting list.
+                GameScreen screen = screensToUpdate[screensToUpdate.Count - 1];
+
+                screensToUpdate.RemoveAt(screensToUpdate.Count - 1);
+
+                // Update the screen.
+                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+                if (screen.ScreenState == ScreenState.TransitionOn ||
+                    screen.ScreenState == ScreenState.Active)
+                {
+                    // If this is the first active screen we came across,
+                    // give it a chance to handle input.
+                    if (!otherScreenHasFocus)
+                    {
+                        screen.HandleInput(input);
+
+                        otherScreenHasFocus = true;
+                    }
+
+                    // If this is an active non-popup, inform any subsequent
+                    // screens that they are covered by it.
+                    if (!screen.IsPopup)
+                        coveredByOtherScreen = true;
+                }
+            }
+
+            // Print debug trace?
+            if (traceEnabled)
+                TraceScreens();
+        }
+
+
+        /// <summary>
+        /// Prints a list of all the screens, for debugging.
+        /// </summary>
+        void TraceScreens()
+        {
+            List<string> screenNames = new List<string>();
+
+            foreach (GameScreen screen in screens)
+                screenNames.Add(screen.GetType().Name);
+
+            Debug.WriteLine(string.Join(", ", screenNames.ToArray()));
+        }
+
+
+        /// <summary>
+        /// Tells each screen to draw itself.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            foreach (GameScreen screen in screens)
+            {
+                if (screen.ScreenState == ScreenState.Hidden)
+                    continue;
+
+                screen.Draw(gameTime);
+            }
+        }
+
+
+
+
+
+        /// <summary>
+        /// Adds a new screen to the screen manager.
+        /// </summary>
+        public void AddScreen(GameScreen screen, PlayerIndex? controllingPlayer)
+        {
+            screen.ControllingPlayer = controllingPlayer;
+            screen.ScreenManager = this;
+            screen.IsExiting = false;
+
+            // If we have a graphics device, tell the screen to load content.
+            if (isInitialized)
+            {
+                screen.LoadContent();
+            }
+
+            screens.Add(screen);
+
+            // update the TouchPanel to respond to gestures this screen is interested in
+            TouchPanel.EnabledGestures = screen.EnabledGestures;
+        }
+
+
+        /// <summary>
+        /// Removes a screen from the screen manager. You should normally
+        /// use GameScreen.ExitScreen instead of calling this directly, so
+        /// the screen can gradually transition off rather than just being
+        /// instantly removed.
+        /// </summary>
+        public void RemoveScreen(GameScreen screen)
+        {
+            // If we have a graphics device, tell the screen to unload content.
+            if (isInitialized)
+            {
+                screen.UnloadContent();
+            }
+
+            screens.Remove(screen);
+            screensToUpdate.Remove(screen);
+
+            // if there is a screen still in the manager, update TouchPanel
+            // to respond to gestures that screen is interested in.
+            if (screens.Count > 0)
+            {
+                TouchPanel.EnabledGestures = screens[screens.Count - 1].EnabledGestures;
+            }
+        }
+
+
+        /// <summary>
+        /// Expose an array holding all the screens. We return a copy rather
+        /// than the real master list, because screens should only ever be added
+        /// or removed using the AddScreen and RemoveScreen methods.
+        /// </summary>
+        public GameScreen[] GetScreens()
+        {
+            return screens.ToArray();
+        }
+
+
+        /// <summary>
+        /// Helper draws a translucent black fullscreen sprite, used for fading
+        /// screens in and out, and for darkening the background behind popups.
+        /// </summary>
+        public void FadeBackBufferToBlack(float alpha)
+        {
+            Viewport viewport = GraphicsDevice.Viewport;
+
+            spriteBatch.Begin();
+
+            spriteBatch.Draw(blankTexture,
+                             new Rectangle(0, 0, viewport.Width, viewport.Height),
+                             Color.Black * alpha);
+
+            spriteBatch.End();
+        }
+
+        /// <summary>
+        /// Informs the screen manager to serialize its state to disk.
+        /// </summary>
+        public void SerializeState()
+        {
+            // open up isolated storage
+            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+            {
+                // if our screen manager directory already exists, delete the contents
+                if (storage.DirectoryExists("ScreenManager"))
+                {
+                    DeleteState(storage);
+                }
+
+                // otherwise just create the directory
+                else
+                {
+                    storage.CreateDirectory("ScreenManager");
+                }
+
+                // create a file we'll use to store the list of screens in the stack
+                using (IsolatedStorageFileStream stream = storage.CreateFile("ScreenManager\\ScreenList.dat"))
+                {
+                    using (BinaryWriter writer = new BinaryWriter(stream))
+                    {
+                        // write out the full name of all the types in our stack so we can
+                        // recreate them if needed.
+                        foreach (GameScreen screen in screens)
+                        {
+                            if (screen.IsSerializable)
+                            {
+                                writer.Write(screen.GetType().AssemblyQualifiedName);
+                            }
+                        }
+                    }
+                }
+
+                // now we create a new file stream for each screen so it can save its state
+                // if it needs to. we name each file "ScreenX.dat" where X is the index of
+                // the screen in the stack, to ensure the files are uniquely named
+                int screenIndex = 0;
+                foreach (GameScreen screen in screens)
+                {
+                    if (screen.IsSerializable)
+                    {
+                        string fileName = string.Format("ScreenManager\\Screen{0}.dat", screenIndex);
+
+                        // open up the stream and let the screen serialize whatever state it wants
+                        using (IsolatedStorageFileStream stream = storage.CreateFile(fileName))
+                        {
+                            screen.Serialize(stream);
+                        }
+
+                        screenIndex++;
+                    }
+                }
+            }
+        }
+
+        public bool DeserializeState()
+        {
+            // open up isolated storage
+            using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
+            {
+                // see if our saved state directory exists
+                if (storage.DirectoryExists("ScreenManager"))
+                {
+                    try
+                    {
+                        // see if we have a screen list
+                        if (storage.FileExists("ScreenManager\\ScreenList.dat"))
+                        {
+                            // load the list of screen types
+                            using (IsolatedStorageFileStream stream = 
+                                storage.OpenFile("ScreenManager\\ScreenList.dat", FileMode.Open, FileAccess.Read))
+                            {
+                                using (BinaryReader reader = new BinaryReader(stream))
+                                {
+                                    while (reader.BaseStream.Position < reader.BaseStream.Length)
+                                    {
+                                        // read a line from our file
+                                        string line = reader.ReadString();
+
+                                        // if it isn't blank, we can create a screen from it
+                                        if (!string.IsNullOrEmpty(line))
+                                        {
+                                            Type screenType = Type.GetType(line);
+                                            GameScreen screen = Activator.CreateInstance(screenType) as GameScreen;
+                                            AddScreen(screen, PlayerIndex.One);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        // next we give each screen a chance to deserialize from the disk
+                        for (int i = 0; i < screens.Count; i++)
+                        {
+                            string filename = string.Format("ScreenManager\\Screen{0}.dat", i);
+                            using (IsolatedStorageFileStream stream = 
+                                storage.OpenFile(filename, FileMode.Open, FileAccess.Read))
+                            {
+                                screens[i].Deserialize(stream);
+                            }
+                        }
+
+                        return true;
+                    }
+                    catch (Exception)
+                    {
+                        // if an exception was thrown while reading, odds are we cannot recover
+                        // from the saved state, so we will delete it so the game can correctly
+                        // launch.
+                        DeleteState(storage);
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Deletes the saved state files from isolated storage.
+        /// </summary>
+        private void DeleteState(IsolatedStorageFile storage)
+        {
+            // get all of the files in the directory and delete them
+            string[] files = storage.GetFileNames("ScreenManager\\*");
+            foreach (string file in files)
+            {
+                storage.DeleteFile(Path.Combine("ScreenManager", file));
+            }
+        }
+
+    }
+}

+ 46 - 0
CatapultWars/Core/Screens/BackgroundScreen.cs

@@ -0,0 +1,46 @@
+//-----------------------------------------------------------------------------
+// BackgroundScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework;
+using GameStateManagement;
+
+namespace CatapultGame
+{
+    class BackgroundScreen : GameScreen
+    {
+        Texture2D background;
+
+        public BackgroundScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(0.0);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+        }
+
+        public override void LoadContent()
+        {
+            background = Load<Texture2D>("Textures/Backgrounds/title_screen");           
+        }
+
+        public override void Draw(GameTime gameTime)
+        {
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+
+            spriteBatch.Begin();
+
+            // Draw Background
+            spriteBatch.Draw(background, new Vector2(0, 0),
+                 new Color(255, 255, 255, TransitionAlpha));
+
+            spriteBatch.End();
+        }
+    }
+}

+ 380 - 0
CatapultWars/Core/Screens/GameScreen.cs

@@ -0,0 +1,380 @@
+//-----------------------------------------------------------------------------
+// GameScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input.Touch;
+using System.IO;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Enum describes the screen transition state.
+    /// </summary>
+    public enum ScreenState
+    {
+        TransitionOn,
+        Active,
+        TransitionOff,
+        Hidden,
+    }
+
+
+    /// <summary>
+    /// A screen is a single layer that has update and draw logic, and which
+    /// can be combined with other layers to build up a complex menu system.
+    /// For instance the main menu, the options menu, the "are you sure you
+    /// want to quit" message box, and the main game itself are all implemented
+    /// as screens.
+    /// </summary>
+    public abstract class GameScreen
+    {
+
+
+        /// <summary>
+        /// Normally when one screen is brought up over the top of another,
+        /// the first screen will transition off to make room for the new
+        /// one. This property indicates whether the screen is only a small
+        /// popup, in which case screens underneath it do not need to bother
+        /// transitioning off.
+        /// </summary>
+        public bool IsPopup
+        {
+            get { return isPopup; }
+            protected set { isPopup = value; }
+        }
+
+        bool isPopup = false;
+
+
+        /// <summary>
+        /// Indicates how long the screen takes to
+        /// transition on when it is activated.
+        /// </summary>
+        public TimeSpan TransitionOnTime
+        {
+            get { return transitionOnTime; }
+            protected set { transitionOnTime = value; }
+        }
+
+        TimeSpan transitionOnTime = TimeSpan.Zero;
+
+
+        /// <summary>
+        /// Indicates how long the screen takes to
+        /// transition off when it is deactivated.
+        /// </summary>
+        public TimeSpan TransitionOffTime
+        {
+            get { return transitionOffTime; }
+            protected set { transitionOffTime = value; }
+        }
+
+        TimeSpan transitionOffTime = TimeSpan.Zero;
+
+
+        /// <summary>
+        /// Gets the current position of the screen transition, ranging
+        /// from zero (fully active, no transition) to one (transitioned
+        /// fully off to nothing).
+        /// </summary>
+        public float TransitionPosition
+        {
+            get { return transitionPosition; }
+            protected set { transitionPosition = value; }
+        }
+
+        float transitionPosition = 1;
+
+
+        /// <summary>
+        /// Gets the current alpha of the screen transition, ranging
+        /// from 1 (fully active, no transition) to 0 (transitioned
+        /// fully off to nothing).
+        /// </summary>
+        public float TransitionAlpha
+        {
+            get { return 1f - TransitionPosition; }
+        }
+
+
+        /// <summary>
+        /// Gets the current screen transition state.
+        /// </summary>
+        public ScreenState ScreenState
+        {
+            get { return screenState; }
+            protected set { screenState = value; }
+        }
+
+        ScreenState screenState = ScreenState.TransitionOn;
+
+
+        /// <summary>
+        /// There are two possible reasons why a screen might be transitioning
+        /// off. It could be temporarily going away to make room for another
+        /// screen that is on top of it, or it could be going away for good.
+        /// This property indicates whether the screen is exiting for real:
+        /// if set, the screen will automatically remove itself as soon as the
+        /// transition finishes.
+        /// </summary>
+        public bool IsExiting
+        {
+            get { return isExiting; }
+            protected internal set { isExiting = value; }
+        }
+
+        bool isExiting = false;
+
+
+        /// <summary>
+        /// Checks whether this screen is active and can respond to user input.
+        /// </summary>
+        public bool IsActive
+        {
+            get
+            {
+                return !otherScreenHasFocus &&
+                       (screenState == ScreenState.TransitionOn ||
+                        screenState == ScreenState.Active);
+            }
+        }
+
+        bool otherScreenHasFocus;
+
+
+        /// <summary>
+        /// Gets the manager that this screen belongs to.
+        /// </summary>
+        public ScreenManager ScreenManager
+        {
+            get { return screenManager; }
+            internal set { screenManager = value; }
+        }
+
+        ScreenManager screenManager;
+
+
+        /// <summary>
+        /// Gets the index of the player who is currently controlling this screen,
+        /// or null if it is accepting input from any player. This is used to lock
+        /// the game to a specific player profile. The main menu responds to input
+        /// from any connected gamepad, but whichever player makes a selection from
+        /// this menu is given control over all subsequent screens, so other gamepads
+        /// are inactive until the controlling player returns to the main menu.
+        /// </summary>
+        public PlayerIndex? ControllingPlayer
+        {
+            get { return controllingPlayer; }
+            internal set { controllingPlayer = value; }
+        }
+
+        PlayerIndex? controllingPlayer;
+
+        /// <summary>
+        /// Gets the gestures the screen is interested in. Screens should be as specific
+        /// as possible with gestures to increase the accuracy of the gesture engine.
+        /// For example, most menus only need Tap or perhaps Tap and VerticalDrag to operate.
+        /// These gestures are handled by the ScreenManager when screens change and
+        /// all gestures are placed in the InputState passed to the HandleInput method.
+        /// </summary>
+        public GestureType EnabledGestures
+        {
+            get { return enabledGestures; }
+            protected set
+            {
+                enabledGestures = value;
+
+                // the screen manager handles this during screen changes, but
+                // if this screen is active and the gesture types are changing,
+                // we have to update the TouchPanel ourself.
+                if (ScreenState == ScreenState.Active)
+                {
+                    TouchPanel.EnabledGestures = value;
+                }
+            }
+        }
+
+        GestureType enabledGestures = GestureType.None;
+
+        /// <summary>
+        /// Gets whether or not this screen is serializable. If this is true,
+        /// the screen will be recorded into the screen manager's state and
+        /// its Serialize and Deserialize methods will be called as appropriate.
+        /// If this is false, the screen will be ignored during serialization.
+        /// By default, all screens are assumed to be serializable.
+        /// </summary>
+        public bool IsSerializable
+        {
+            get { return isSerializable; }
+            protected set { isSerializable = value; }
+        }
+
+        bool isSerializable = true;
+
+
+
+
+
+        /// <summary>
+        /// Load graphics content for the screen.
+        /// </summary>
+        public virtual void LoadContent() { }
+
+
+        /// <summary>
+        /// Unload content for the screen.
+        /// </summary>
+        public virtual void UnloadContent() { }
+
+
+
+
+
+        /// <summary>
+        /// Allows the screen to run logic, such as updating the transition position.
+        /// Unlike HandleInput, this method is called regardless of whether the screen
+        /// is active, hidden, or in the middle of a transition.
+        /// </summary>
+        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                      bool coveredByOtherScreen)
+        {
+            this.otherScreenHasFocus = otherScreenHasFocus;
+
+            if (isExiting)
+            {
+                // If the screen is going away to die, it should transition off.
+                screenState = ScreenState.TransitionOff;
+
+                if (!UpdateTransition(gameTime, transitionOffTime, 1))
+                {
+                    // When the transition finishes, remove the screen.
+                    ScreenManager.RemoveScreen(this);
+                }
+            }
+            else if (coveredByOtherScreen)
+            {
+                // If the screen is covered by another, it should transition off.
+                if (UpdateTransition(gameTime, transitionOffTime, 1))
+                {
+                    // Still busy transitioning.
+                    screenState = ScreenState.TransitionOff;
+                }
+                else
+                {
+                    // Transition finished!
+                    screenState = ScreenState.Hidden;
+                }
+            }
+            else
+            {
+                // Otherwise the screen should transition on and become active.
+                if (UpdateTransition(gameTime, transitionOnTime, -1))
+                {
+                    // Still busy transitioning.
+                    screenState = ScreenState.TransitionOn;
+                }
+                else
+                {
+                    // Transition finished!
+                    screenState = ScreenState.Active;
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Helper for updating the screen transition position.
+        /// </summary>
+        bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
+        {
+            // How much should we move by?
+            float transitionDelta;
+
+            if (time == TimeSpan.Zero)
+                transitionDelta = 1;
+            else
+                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
+                                          time.TotalMilliseconds);
+
+            // Update the transition position.
+            transitionPosition += transitionDelta * direction;
+
+            // Did we reach the end of the transition?
+            if (((direction < 0) && (transitionPosition <= 0)) ||
+                ((direction > 0) && (transitionPosition >= 1)))
+            {
+                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
+                return false;
+            }
+
+            // Otherwise we are still busy transitioning.
+            return true;
+        }
+
+
+        /// <summary>
+        /// Allows the screen to handle user input. Unlike Update, this method
+        /// is only called when the screen is active, and not when some other
+        /// screen has taken the focus.
+        /// </summary>
+        public virtual void HandleInput(InputState input) { }
+
+
+        /// <summary>
+        /// This is called when the screen should draw itself.
+        /// </summary>
+        public virtual void Draw(GameTime gameTime) { }
+
+
+
+
+        /// <summary>
+        /// Tells the screen to serialize its state into the given stream.
+        /// </summary>
+        public virtual void Serialize(Stream stream) { }
+
+        /// <summary>
+        /// Tells the screen to deserialize its state from the given stream.
+        /// </summary>
+        public virtual void Deserialize(Stream stream) { }
+
+        /// <summary>
+        /// Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
+        /// instantly kills the screen, this method respects the transition timings
+        /// and will give the screen a chance to gradually transition off.
+        /// </summary>
+        public void ExitScreen()
+        {
+            if (TransitionOffTime == TimeSpan.Zero)
+            {
+                // If the screen has a zero transition time, remove it immediately.
+                ScreenManager.RemoveScreen(this);
+            }
+            else
+            {
+                // Otherwise flag that it should transition off and then exit.
+                isExiting = true;
+            }
+        }
+
+
+
+        /// <summary>
+        /// A helper method which loads assets using the screen manager's
+        /// associated game content loader.
+        /// </summary>
+        /// <typeparam name="T">Type of asset.</typeparam>
+        /// <param name="assetName">Asset name, relative to the loader root
+        /// directory, and not including the .xnb extension.</param>
+        /// <returns></returns>
+        public T Load<T>(string assetName)
+        {
+            return ScreenManager.Game.Content.Load<T>(assetName);
+        }
+    }
+}

+ 483 - 0
CatapultWars/Core/Screens/GameplayScreen.cs

@@ -0,0 +1,483 @@
+//-----------------------------------------------------------------------------
+// GameplayScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Audio;
+using System.IO.IsolatedStorage;
+using System.IO;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+//using Microsoft.Devices.Sensors;
+using GameStateManagement;
+
+namespace CatapultGame
+{
+    class GameplayScreen : GameScreen
+    {
+        // Texture Members
+        Texture2D foregroundTexture;
+        Texture2D cloud1Texture;
+        Texture2D cloud2Texture;
+        Texture2D mountainTexture;
+        Texture2D skyTexture;
+        Texture2D hudBackgroundTexture;
+        Texture2D ammoTypeTexture;
+        Texture2D windArrowTexture;
+        Texture2D defeatTexture;
+        Texture2D victoryTexture;
+        SpriteFont hudFont;
+
+        // Rendering members
+        Vector2 cloud1Position;
+        Vector2 cloud2Position;
+
+        Vector2 playerHUDPosition;
+        Vector2 computerHUDPosition;
+        Vector2 windArrowPosition;
+
+        // Gameplay members
+        Human player;
+        AI computer;
+        Vector2 wind;
+        bool changeTurn;
+        bool isHumanTurn;
+        bool gameOver;
+        Random random;
+        const int minWind = 0;
+        const int maxWind = 10;
+
+        // Helper members
+        bool isDragging;
+
+        public GameplayScreen()
+        {
+            EnabledGestures = GestureType.FreeDrag |
+                GestureType.DragComplete |
+                GestureType.Tap;
+
+            random = new Random();
+        }
+
+        /// <summary>
+        /// Loads the game assets and initializes "players"
+        /// </summary>
+        public override void LoadContent()
+        {
+            base.LoadContent();
+#if ANDROID || IPHONE			
+			LoadAssets();
+#endif			
+            // Start the game
+            Start();
+        }
+
+        public void LoadAssets()
+        {
+            // Load textures
+            foregroundTexture = Load<Texture2D>("Textures/Backgrounds/gameplay_screen");
+            cloud1Texture = Load<Texture2D>("Textures/Backgrounds/cloud1");
+            cloud2Texture = Load<Texture2D>("Textures/Backgrounds/cloud2");
+            mountainTexture = Load<Texture2D>("Textures/Backgrounds/mountain");
+            skyTexture = Load<Texture2D>("Textures/Backgrounds/sky");
+            defeatTexture = Load<Texture2D>("Textures/Backgrounds/defeat");
+            victoryTexture = Load<Texture2D>("Textures/Backgrounds/victory");
+            hudBackgroundTexture = Load<Texture2D>("Textures/HUD/hudBackground");
+            windArrowTexture = Load<Texture2D>("Textures/HUD/windArrow");
+            ammoTypeTexture = Load<Texture2D>("Textures/HUD/ammoType");
+			
+			
+			
+            // Load font
+            hudFont = Load<SpriteFont>("Fonts/HUDFont");
+
+            // Define initial cloud position
+            cloud1Position = new Vector2(224 - cloud1Texture.Width, 32);
+            cloud2Position = new Vector2(64, 90);
+
+            // Define initial HUD positions
+            playerHUDPosition = new Vector2(7, 7);
+            computerHUDPosition = new Vector2(613, 7);
+            windArrowPosition = new Vector2(345, 46);
+
+            // Initialize human & AI players
+            player = new Human(ScreenManager.Game, ScreenManager.SpriteBatch);
+            player.Initialize();
+            player.Name = "Player";
+
+            computer = new AI(ScreenManager.Game, ScreenManager.SpriteBatch);
+            computer.Initialize();
+            computer.Name = "Phone";
+
+            // Identify enemies
+            player.Enemy = computer;
+            computer.Enemy = player;
+        }
+
+        /// <summary>
+        /// Runs one frame of update for the game.
+        /// </summary>
+        /// <param name="gameTime">Provides a snapshot of timing values.</param>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
+        {
+            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // Check it one of the players reached 5 and stop the game
+            if ((player.Catapult.GameOver || computer.Catapult.GameOver) &&
+                (gameOver == false))
+            {
+                gameOver = true;
+
+                if (player.Score > computer.Score)
+                {
+                    AudioManager.PlaySound("gameOver_Win");
+                }
+                else
+                {
+                    AudioManager.PlaySound("gameOver_Lose");
+                }
+
+                return;
+            }
+
+            // If Reset flag raised and both catapults are not animating - 
+            // active catapult finished the cycle, new turn!
+            if ((player.Catapult.CurrentState == CatapultState.Reset ||
+                computer.Catapult.CurrentState == CatapultState.Reset) &&
+                !(player.Catapult.AnimationRunning ||
+                computer.Catapult.AnimationRunning))
+            {
+                changeTurn = true;
+
+                if (player.IsActive == true) //Last turn was a human turn?
+                {
+                    player.IsActive = false;
+                    computer.IsActive = true;
+                    isHumanTurn = false;
+                    player.Catapult.CurrentState = CatapultState.Idle;
+                    computer.Catapult.CurrentState = CatapultState.Aiming;
+                }
+                else //It was an AI turn
+                {
+                    player.IsActive = true;
+                    computer.IsActive = false;
+                    isHumanTurn = true;
+                    computer.Catapult.CurrentState = CatapultState.Idle;
+                    player.Catapult.CurrentState = CatapultState.Idle;
+                }
+            }
+
+            if (changeTurn)
+            {
+                // Update wind
+                wind = new Vector2(random.Next(-1, 2),
+                    random.Next(minWind, maxWind + 1));
+
+                // Set new wind value to the players and 
+                player.Catapult.Wind = computer.Catapult.Wind =
+                    wind.X > 0 ? wind.Y : -wind.Y;
+                changeTurn = false;
+            }
+
+            // Update the players
+            player.Update(gameTime);
+            computer.Update(gameTime);
+
+            // Updates the clouds position
+            UpdateClouds(elapsed);
+
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+        }
+
+        /// <summary>
+        /// Draw the game world, effects, and HUD
+        /// </summary>
+        /// <param name="gameTime">The elapsed time since last Draw</param>
+        public override void Draw(GameTime gameTime)
+        {
+            ScreenManager.SpriteBatch.Begin();
+
+            // Render all parts of the screen
+            DrawBackground();
+            DrawComputer(gameTime);
+            DrawPlayer(gameTime);
+            DrawHud();
+
+            ScreenManager.SpriteBatch.End();
+        }
+
+        /// <summary>
+        /// Input helper method provided by GameScreen.  Packages up the various input
+        /// values for ease of use.
+        /// </summary>
+        /// <param name="input">The state of the gamepads</param>
+        public override void HandleInput(InputState input)
+        {
+            if (input == null)
+                throw new ArgumentNullException("input");
+
+            if (gameOver)
+            {
+                if (input.IsPauseGame(null))
+                {
+                    FinishCurrentGame();
+                }
+
+                foreach (GestureSample gestureSample in input.Gestures)
+                {
+                    if (gestureSample.GestureType == GestureType.Tap)
+                    {
+                        FinishCurrentGame();
+                    }
+                }
+
+                return;
+            }
+
+            if (input.IsPauseGame(null))
+            {
+                PauseCurrentGame();
+            }
+            else if (isHumanTurn &&
+                (player.Catapult.CurrentState == CatapultState.Idle ||
+                    player.Catapult.CurrentState == CatapultState.Aiming))
+            {
+				// First we try with mouse input
+				player.HandleInput(input);
+				if (input.MouseGesture == MouseGestureType.FreeDrag)
+					isDragging = true;
+				else if (input.MouseGesture == MouseGestureType.DragComplete)
+					isDragging = false;
+
+                // Read all available gestures
+                foreach (GestureSample gestureSample in input.Gestures)
+                {
+                    if (gestureSample.GestureType == GestureType.FreeDrag)
+                        isDragging = true;
+                    else if (gestureSample.GestureType == GestureType.DragComplete)
+                        isDragging = false;
+
+                    player.HandleInput(gestureSample);
+                }
+            }
+        }
+
+        private void UpdateClouds(float elapsedTime)
+        {
+            // Move the clouds according to the wind
+            int windDirection = wind.X > 0 ? 1 : -1;
+
+            cloud1Position += new Vector2(24.0f, 0.0f) * elapsedTime *
+                windDirection * wind.Y;
+            if (cloud1Position.X > ScreenManager.GraphicsDevice.Viewport.Width)
+                cloud1Position.X = -cloud1Texture.Width * 2.0f;
+            else if (cloud1Position.X < -cloud1Texture.Width * 2.0f)
+                cloud1Position.X = ScreenManager.GraphicsDevice.Viewport.Width;
+
+            cloud2Position += new Vector2(16.0f, 0.0f) * elapsedTime *
+                windDirection * wind.Y;
+            if (cloud2Position.X > ScreenManager.GraphicsDevice.Viewport.Width)
+                cloud2Position.X = -cloud2Texture.Width * 2.0f;
+            else if (cloud2Position.X < -cloud2Texture.Width * 2.0f)
+                cloud2Position.X = ScreenManager.GraphicsDevice.Viewport.Width;
+        }
+
+        /// <summary>
+        /// Draws the player's catapult
+        /// </summary>
+        void DrawPlayer(GameTime gameTime)
+        {
+            if (!gameOver)
+                player.Draw(gameTime);
+        }
+
+        /// <summary>
+        /// Draws the AI's catapult
+        /// </summary>
+        void DrawComputer(GameTime gameTime)
+        {
+            if (!gameOver)
+                computer.Draw(gameTime);
+        }
+
+        /// <summary>
+        /// Draw the sky, clouds, mountains, etc. 
+        /// </summary>
+        private void DrawBackground()
+        {
+            // Clear the background
+            ScreenManager.Game.GraphicsDevice.Clear(Color.White);
+
+            // Draw the Sky
+            ScreenManager.SpriteBatch.Draw(skyTexture, Vector2.Zero, Color.White);
+
+            // Draw Cloud #1
+            ScreenManager.SpriteBatch.Draw(cloud1Texture,
+                cloud1Position, Color.White);
+
+            // Draw the Mountain
+            ScreenManager.SpriteBatch.Draw(mountainTexture,
+                Vector2.Zero, Color.White);
+
+            // Draw Cloud #2
+            ScreenManager.SpriteBatch.Draw(cloud2Texture,
+                cloud2Position, Color.White);
+
+            // Draw the Castle, trees, and foreground 
+            ScreenManager.SpriteBatch.Draw(foregroundTexture,
+                Vector2.Zero, Color.White);
+        }
+
+        /// <summary>
+        /// Draw the HUD, which consists of the score elements and the GAME OVER tag.
+        /// </summary>
+        void DrawHud()
+        {
+            if (gameOver)
+            {
+                Texture2D texture;
+                if (player.Score > computer.Score)
+                {
+                    texture = victoryTexture;
+                }
+                else
+                {
+                    texture = defeatTexture;
+                }
+
+                ScreenManager.SpriteBatch.Draw(
+                    texture,
+                    new Vector2(ScreenManager.Game.GraphicsDevice.Viewport.Width / 2 - texture.Width / 2,
+                                ScreenManager.Game.GraphicsDevice.Viewport.Height / 2 - texture.Height / 2),
+                    Color.White);
+            }
+            else
+            {
+                // Draw Player Hud
+                ScreenManager.SpriteBatch.Draw(hudBackgroundTexture,
+                    playerHUDPosition, Color.White);
+                ScreenManager.SpriteBatch.Draw(ammoTypeTexture,
+                    playerHUDPosition + new Vector2(33, 35), Color.White);
+                DrawString(hudFont, player.Score.ToString(),
+                    playerHUDPosition + new Vector2(123, 35), Color.White);
+                DrawString(hudFont, player.Name,
+                    playerHUDPosition + new Vector2(40, 1), Color.Blue);
+
+                // Draw Computer Hud
+                ScreenManager.SpriteBatch.Draw(hudBackgroundTexture,
+                    computerHUDPosition, Color.White);
+                ScreenManager.SpriteBatch.Draw(ammoTypeTexture,
+                    computerHUDPosition + new Vector2(33, 35), Color.White);
+                DrawString(hudFont, computer.Score.ToString(),
+                    computerHUDPosition + new Vector2(123, 35), Color.White);
+                DrawString(hudFont, computer.Name,
+                    computerHUDPosition + new Vector2(40, 1), Color.Red);
+
+                // Draw Wind direction
+                string text = "WIND";
+                Vector2 size = hudFont.MeasureString(text);
+                Vector2 windarrowScale = new Vector2(wind.Y / 10, 1);
+                ScreenManager.SpriteBatch.Draw(windArrowTexture,
+                    windArrowPosition, null, Color.White, 0, Vector2.Zero,
+                    windarrowScale, wind.X > 0
+                    ? SpriteEffects.None : SpriteEffects.FlipHorizontally, 0);
+
+                DrawString(hudFont, text,
+                    windArrowPosition - new Vector2(0, size.Y), Color.Black);
+                if (wind.Y == 0)
+                {
+                    text = "NONE";
+                    DrawString(hudFont, text, windArrowPosition, Color.Black);
+                }
+
+                if (isHumanTurn)
+                {
+                    // Prepare human prompt message
+                    text = !isDragging ?
+                        "Drag Anywhere to Fire" : "Release to Fire!";
+                    size = hudFont.MeasureString(text);
+                }
+                else
+                {
+                    // Prepare AI message
+                    text = "I'll get you yet!";
+                    size = hudFont.MeasureString(text);
+                }
+
+                DrawString(hudFont, text,
+                    new Vector2(
+                        ScreenManager.GraphicsDevice.Viewport.Width / 2 - size.X / 2,
+                        ScreenManager.GraphicsDevice.Viewport.Height - size.Y),
+                        Color.Green);
+            }
+        }
+
+        /// <summary>
+        /// A simple helper to draw shadowed text.
+        /// </summary>
+        void DrawString(SpriteFont font, string text, Vector2 position, Color color)
+        {
+            ScreenManager.SpriteBatch.DrawString(font, text,
+                new Vector2(position.X + 1, position.Y + 1), Color.Black);
+            ScreenManager.SpriteBatch.DrawString(font, text, position, color);
+        }
+
+        /// <summary>
+        /// A simple helper to draw shadowed text.
+        /// </summary>
+        void DrawString(SpriteFont font, string text, Vector2 position, Color color, float fontScale)
+        {
+            ScreenManager.SpriteBatch.DrawString(font, text, new Vector2(position.X + 1,
+                position.Y + 1), Color.Black, 0, new Vector2(0, font.LineSpacing / 2),
+                fontScale, SpriteEffects.None, 0);
+            ScreenManager.SpriteBatch.DrawString(font, text, position, color, 0,
+                new Vector2(0, font.LineSpacing / 2), fontScale, SpriteEffects.None, 0);
+        }
+
+        /// <summary>
+        /// Finish the current game
+        /// </summary>
+        private void FinishCurrentGame()
+        {
+            ExitScreen();
+        }
+
+        /// <summary>
+        /// Pause the current game
+        /// </summary>
+        private void PauseCurrentGame()
+        {
+            var pauseMenuBackground = new BackgroundScreen();
+
+            if (isDragging)
+            {
+                isDragging = false;
+                player.Catapult.CurrentState = CatapultState.Idle;
+            }
+
+            ScreenManager.AddScreen(pauseMenuBackground, null);
+            ScreenManager.AddScreen(new PauseScreen(pauseMenuBackground, 
+                player, computer), null);
+        }
+
+        /// <summary>
+        /// Starts a new game session, setting all game states to initial values.
+        /// </summary>
+        void Start()
+        {
+            // Set initial wind direction
+            wind = Vector2.Zero;
+            isHumanTurn = false;
+            changeTurn = true;
+            computer.Catapult.CurrentState = CatapultState.Reset;
+        }
+    }
+}

+ 174 - 0
CatapultWars/Core/Screens/InstructionsScreen.cs

@@ -0,0 +1,174 @@
+//-----------------------------------------------------------------------------
+// BackgroundScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework;
+using GameStateManagement;
+using Microsoft.Xna.Framework.Input.Touch;
+
+#if MONOMAC
+using MonoMac.AppKit;
+using MonoMac.Foundation;
+#endif
+
+#if IPHONE
+using MonoTouch.UIKit;
+using MonoTouch.Foundation;
+#endif
+
+namespace CatapultGame
+{
+	class InstructionsScreen : GameScreen
+	{
+		Texture2D background;
+		SpriteFont font;
+		bool isLoading;
+		GameplayScreen gameplayScreen;
+		System.Threading.Thread thread;
+
+		public InstructionsScreen ()
+		{
+			EnabledGestures = GestureType.Tap;
+
+			TransitionOnTime = TimeSpan.FromSeconds (0);
+			TransitionOffTime = TimeSpan.FromSeconds (0.5);
+		}
+
+		public override void LoadContent ()
+		{
+			background = Load<Texture2D> ("Textures/Backgrounds/instructions");
+			font = Load<SpriteFont> ("Fonts/MenuFont");
+		}
+
+		public override void Update (GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
+		{
+			// If additional thread is running, skip
+			if (null != thread) {
+				// If additional thread finished loading and the screen is not exiting
+				if (thread.ThreadState == System.Threading.ThreadState.Stopped && !IsExiting) {
+					isLoading = false;
+					
+					// Exit the screen and show the gameplay screen 
+					// with pre-loaded assets
+					ExitScreen ();
+					ScreenManager.AddScreen (gameplayScreen, null);
+				}
+			}
+			base.Update (gameTime, otherScreenHasFocus, coveredByOtherScreen);
+		}
+
+		public override void HandleInput (InputState input)
+		{
+			if (isLoading == true) {
+#if ANDROID || IPHONE
+				// Exit the screen and show the gameplay screen 
+					// with pre-loaded assets
+				ExitScreen ();
+				ScreenManager.AddScreen (gameplayScreen, null);
+#endif				
+				base.HandleInput (input);
+				return;
+			}
+			PlayerIndex player;
+			if (input.IsNewKeyPress (Microsoft.Xna.Framework.Input.Keys.Space, ControllingPlayer, out player) ||
+			    input.IsNewKeyPress (Microsoft.Xna.Framework.Input.Keys.Enter, ControllingPlayer, out player) ||
+			    input.MouseGesture.HasFlag(MouseGestureType.LeftClick)||
+			    input.IsNewButtonPress (Microsoft.Xna.Framework.Input.Buttons.Start, ControllingPlayer, out player)) {
+				// Create a new instance of the gameplay screen
+				gameplayScreen = new GameplayScreen ();
+				gameplayScreen.ScreenManager = ScreenManager;
+
+				// Start loading the resources in additional thread
+#if MONOMAC
+				// create a new thread using BackgroundWorkerThread as method to execute
+				thread = new Thread (LoadAssetsWorkerThread as ThreadStart);
+#else
+				thread = new System.Threading.Thread (new System.Threading.ThreadStart (gameplayScreen.LoadAssets));
+#endif
+				isLoading = true;
+				// start it
+				thread.Start ();
+
+			}
+
+			foreach (var gesture in input.Gestures) {
+				if (gesture.GestureType == GestureType.Tap) {
+					// Create a new instance of the gameplay screen
+					gameplayScreen = new GameplayScreen ();
+					gameplayScreen.ScreenManager = ScreenManager;
+					
+#if ANDROID || IPHONE	
+					isLoading = true;									
+#else				
+					// Start loading the resources in additional thread
+					thread = new System.Threading.Thread (new System.Threading.ThreadStart (gameplayScreen.LoadAssets));
+					isLoading = true;
+					thread.Start ();	
+#endif										
+					
+				}
+			}
+
+			base.HandleInput (input);
+		}
+
+		void LoadAssetsWorkerThread ()
+		{
+
+#if MONOMAC || IPHONE			
+			// Create an Autorelease Pool or we will leak objects.
+			using (var pool = new NSAutoreleasePool()) {
+#else				
+				
+#endif				
+				// Make sure we invoke this on the Main Thread or OpenGL will throw an error
+#if MONOMAC
+				MonoMac.AppKit.NSApplication.SharedApplication.BeginInvokeOnMainThread (delegate {
+#endif
+#if IPHONE
+				var invokeOnMainThredObj = new NSObject();
+				invokeOnMainThredObj.InvokeOnMainThread(delegate {
+#endif
+					gameplayScreen.LoadAssets ();
+#if MONOMAC || IPHONE						
+				});
+					
+			}				
+#endif				
+
+		}
+
+		public override void Draw (GameTime gameTime)
+		{
+			SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+
+			spriteBatch.Begin ();
+
+			// Draw Background
+			spriteBatch.Draw (background, new Vector2 (0, 0), new Color (255, 255, 255, TransitionAlpha));
+
+			// If loading gameplay screen resource in the 
+			// background show "Loading..." text
+			if (isLoading) {
+				string text = "Loading...";
+				Vector2 size = font.MeasureString (text);
+				Vector2 position = new Vector2 ((ScreenManager.GraphicsDevice.Viewport.Width - size.X) / 2,
+				                                (ScreenManager.GraphicsDevice.Viewport.Height - size.Y) / 2);
+				spriteBatch.DrawString (font, text, position, Color.Black);
+				spriteBatch.DrawString (font, text, position - new Vector2 (-4, 4), new Color (255f, 150f, 0f));
+			}
+
+			spriteBatch.End ();
+		}
+	}
+}

+ 70 - 0
CatapultWars/Core/Screens/MainMenuScreen.cs

@@ -0,0 +1,70 @@
+//-----------------------------------------------------------------------------
+// MainMenuScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+
+namespace CatapultGame
+{
+    class MainMenuScreen : MenuScreen
+    {
+        public MainMenuScreen()
+            : base(String.Empty)
+        {
+            IsPopup = true;
+
+            // Create our menu entries.
+            MenuEntry startGameMenuEntry = new MenuEntry("Play");
+            MenuEntry exitMenuEntry = new MenuEntry("Exit");
+
+            // Hook up menu event handlers.
+            startGameMenuEntry.Selected += StartGameMenuEntrySelected;
+            exitMenuEntry.Selected += OnCancel;
+
+            // Add entries to the menu.
+            MenuEntries.Add(startGameMenuEntry);
+            MenuEntries.Add(exitMenuEntry);
+        }
+
+        protected override void UpdateMenuEntryLocations()
+        {
+            base.UpdateMenuEntryLocations();
+
+            foreach (var entry in MenuEntries)
+            {
+                Vector2 position = entry.Position;
+
+                position.Y += 60;
+
+                entry.Position = position;
+            }
+        }
+
+        /// <summary>
+        /// Handles "Play" menu item selection
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void StartGameMenuEntrySelected(object sender, EventArgs e)
+        {
+            ScreenManager.AddScreen(new InstructionsScreen(), null);
+        }
+
+        /// <summary>
+        /// Handles "Exit" menu item selection
+        /// </summary>
+        /// 
+        protected override void OnCancel(PlayerIndex playerIndex)
+        {
+            ScreenManager.Game.Exit();
+        }
+    }
+}

+ 173 - 0
CatapultWars/Core/Screens/MenuEntry.cs

@@ -0,0 +1,173 @@
+//-----------------------------------------------------------------------------
+// MenuEntry.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Helper class represents a single entry in a MenuScreen. By default this
+    /// just draws the entry text string, but it can be customized to display menu
+    /// entries in different ways. This also provides an event that will be raised
+    /// when the menu entry is selected.
+    /// </summary>
+    class MenuEntry
+    {
+
+        /// <summary>
+        /// The text rendered for this entry.
+        /// </summary>
+        string text;
+
+        /// <summary>
+        /// Tracks a fading selection effect on the entry.
+        /// </summary>
+        /// <remarks>
+        /// The entries transition out of the selection effect when they are deselected.
+        /// </remarks>
+        float selectionFade;
+
+        /// <summary>
+        /// The position at which the entry is drawn. This is set by the MenuScreen
+        /// each frame in Update.
+        /// </summary>
+        Vector2 position;
+
+
+
+
+        /// <summary>
+        /// Gets or sets the text of this menu entry.
+        /// </summary>
+        public string Text
+        {
+            get { return text; }
+            set { text = value; }
+        }
+
+        /// <summary>
+        /// Gets or sets the position at which to draw this menu entry.
+        /// </summary>
+        public Vector2 Position
+        {
+            get { return position; }
+            set { position = value; }
+        }
+
+
+
+
+
+        /// <summary>
+        /// Event raised when the menu entry is selected.
+        /// </summary>
+        public event EventHandler<PlayerIndexEventArgs> Selected;
+
+
+        /// <summary>
+        /// Method for raising the Selected event.
+        /// </summary>
+        protected internal virtual void OnSelectEntry(PlayerIndex playerIndex)
+        {
+            if (Selected != null)
+                Selected(this, new PlayerIndexEventArgs(playerIndex));
+        }
+
+
+
+        /// <summary>
+        /// Constructs a new menu entry with the specified text.
+        /// </summary>
+        public MenuEntry(string text)
+        {
+            this.text = text;
+        }
+
+
+
+
+        /// <summary>
+        /// Updates the menu entry.
+        /// </summary>
+        public virtual void Update(MenuScreen screen, bool isSelected, GameTime gameTime)
+        {
+            // there is no such thing as a selected item on Windows Phone, so we always
+            // force isSelected to be false
+#if WINDOWS_PHONE
+            isSelected = false;
+#endif
+            // When the menu selection changes, entries gradually fade between
+            // their selected and deselected appearance, rather than instantly
+            // popping to the new state.
+            float fadeSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 4;
+
+            if (isSelected)
+                selectionFade = Math.Min(selectionFade + fadeSpeed, 1);
+            else
+                selectionFade = Math.Max(selectionFade - fadeSpeed, 0);
+        }
+
+
+        /// <summary>
+        /// Draws the menu entry. This can be overridden to customize the appearance.
+        /// </summary>
+        public virtual void Draw(MenuScreen screen, bool isSelected, GameTime gameTime)
+        {
+            // there is no such thing as a selected item on Windows Phone, so we always
+            // force isSelected to be false
+#if WINDOWS_PHONE
+            isSelected = false;
+#endif
+
+            // Draw the selected entry in yellow, otherwise white.
+            Color color = isSelected ? Color.Yellow : Color.White;
+
+            // Pulsate the size of the selected menu entry.
+            double time = gameTime.TotalGameTime.TotalSeconds;
+            
+            float pulsate = (float)Math.Sin(time * 6) + 1;
+
+            float scale = 1 + pulsate * 0.05f * selectionFade;
+
+            // Modify the alpha to fade text out during transitions.
+            color = new Color(color.R, color.G, color.B) * screen.TransitionAlpha;
+
+            // Draw text, centered on the middle of each line.
+            ScreenManager screenManager = screen.ScreenManager;
+            SpriteBatch spriteBatch = screenManager.SpriteBatch;
+            SpriteFont font = screenManager.Font;
+
+            Vector2 origin = new Vector2(0, font.LineSpacing / 2);
+
+            spriteBatch.DrawString(font, text, position + new Vector2(4, 4), Color.Black, 0,
+                origin, scale, SpriteEffects.None, 0);
+            spriteBatch.DrawString(font, text, position, color, 0,
+                                   origin, scale, SpriteEffects.None, 0);
+        }
+
+
+        /// <summary>
+        /// Queries how much space this menu entry requires.
+        /// </summary>
+        public virtual int GetHeight(MenuScreen screen)
+        {
+            return screen.ScreenManager.Font.LineSpacing;
+        }
+
+        /// <summary>
+        /// Queries how wide the entry is, used for centering on the screen.
+        /// </summary>
+        public virtual int GetWidth(MenuScreen screen)
+        {
+            return (int)screen.ScreenManager.Font.MeasureString(Text).X;
+        }
+
+    }
+}

+ 309 - 0
CatapultWars/Core/Screens/MenuScreen.cs

@@ -0,0 +1,309 @@
+//-----------------------------------------------------------------------------
+// MenuScreen.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Input;
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// Base class for screens that contain a menu of options. The user can
+    /// move up and down to select an entry, or cancel to back out of the screen.
+    /// </summary>
+    abstract class MenuScreen : GameScreen
+    {
+
+        // the number of pixels to pad above and below menu entries for touch input
+        const int menuEntryPadding = 35;
+
+        List<MenuEntry> menuEntries = new List<MenuEntry>();
+        int selectedEntry = 0;
+        string menuTitle;
+
+
+
+
+        /// <summary>
+        /// Gets the list of menu entries, so derived classes can add
+        /// or change the menu contents.
+        /// </summary>
+        protected IList<MenuEntry> MenuEntries
+        {
+            get { return menuEntries; }
+        }
+
+
+
+
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        public MenuScreen(string menuTitle)
+        {
+            // menus generally only need Tap for menu selection
+            EnabledGestures = GestureType.Tap;
+
+            this.menuTitle = menuTitle;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.5);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+        }
+
+
+
+
+        /// <summary>
+        /// Allows the screen to create the hit bounds for a particular menu entry.
+        /// </summary>
+        protected virtual Rectangle GetMenuEntryHitBounds(MenuEntry entry)
+        {
+            // the hit bounds are the entire width of the screen, and the height of the entry
+            // with some additional padding above and below.
+            return new Rectangle(
+                0,
+                (int)entry.Position.Y - menuEntryPadding,
+                ScreenManager.GraphicsDevice.Viewport.Width,
+                entry.GetHeight(this) + (menuEntryPadding * 2));
+        }
+
+        /// <summary>
+        /// Responds to user input, changing the selected entry and accepting
+        /// or cancelling the menu.
+        /// </summary>
+        public override void HandleInput(InputState input)
+        {
+            // we cancel the current menu screen if the user presses the back button
+            PlayerIndex player;
+            if (input.IsNewButtonPress(Buttons.Back, ControllingPlayer, out player))
+            {
+                OnCancel(player);
+            }
+
+			if (input.IsMenuDown(ControllingPlayer)) {
+				if (selectedEntry < menuEntries.Count - 1)
+					selectedEntry += 1;
+			}
+
+			if (input.IsMenuUp(ControllingPlayer)) {
+				if (selectedEntry > 0)
+					selectedEntry -= 1;
+			}
+
+			if (input.IsMenuSelect(ControllingPlayer, out player)) {
+				menuEntries[selectedEntry].OnSelectEntry(player);
+			}
+
+			if (input.MouseGesture.HasFlag(MouseGestureType.LeftClick)) {
+
+				Point clickLocation = new Point((int)input.CurrentMousePosition.X, (int)input.CurrentMousePosition.Y);
+				// iterate the entries to see if any were tapped
+				for (int i = 0; i < menuEntries.Count; i++)
+				{
+					MenuEntry menuEntry = menuEntries[i];
+
+					if (GetMenuEntryHitBounds(menuEntry).Contains(clickLocation))
+					{
+						// select the entry. since gestures are only available on Windows Phone,
+						// we can safely pass PlayerIndex.One to all entries since there is only
+						// one player on Windows Phone.
+						OnSelectEntry(i, PlayerIndex.One);
+					}
+				}
+			}
+
+			if (input.MouseGesture.HasFlag(MouseGestureType.Move)) {
+
+				Point clickLocation = new Point((int)input.CurrentMousePosition.X, (int)input.CurrentMousePosition.Y);
+				// iterate the entries to see if any were tapped
+				for (int i = 0; i < menuEntries.Count; i++)
+				{
+					MenuEntry menuEntry = menuEntries[i];
+
+					if (GetMenuEntryHitBounds(menuEntry).Contains(clickLocation))
+					{
+						// select the entry. since gestures are only available on Windows Phone,
+						// we can safely pass PlayerIndex.One to all entries since there is only
+						// one player on Windows Phone.
+						//OnSelectEntry(i, PlayerIndex.One);
+						selectedEntry = i;
+					}
+				}
+			}
+
+            // look for any taps that occurred and select any entries that were tapped
+            foreach (GestureSample gesture in input.Gestures)
+            {
+                if (gesture.GestureType == GestureType.Tap)
+                {
+                    // convert the position to a Point that we can test against a Rectangle
+                    Point tapLocation = new Point((int)gesture.Position.X, (int)gesture.Position.Y);
+
+                    // iterate the entries to see if any were tapped
+                    for (int i = 0; i < menuEntries.Count; i++)
+                    {
+                        MenuEntry menuEntry = menuEntries[i];
+
+                        if (GetMenuEntryHitBounds(menuEntry).Contains(tapLocation))
+                        {
+                            // select the entry. since gestures are only available on Windows Phone,
+                            // we can safely pass PlayerIndex.One to all entries since there is only
+                            // one player on Windows Phone.
+                            OnSelectEntry(i, PlayerIndex.One);
+                        }
+                    }
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Handler for when the user has chosen a menu entry.
+        /// </summary>
+        protected virtual void OnSelectEntry(int entryIndex, PlayerIndex playerIndex)
+        {
+            menuEntries[entryIndex].OnSelectEntry(playerIndex);
+        }
+
+
+        /// <summary>
+        /// Handler for when the user has cancelled the menu.
+        /// </summary>
+        protected virtual void OnCancel(PlayerIndex playerIndex)
+        {
+            ExitScreen();
+        }
+
+
+        /// <summary>
+        /// Helper overload makes it easy to use OnCancel as a MenuEntry event handler.
+        /// </summary>
+        protected void OnCancel(object sender, PlayerIndexEventArgs e)
+        {
+            OnCancel(e.PlayerIndex);
+        }
+
+
+
+
+
+        /// <summary>
+        /// Allows the screen the chance to position the menu entries. By default
+        /// all menu entries are lined up in a vertical list, centered on the screen.
+        /// </summary>
+        protected virtual void UpdateMenuEntryLocations()
+        {
+            // Make the menu slide into place during transitions, using a
+            // power curve to make things look more interesting (this makes
+            // the movement slow down as it nears the end).
+            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
+
+            // start at Y = 175; each X value is generated per entry
+            Vector2 position = new Vector2(0f, 175f);
+
+            // update each menu entry's location in turn
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                MenuEntry menuEntry = menuEntries[i];
+                
+                // each entry is to be centered horizontally
+                position.X = ScreenManager.GraphicsDevice.Viewport.Width / 2 - menuEntry.GetWidth(this) / 2;
+
+                if (ScreenState == ScreenState.TransitionOn)
+                    position.X -= transitionOffset * 256;
+                else
+                    position.X += transitionOffset * 512;
+
+                // set the entry's position
+                menuEntry.Position = position;
+
+                // move down for the next entry the size of this entry plus our padding
+                position.Y += menuEntry.GetHeight(this) + (menuEntryPadding * 2);
+            }
+        }
+
+        /// <summary>
+        /// Updates the menu.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            // Update each nested MenuEntry object.
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                bool isSelected = IsActive && (i == selectedEntry);
+//				if (!menuEntries[i].HasMouseEnteredAttached)
+//					menuEntries [i].MouseEntered += MouseEntered;
+//				if (!menuEntries[i].HasMouseClickedAttached)
+//					menuEntries [i].MouseClicked += MouseClicked;
+                menuEntries[i].Update(this, isSelected, gameTime);
+            }
+        }
+
+//		void MouseClicked (object sender, MenuEntryEventArgs e)
+//		{
+//			e.MenuEntry.OnSelectEntry(PlayerIndex.One);
+//		}
+//
+//		void MouseEntered (object sender, MenuEntryEventArgs e)
+//		{
+//			//Console.WriteLine("Mouse Entered menu item " + e.MenuEntry.Text);
+//			selectedEntry = menuEntries.IndexOf(e.MenuEntry);
+//		}
+
+        /// <summary>
+        /// Draws the menu.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            // make sure our entries are in the right place before we draw them
+            UpdateMenuEntryLocations();
+
+            GraphicsDevice graphics = ScreenManager.GraphicsDevice;
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            SpriteFont font = ScreenManager.Font;
+
+            spriteBatch.Begin();
+
+            // Draw each menu entry in turn.
+            for (int i = 0; i < menuEntries.Count; i++)
+            {
+                MenuEntry menuEntry = menuEntries[i];
+
+                bool isSelected = IsActive && (i == selectedEntry);
+
+                menuEntry.Draw(this, isSelected, gameTime);
+            }
+
+            // Make the menu slide into place during transitions, using a
+            // power curve to make things look more interesting (this makes
+            // the movement slow down as it nears the end).
+            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
+
+            // Draw the menu title centered on the screen
+            Vector2 titlePosition = new Vector2(graphics.Viewport.Width / 2, 80);
+            Vector2 titleOrigin = font.MeasureString(menuTitle) / 2;
+            Color titleColor = new Color(192, 192, 192) * TransitionAlpha;
+            float titleScale = 1.25f;
+
+            titlePosition.Y -= transitionOffset * 100;
+
+            spriteBatch.DrawString(font, menuTitle, titlePosition, titleColor, 0,
+                                   titleOrigin, titleScale, SpriteEffects.None, 0);
+
+            spriteBatch.End();
+        }
+
+
+    }
+}

+ 107 - 0
CatapultWars/Core/Screens/PauseScreen.cs

@@ -0,0 +1,107 @@
+//-----------------------------------------------------------------------------
+// PauseScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+
+namespace CatapultGame
+{
+    class PauseScreen : MenuScreen
+    {
+        GameScreen backgroundScreen;
+        Player human;
+        Player computer;
+        bool prevHumanIsActive;
+        bool prevCompuerIsActive;
+
+        public PauseScreen(GameScreen backgroundScreen, Player human, Player computer)
+            : base(String.Empty)
+        {
+            IsPopup = true;
+
+            this.backgroundScreen = backgroundScreen;
+
+            // Create our menu entries.
+            MenuEntry startGameMenuEntry = new MenuEntry("Return");
+            MenuEntry exitMenuEntry = new MenuEntry("Quit Game");
+
+            // Hook up menu event handlers.
+            startGameMenuEntry.Selected += StartGameMenuEntrySelected;
+            exitMenuEntry.Selected += OnCancel;
+
+            // Add entries to the menu.
+            MenuEntries.Add(startGameMenuEntry);
+            MenuEntries.Add(exitMenuEntry);
+
+            this.human = human;
+            this.computer = computer;
+
+            // Preserve the old state of the game
+            prevHumanIsActive = this.human.Catapult.IsActive;
+            prevCompuerIsActive = this.computer.Catapult.IsActive;
+
+            // Pause the game logic progress
+            this.human.Catapult.IsActive = false;
+            this.computer.Catapult.IsActive = false;
+
+            AudioManager.PauseResumeSounds(false);
+        }
+
+        protected override void UpdateMenuEntryLocations()
+        {
+            base.UpdateMenuEntryLocations();
+
+            foreach (var entry in MenuEntries)
+            {
+                Vector2 position = entry.Position;
+
+                position.Y += 60;
+
+                entry.Position = position;
+            }
+        }
+
+        /// <summary>
+        /// Handles "Return" menu item selection
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void StartGameMenuEntrySelected(object sender, EventArgs e)
+        {
+            human.Catapult.IsActive = prevHumanIsActive;
+            computer.Catapult.IsActive = prevCompuerIsActive;
+
+            if (!(human as Human).isDragging)
+                AudioManager.PauseResumeSounds(true);
+            else
+            {
+                (human as Human).ResetDragState();
+                AudioManager.StopSounds();
+            }
+
+            backgroundScreen.ExitScreen();
+            ExitScreen();
+        }
+
+        /// <summary>
+        /// Handles "Exit" menu item selection
+        /// </summary>
+        /// 
+        protected override void OnCancel(PlayerIndex playerIndex)
+        {
+            AudioManager.StopSounds();
+            ScreenManager.AddScreen(new MainMenuScreen(), null);
+            ExitScreen();
+
+
+        }
+    }
+}

+ 152 - 0
CatapultWars/Core/Utility/Animation.cs

@@ -0,0 +1,152 @@
+//-----------------------------------------------------------------------------
+// Animation.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Animation.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace CatapultGame
+{
+    class Animation
+    {
+        // Animation variables
+        Texture2D animatedCharacter;
+        Point sheetSize;     
+        Point currentFrame;
+        public Point FrameSize { get; set; }
+
+        public int FrameCount
+        {
+            get { return sheetSize.X * sheetSize.Y; }
+        }
+
+        public Vector2 Offset { get; set; }
+
+        /// <summary>
+        /// Returns or sets the current animation frame.
+        /// </summary>
+        public int FrameIndex
+        {
+            get
+            {
+                return sheetSize.X * currentFrame.Y + currentFrame.X;
+            }
+            set
+            {
+                if (value >= sheetSize.X * sheetSize.Y + 1)
+                {
+                    throw new InvalidOperationException(
+                        "Specified frame index exeeds available frames");
+                }
+
+                currentFrame.Y = value / sheetSize.X;
+                currentFrame.X = value % sheetSize.X;
+            }
+        }
+
+        public bool IsActive { get; private set; }
+
+        /// <summary>
+        /// Constructor of an animation class
+        /// </summary>
+        /// <param name="frameSheet">Texture with animation frames sheet</param>
+        /// <param name="size">Single frame size</param>
+        /// <param name="frameSheetSize">The whole frame sheet size</param>
+        /// <param name="interval">Interval between progressing to the next frame</param>
+        public Animation(Texture2D frameSheet, Point size,
+            Point frameSheetSize)
+        {
+            animatedCharacter = frameSheet;
+            FrameSize = size;
+            sheetSize = frameSheetSize;
+            Offset = Vector2.Zero;
+        }
+
+        /// <summary>
+        /// Updates the animaton progress
+        /// </summary>
+        public void Update()
+        {
+            if (IsActive)
+            {
+                if (FrameIndex >= FrameCount - 1)
+                {                  
+                    IsActive = false;
+                    FrameIndex = FrameCount - 1; // Stop at last frame 
+                }
+                else
+                {
+                    // Remember that updating "currentFrame" will also
+                    // update the FrameIndex property.
+
+                    currentFrame.X++;
+                    if (currentFrame.X >= sheetSize.X)
+                    {
+                        currentFrame.X = 0;
+                        currentFrame.Y++;
+                    }
+                    if (currentFrame.Y >= sheetSize.Y)
+                        currentFrame.Y = 0;
+                }             
+            }           
+        }
+
+        /// <summary>
+        /// Rendering of the animation
+        /// </summary>
+        /// <param name="spriteBatch">SpriteBatch in which current 
+        /// frame will be rendered</param>
+        /// <param name="position">The position of current frame</param>
+        /// <param name="spriteEffect">SpriteEffect to apply on 
+        /// current frame</param>
+        public void Draw(SpriteBatch spriteBatch, Vector2 position, 
+            SpriteEffects spriteEffect)
+        {
+            Draw(spriteBatch, position, 1.0f, spriteEffect);
+        }
+
+        /// <summary>
+        /// Rendering of the animation
+        /// </summary>
+        /// <param name="spriteBatch">SpriteBatch in which current frame
+        /// will be rendered</param>
+        /// <param name="position">The position of the current frame</param>
+        /// <param name="scale">Scale factor to apply on the current frame</param>
+        /// <param name="spriteEffect">SpriteEffect to apply on the 
+        /// current frame</param>
+        public void Draw(SpriteBatch spriteBatch, Vector2 position, float scale, 
+            SpriteEffects spriteEffect)
+        {
+            spriteBatch.Draw(animatedCharacter, position + Offset, new Rectangle(
+                  FrameSize.X * currentFrame.X,
+                  FrameSize.Y * currentFrame.Y,
+                  FrameSize.X,
+                  FrameSize.Y),
+                  Color.White, 0f, Vector2.Zero, scale, spriteEffect, 0);
+        }
+
+        /// <summary>
+        /// Causes the animation to start playing from a specified frame index
+        /// </summary>
+        /// <param name="frameIndex"></param>
+        public void PlayFromFrameIndex(int frameIndex)
+        {
+            FrameIndex = frameIndex;
+            IsActive = true;
+        }
+    }
+}

+ 194 - 0
CatapultWars/Core/Utility/AudioManager.cs

@@ -0,0 +1,194 @@
+//-----------------------------------------------------------------------------
+// AudioManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Component that manages audio playback for all sounds.
+    /// </summary>
+    public class AudioManager : GameComponent
+    {
+        /// <summary>
+        /// The singleton for this type.
+        /// </summary>
+        private static AudioManager audioManager = null;
+
+        private SoundEffectInstance musicSound;
+        private Dictionary<string, SoundEffectInstance> soundBank;
+        private string[,] soundNames;
+
+
+        private AudioManager(Game game)
+            : base(game) { }
+
+        /// <summary>
+        /// Initialize the static AudioManager functionality.
+        /// </summary>
+        /// <param name="game">The game that this component will be attached to.</param>
+        public static void Initialize(Game game)
+        {
+            audioManager = new AudioManager(game);
+
+            if (game != null)
+            {
+                game.Components.Add(audioManager);
+            }
+        }
+
+
+        /// <summary>
+        /// Loads a sounds and organizes them for future usage
+        /// </summary>
+        public static void LoadSounds()
+        {
+            string soundLocation = "Sounds/";
+            audioManager.soundNames = new string[,] { 
+                            {"CatapultExplosion", "catapultExplosion"}, 
+                            {"Lose", "gameOver_Lose"},
+                            {"Win", "gameOver_Win"},
+                            {"BoulderHit", "boulderHit"},
+                            {"CatapultFire", "catapultFire"},
+                            {"RopeStretch", "ropeStretch"}};
+
+            audioManager.soundBank = new Dictionary<string, SoundEffectInstance>();
+
+            for (int i = 0; i < audioManager.soundNames.GetLength(0); i++)
+            {
+                SoundEffect se = audioManager.Game.Content.Load<SoundEffect>(
+                    soundLocation + audioManager.soundNames[i, 0]);
+                audioManager.soundBank.Add(
+                    audioManager.soundNames[i, 1], se.CreateInstance());
+            }
+        }
+
+        /// <summary>
+        /// Plays a sound by name.
+        /// </summary>
+        /// <param name="soundName">The name of the sound to play</param>
+        public static void PlaySound(string soundName)
+        {
+            // If the sound exists, start it
+            if (audioManager.soundBank.ContainsKey(soundName))
+                audioManager.soundBank[soundName].Play();
+        }
+
+        /// <summary>
+        /// Plays a sound by name.
+        /// </summary>
+        /// <param name="soundName">The name of the sound to play</param>
+        /// <param name="isLooped">Indicates if the sound should loop</param>
+        public static void PlaySound(string soundName, bool isLooped)
+        {
+            // If the sound exists, start it
+            if (audioManager.soundBank.ContainsKey(soundName))
+            {
+                if (audioManager.soundBank[soundName].IsLooped != isLooped)
+                    audioManager.soundBank[soundName].IsLooped = isLooped;
+
+                audioManager.soundBank[soundName].Play();
+            }
+        }
+
+
+        /// <summary>
+        /// Stops a sound mid-play. If the sound is not playing, this
+        /// method does nothing.
+        /// </summary>
+        /// <param name="soundName">The name of the sound to stop</param>
+        public static void StopSound(string soundName)
+        {
+            // If the sound exists, stop it
+            if (audioManager.soundBank.ContainsKey(soundName))
+                audioManager.soundBank[soundName].Stop();
+        }
+
+        /// <summary>
+        /// Stops a sound mid-play. If the sound is not playing, this
+        /// method does nothing.
+        /// </summary>
+        /// <param name="soundName">The name of the sound to stop</param>
+        public static void StopSounds()
+        {
+            var soundEffectInstances = from sound in audioManager.soundBank.Values
+                                       where sound.State != SoundState.Stopped
+                                       select sound;
+
+            foreach (var soundeffectInstance in soundEffectInstances)
+                soundeffectInstance.Stop();
+        }
+
+        /// <summary>
+        /// Pause or Resume all sounds to support pause screen
+        /// </summary>
+        /// <param name="isPause">Should pause or resume?</param>
+        public static void PauseResumeSounds(bool isPause)
+        {
+            SoundState state = isPause ? SoundState.Paused : SoundState.Playing;
+
+            var soundEffectInstances = from sound in audioManager.soundBank.Values
+                                where sound.State == state
+                                select sound;
+
+            foreach (var soundeffectInstance in soundEffectInstances)
+            {
+                if (isPause)
+                    soundeffectInstance.Play();
+                else
+                    soundeffectInstance.Pause();
+            }
+        }
+        /// <summary>
+        /// Play music by sound name.
+        /// </summary>
+        /// <param name="musicSoundName">The name of the music sound</param>
+        public static void PlayMusic(string musicSoundName)
+        {
+            // Stop the old music sound
+            if (audioManager.musicSound != null)
+                audioManager.musicSound.Stop(true);
+
+            // If the music sound exists
+            if (audioManager.soundBank.ContainsKey(musicSoundName))
+            {
+                // Get the instance and start it
+                audioManager.musicSound = audioManager.soundBank[musicSoundName];
+                if (!audioManager.musicSound.IsLooped)
+                    audioManager.musicSound.IsLooped = true;
+                audioManager.musicSound.Play();
+            }
+        }
+
+        /// <summary>
+        /// Clean up the component when it is disposing.
+        /// </summary>
+        protected override void Dispose(bool disposing)
+        {
+            try
+            {
+                if (disposing)
+                {
+                    foreach (var item in soundBank)
+                    {
+                        item.Value.Dispose();
+                    }
+                    soundBank.Clear();
+                    soundBank = null;
+                }
+            }
+            finally
+            {
+                base.Dispose(disposing);
+            }
+        }
+    }
+}

+ 337 - 0
CatapultWars/Core/Utility/InputState.cs

@@ -0,0 +1,337 @@
+//-----------------------------------------------------------------------------
+// InputState.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+
+namespace GameStateManagement
+{
+	/// <summary>
+	/// Helper for reading input from keyboard, gamepad, and touch input. This class 
+	/// tracks both the current and previous state of the input devices, and implements 
+	/// query methods for high level input actions such as "move up through the menu"
+	/// or "pause the game".
+	/// </summary>
+	public class InputState
+	{
+
+		public const int MaxInputs = 4;
+		public readonly KeyboardState[] CurrentKeyboardStates;
+		public readonly GamePadState[] CurrentGamePadStates;
+		public readonly KeyboardState[] LastKeyboardStates;
+		public readonly GamePadState[] LastGamePadStates;
+		public readonly bool[] GamePadWasConnected;
+		public TouchCollection TouchState;
+		public MouseState CurrentMouseState;
+		public MouseState LastMouseState;
+		public readonly List<GestureSample> Gestures = new List<GestureSample> ();
+
+
+
+
+		/// <summary>
+		/// Constructs a new input state.
+		/// </summary>
+		public InputState ()
+		{
+			CurrentKeyboardStates = new KeyboardState[MaxInputs];
+			CurrentGamePadStates = new GamePadState[MaxInputs];
+
+			LastKeyboardStates = new KeyboardState[MaxInputs];
+			LastGamePadStates = new GamePadState[MaxInputs];
+
+			GamePadWasConnected = new bool[MaxInputs];
+		}
+
+
+
+
+
+		/// <summary>
+		/// Reads the latest state of the keyboard and gamepad.
+		/// </summary>
+		public void Update ()
+		{
+			for (int i = 0; i < MaxInputs; i++) {
+				LastKeyboardStates [i] = CurrentKeyboardStates [i];
+				LastGamePadStates [i] = CurrentGamePadStates [i];
+
+				CurrentKeyboardStates [i] = Keyboard.GetState ((PlayerIndex)i);
+				CurrentGamePadStates [i] = GamePad.GetState ((PlayerIndex)i);
+
+				// Keep track of whether a gamepad has ever been
+				// connected, so we can detect if it is unplugged.
+				if (CurrentGamePadStates [i].IsConnected) {
+					GamePadWasConnected [i] = true;
+				}
+			}
+
+			TouchState = TouchPanel.GetState ();
+
+			LastMouseState = CurrentMouseState;
+			CurrentMouseState = Mouse.GetState ();
+
+			UpdateMouseStates();
+
+			Gestures.Clear ();
+			while (TouchPanel.IsGestureAvailable) {
+				Gestures.Add (TouchPanel.ReadGesture ());
+			}
+		}
+
+		bool dragging = false;
+		bool dragComplete = false;
+		bool leftMouseDown = false;
+		int dragThreshold = 3;
+		MouseGestureType mouseGestureType;
+		Vector2 currentMousePosition = Vector2.Zero;
+		Vector2 prevMousePosition = Vector2.Zero;
+		Vector2 dragMouseStart = Vector2.Zero;
+		Vector2 dragMouseEnd = Vector2.Zero;
+
+		public MouseGestureType MouseGesture
+		{
+			get {
+				return mouseGestureType;
+			}
+		}
+
+		public Vector2 CurrentMousePosition
+		{
+			get {
+				return currentMousePosition;
+			}
+		}
+
+		public Vector2 PrevMousePosition
+		{
+			get {
+				return prevMousePosition;
+			}
+		}
+
+		public Vector2 MouseDelta
+		{
+			get {
+				return prevMousePosition - currentMousePosition;
+			}
+		}
+
+		public Vector2 MouseDragDelta
+		{
+			get {
+				return dragMouseStart - dragMouseEnd;
+			}
+		}
+
+		public Vector2 MouseDragStartPosition
+		{
+			get {
+				return dragMouseStart;
+			}
+		}
+
+		public Vector2 MouseDragEndPosition
+		{
+			get {
+				return dragMouseEnd;
+			}
+		}
+
+		void UpdateMouseStates ()
+		{
+			currentMousePosition.X = CurrentMouseState.X;
+			currentMousePosition.Y = CurrentMouseState.Y;
+
+			prevMousePosition.X = LastMouseState.X;
+			prevMousePosition.Y = LastMouseState.Y;
+
+			if (mouseGestureType.HasFlag(MouseGestureType.LeftClick))
+				mouseGestureType = mouseGestureType ^ MouseGestureType.LeftClick;
+
+			if (mouseGestureType.HasFlag(MouseGestureType.Move))
+				mouseGestureType = mouseGestureType ^ MouseGestureType.Move;
+
+			if (MouseDelta.Length() != 0)
+				mouseGestureType = mouseGestureType | MouseGestureType.Move;
+
+			// If we were dragging and the left mouse button was released
+			// then we are no longer dragging and need to throw the banana.
+			if (CurrentMouseState.LeftButton == ButtonState.Released &&
+					dragging) {
+
+				leftMouseDown = false;
+				dragging = false;
+				dragComplete = true;
+				dragMouseEnd = currentMousePosition;
+				mouseGestureType |= MouseGestureType.DragComplete;
+				mouseGestureType = mouseGestureType ^ MouseGestureType.FreeDrag;
+				//Console.WriteLine ("Dragging: " + mouseGestureType);
+
+			}
+
+			// Let's set the left mouse down and the mouse origin
+			if (!leftMouseDown && CurrentMouseState.LeftButton == ButtonState.Pressed &&
+					!CurrentMouseState.Equals (LastMouseState)) {
+				//Console.WriteLine ("left down");
+				leftMouseDown = true;
+				dragComplete = false;
+				dragMouseStart = currentMousePosition;
+			}
+
+			if (leftMouseDown && CurrentMouseState.LeftButton == ButtonState.Released &&
+					!CurrentMouseState.Equals (LastMouseState)) {
+				leftMouseDown = false;
+				mouseGestureType |= MouseGestureType.LeftClick;
+			}
+
+			// Here we test the distance and if over the threshold then we set the dragging to true
+			//  Current threshold is 5 pixels.
+			if (leftMouseDown && !dragging) {
+
+				Vector2 delta = dragMouseStart - currentMousePosition;
+
+				if (delta.Length() > dragThreshold) {
+					dragging = true;
+					dragMouseStart = currentMousePosition;
+					mouseGestureType = mouseGestureType | MouseGestureType.FreeDrag;
+					//Console.WriteLine ("Dragging: " + mouseGestureType);
+				}
+			}
+
+			//Console.WriteLine(mouseGestureType);
+		}
+
+		/// <summary>
+		/// Helper for checking if a key was newly pressed during this update. The
+		/// controllingPlayer parameter specifies which player to read input for.
+		/// If this is null, it will accept input from any player. When a keypress
+		/// is detected, the output playerIndex reports which player pressed it.
+		/// </summary>
+		public bool IsNewKeyPress (Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+		{
+			if (controllingPlayer.HasValue) {
+				// Read input from the specified player.
+				playerIndex = controllingPlayer.Value;
+
+				int i = (int)playerIndex;
+
+				return (CurrentKeyboardStates [i].IsKeyDown (key) && LastKeyboardStates [i].IsKeyUp (key));
+			} else {
+				// Accept input from any player.
+				return (IsNewKeyPress (key, PlayerIndex.One, out playerIndex) ||
+					IsNewKeyPress (key, PlayerIndex.Two, out playerIndex) ||
+					IsNewKeyPress (key, PlayerIndex.Three, out playerIndex) ||
+					IsNewKeyPress (key, PlayerIndex.Four, out playerIndex));
+			}
+		}
+
+
+		/// <summary>
+		/// Helper for checking if a button was newly pressed during this update.
+		/// The controllingPlayer parameter specifies which player to read input for.
+		/// If this is null, it will accept input from any player. When a button press
+		/// is detected, the output playerIndex reports which player pressed it.
+		/// </summary>
+		public bool IsNewButtonPress (Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+		{
+			if (controllingPlayer.HasValue) {
+				// Read input from the specified player.
+				playerIndex = controllingPlayer.Value;
+
+				int i = (int)playerIndex;
+
+				return (CurrentGamePadStates [i].IsButtonDown (button) && LastGamePadStates [i].IsButtonUp (button));
+			} else {
+				// Accept input from any player.
+				return (IsNewButtonPress (button, PlayerIndex.One, out playerIndex) ||
+					IsNewButtonPress (button, PlayerIndex.Two, out playerIndex) ||
+					IsNewButtonPress (button, PlayerIndex.Three, out playerIndex) ||
+					IsNewButtonPress (button, PlayerIndex.Four, out playerIndex));
+			}
+		}
+
+
+		/// <summary>
+		/// Checks for a "menu select" input action.
+		/// The controllingPlayer parameter specifies which player to read input for.
+		/// If this is null, it will accept input from any player. When the action
+		/// is detected, the output playerIndex reports which player pressed it.
+		/// </summary>
+		public bool IsMenuSelect (PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+		{
+			return IsNewKeyPress (Keys.Space, controllingPlayer, out playerIndex) ||
+						IsNewKeyPress (Keys.Enter, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.A, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.Start, controllingPlayer, out playerIndex);
+		}
+
+
+		/// <summary>
+		/// Checks for a "menu cancel" input action.
+		/// The controllingPlayer parameter specifies which player to read input for.
+		/// If this is null, it will accept input from any player. When the action
+		/// is detected, the output playerIndex reports which player pressed it.
+		/// </summary>
+		public bool IsMenuCancel (PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
+		{
+			return IsNewKeyPress (Keys.Escape, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.B, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.Back, controllingPlayer, out playerIndex);
+		}
+
+
+		/// <summary>
+		/// Checks for a "menu up" input action.
+		/// The controllingPlayer parameter specifies which player to read
+		/// input for. If this is null, it will accept input from any player.
+		/// </summary>
+		public bool IsMenuUp (PlayerIndex? controllingPlayer)
+		{
+			PlayerIndex playerIndex;
+
+			return IsNewKeyPress (Keys.Up, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.DPadUp, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.LeftThumbstickUp, controllingPlayer, out playerIndex);
+		}
+
+
+		/// <summary>
+		/// Checks for a "menu down" input action.
+		/// The controllingPlayer parameter specifies which player to read
+		/// input for. If this is null, it will accept input from any player.
+		/// </summary>
+		public bool IsMenuDown (PlayerIndex? controllingPlayer)
+		{
+			PlayerIndex playerIndex;
+
+			return IsNewKeyPress (Keys.Down, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.DPadDown, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.LeftThumbstickDown, controllingPlayer, out playerIndex);
+		}
+
+
+		/// <summary>
+		/// Checks for a "pause the game" input action.
+		/// The controllingPlayer parameter specifies which player to read
+		/// input for. If this is null, it will accept input from any player.
+		/// </summary>
+		public bool IsPauseGame (PlayerIndex? controllingPlayer)
+		{
+			PlayerIndex playerIndex;
+
+			return IsNewKeyPress (Keys.Escape, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.Back, controllingPlayer, out playerIndex) ||
+						IsNewButtonPress (Buttons.Start, controllingPlayer, out playerIndex);
+		}
+
+
+	}
+}

+ 51 - 0
CatapultWars/Core/Utility/MouseGestureType.cs

@@ -0,0 +1,51 @@
+// /*
+// Microsoft Public License (Ms-PL)
+// MonoGame - Copyright © 2009-2010 The MonoGame Team
+// 
+// All rights reserved.
+// 
+// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
+// accept the license, do not use the software.
+// 
+// 1. Definitions
+// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under 
+// U.S. copyright law.
+// 
+// A "contribution" is the original software, or any additions or changes to the software.
+// A "contributor" is any person that distributes its contribution under this license.
+// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
+// 
+// 2. Grant of Rights
+// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
+// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, 
+// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+// 
+// 3. Conditions and Limitations
+// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, 
+// your patent license from such contributor to the software ends automatically.
+// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution 
+// notices that are present in the software.
+// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including 
+// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object 
+// code form, you may only do so under a license that complies with this license.
+// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
+// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
+// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
+// purpose and non-infringement.
+// */
+using System;
+
+namespace GameStateManagement
+{
+	[Flags]
+	public enum MouseGestureType
+	{
+		None = 0,
+		LeftClick = 1,
+		DragComplete = 2,		        
+		FreeDrag = 4,
+		Move = 8,
+	}
+}

+ 177 - 0
CatapultWars/Core/Utility/Projectile.cs

@@ -0,0 +1,177 @@
+//-----------------------------------------------------------------------------
+// Projectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Projectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace CatapultGame
+{
+    class Projectile : DrawableGameComponent
+    {
+        SpriteBatch spriteBatch;
+        Game curGame;
+        Random random;
+
+        // Textures for projectile
+        string textureName;
+        // Position and speed of projectile
+        Vector2 projectileVelocity = Vector2.Zero;
+        float projectileInitialVelocityY;
+        Vector2 projectileRotationPosition = Vector2.Zero;
+        float projectileRotation;
+        float flightTime;
+        bool isAI;
+        float hitOffset;
+        float gravity;
+
+        Vector2 projectileStartPosition;
+        public Vector2 ProjectileStartPosition
+        {
+            get
+            {
+                return projectileStartPosition;
+            }
+            set
+            {
+                projectileStartPosition = value;
+            }
+        }
+
+        Vector2 projectilePosition = Vector2.Zero;
+        public Vector2 ProjectilePosition
+        {
+            get
+            {
+                return projectilePosition;
+            }
+            set
+            {
+                projectilePosition = value;
+            }
+        }
+
+        /// <summary>
+        /// Gets the position where the projectile hit the ground.
+        /// Only valid after a hit occurs.
+        /// </summary>
+        public Vector2 ProjectileHitPosition { get; private set; }
+
+        Texture2D projectileTexture;
+        public Texture2D ProjectileTexture
+        {
+            get
+            {
+                return projectileTexture;
+            }
+            set
+            {
+                projectileTexture = value;
+            }
+        }
+
+        public Projectile(Game game)
+            : base(game)
+        {
+            curGame = game;
+            random = new Random();
+        }
+
+        public Projectile(Game game, SpriteBatch screenSpriteBatch,
+          string TextureName, Vector2 startPosition, float groundHitOffset,
+          bool isAi, float Gravity)
+            : this(game)
+        {
+            spriteBatch = screenSpriteBatch;
+            projectileStartPosition = startPosition;
+            textureName = TextureName;
+            isAI = isAi;
+            hitOffset = groundHitOffset;
+            gravity = Gravity;
+        }
+
+        public override void Initialize()
+        {
+            // Load a projectile texture
+            projectileTexture = curGame.Content.Load<Texture2D>(textureName);
+        }
+
+        public override void Draw(GameTime gameTime)
+        {
+            spriteBatch.Draw(projectileTexture, projectilePosition, null, 
+                Color.White, projectileRotation,
+                new Vector2(projectileTexture.Width / 2,
+                            projectileTexture.Height / 2),
+                1.0f, SpriteEffects.None, 0);
+
+            base.Draw(gameTime);
+        }
+
+        /// <summary>
+        /// Helper function - calculates the projectile position and velocity based on time.
+        /// </summary>
+        /// <param name="gameTime">The time since last calculation</param>
+        public void UpdateProjectileFlightData(GameTime gameTime, float wind, float gravity, out bool groundHit)
+        {
+            flightTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
+
+            // Calculate new projectile position using standard
+            // formulas, taking the wind as a force.
+            int direction = isAI ? -1 : 1;
+
+            float previousXPosition = projectilePosition.X;
+            float previousYPosition = projectilePosition.Y;
+
+            projectilePosition.X = projectileStartPosition.X + 
+                (direction * projectileVelocity.X * flightTime) + 
+                0.5f * (8 * wind * (float)Math.Pow(flightTime, 2));
+
+            projectilePosition.Y = projectileStartPosition.Y -
+                (projectileVelocity.Y * flightTime) +
+                0.5f * (gravity * (float)Math.Pow(flightTime, 2));            
+
+            // Calculate the projectile rotation
+            projectileRotation += MathHelper.ToRadians(projectileVelocity.X * 0.5f);
+
+            // Check if projectile hit the ground or even passed it 
+            // (could happen during normal calculation)
+            if (projectilePosition.Y >= 332 + hitOffset)
+            {
+                projectilePosition.X = previousXPosition;
+                projectilePosition.Y = previousYPosition;
+
+                ProjectileHitPosition = new Vector2(previousXPosition, 332);
+
+                groundHit = true;
+            }
+            else
+            {
+                groundHit = false;
+            }
+        }
+
+        public void Fire(float velocityX, float velocityY)
+        {
+            // Set initial projectile velocity
+            projectileVelocity.X = velocityX;
+            projectileVelocity.Y = velocityY;
+            projectileInitialVelocityY = projectileVelocity.Y;
+            // Reset calculation variables
+            flightTime = 0;
+        }
+    }
+}

+ 30 - 0
CatapultWars/Platforms/Android/Activity1.cs

@@ -0,0 +1,30 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using Android.Views;
+using Microsoft.Xna.Framework;
+
+namespace CatapultGame
+{
+    [Activity(
+        Label = "@string/app_name",
+        MainLauncher = true,
+        AlwaysRetainTaskState = true,
+        LaunchMode = LaunchMode.SingleInstance,
+        ScreenOrientation = ScreenOrientation.SensorLandscape,
+        ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize
+    )]
+    public class MainActivity : AndroidGameActivity
+    {
+        private CatapultGame _game;
+
+        protected override void OnCreate(Bundle bundle)
+        {
+            base.OnCreate(bundle);
+
+            _game = new CatapultGame();
+            SetContentView((View)_game.Services.GetService(typeof(View)));
+            _game.Run();
+        }
+    }
+}

+ 18 - 0
CatapultWars/Platforms/Android/AndroidManifest.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.asteroidbeltassault">
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="34" />
+    
+    <application android:allowBackup="true" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
+        <activity android:name="microsoft.xna.framework.AndroidGameActivity" 
+                  android:label="@string/app_name" 
+                  android:launchMode="singleInstance" 
+                  android:screenOrientation="sensorLandscape" 
+                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>

+ 33 - 0
CatapultWars/Platforms/Android/CatapultWars.Android.csproj

@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0-android</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>MonoGame.Samples.CatapultWars</RootNamespace>
+    <AssemblyName>MonoGame.Samples.CatapultWars</AssemblyName>
+    <SupportedOSPlatformVersion>28</SupportedOSPlatformVersion>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.Android" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\CatapultWars.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.wav" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 44 - 0
CatapultWars/Platforms/Android/Resources/AboutResources.txt

@@ -0,0 +1,44 @@
+Images, layout descriptions, binary blobs and string dictionaries can be included 
+in your application as resource files.  Various Android APIs are designed to 
+operate on the resource IDs instead of dealing with images, strings or binary blobs 
+directly.
+
+For example, a sample Android app that contains a user interface layout (main.axml),
+an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 
+would keep its resources in the "Resources" directory of the application:
+
+Resources/
+    drawable/
+        icon.png
+
+    layout/
+        main.axml
+
+    values/
+        strings.xml
+
+In order to get the build system to recognize Android resources, set the build action to
+"AndroidResource".  The native Android APIs do not operate directly with filenames, but 
+instead operate on resource IDs.  When you compile an Android application that uses resources, 
+the build system will package the resources for distribution and generate a class called "R" 
+(this is an Android convention) that contains the tokens for each one of the resources 
+included. For example, for the above Resources layout, this is what the R class would expose:
+
+public class R {
+    public class drawable {
+        public const int icon = 0x123;
+    }
+
+    public class layout {
+        public const int main = 0x456;
+    }
+
+    public class strings {
+        public const int first_string = 0xabc;
+        public const int second_string = 0xbcd;
+    }
+}
+
+You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 
+to reference the layout/main.axml file, or R.strings.first_string to reference the first 
+string in the dictionary file values/strings.xml.

+ 73 - 0
CatapultWars/Platforms/Android/Resources/Resource.designer.cs

@@ -0,0 +1,73 @@
+#pragma warning disable 1591
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.586
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+[assembly: Android.Runtime.ResourceDesignerAttribute("MonoGame.Samples.CatapultWars.Resource", IsApplication=true)]
+
+namespace MonoGame.Samples.CatapultWars
+{
+	
+	
+	[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
+	public partial class Resource
+	{
+		
+		Resource()
+		{
+			global::Android.Runtime.ResourceIdManager.UpdateIdValues ();
+		}
+		
+		public static void UpdateIdValues()
+		{
+		}
+		
+		public partial class Attribute
+		{
+			
+			private Attribute()
+			{
+			}
+		}
+		
+		public partial class Drawable
+		{
+			
+			// aapt resource value: 0x7f020000
+			public const int Icon = 2130837504;
+			
+			private Drawable()
+			{
+			}
+		}
+		
+		public partial class Layout
+		{
+			
+			// aapt resource value: 0x7f030000
+			public const int Main = 2130903040;
+			
+			private Layout()
+			{
+			}
+		}
+		
+		public partial class String
+		{
+			
+			// aapt resource value: 0x7f040000
+			public const int app_name = 2130968576;
+			
+			private String()
+			{
+			}
+		}
+	}
+}
+#pragma warning restore 1591

二進制
CatapultWars/Platforms/Android/Resources/drawable/Icon.png


+ 8 - 0
CatapultWars/Platforms/Android/Resources/layout/Main.axml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+</LinearLayout>
+

+ 4 - 0
CatapultWars/Platforms/Android/Resources/values/Strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">Catapault Wars</string>
+</resources>

+ 5 - 0
CatapultWars/Platforms/Android/Resources/values/styles.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="AppTheme" parent="@android:style/Theme.Holo.Light">
+    </style>
+</resources>

+ 36 - 0
CatapultWars/Platforms/Desktop/CatapultWars.DesktopGL.csproj

@@ -0,0 +1,36 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <AssemblyName>CatapultWars</AssemblyName>
+    <RootNamespace>CatapultGame</RootNamespace>
+    <DefineConstants>DESKTOPGL</DefineConstants>
+    <ApplicationManifest>..\..\Core\Content\app.manifest</ApplicationManifest>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\CatapultWars.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.wav" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 15 - 0
CatapultWars/Platforms/Desktop/Program.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace CatapultGame
+{
+    public static class Program
+    {
+        static void Main(string[] args)
+        {
+            using (var game = new CatapultGame())
+            {
+                game.Run();
+            }
+        }
+    }
+}

+ 38 - 0
CatapultWars/Platforms/Windows/CatapultWars.Windows.csproj

@@ -0,0 +1,38 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net8.0-windows</TargetFramework>
+    <PublishReadyToRun>false</PublishReadyToRun>
+    <TieredCompilation>false</TieredCompilation>
+    <UseWindowsForms>true</UseWindowsForms>
+    <AssemblyName>CatapultWars</AssemblyName>
+    <RootNamespace>CatapultGame</RootNamespace>
+    <DefineConstants>WINDOWS</DefineConstants>
+    <ApplicationManifest>..\..\Core\Content\app.manifest</ApplicationManifest>
+    <ApplicationIcon>..\..\Core\Content\Game.ico</ApplicationIcon>
+  </PropertyGroup>
+
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\CatapultWars.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.wav" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 16 - 0
CatapultWars/Platforms/Windows/Program.cs

@@ -0,0 +1,16 @@
+using System;
+
+namespace CatapultGame
+{
+    public static class Program
+    {
+        [STAThread]
+        static void Main(string[] args)
+        {
+            using (var game = new CatapultGame())
+            {
+                game.Run();
+            }
+        }
+    }
+}

+ 33 - 0
CatapultWars/Platforms/iOS/CatapultWars.iOS.csproj

@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0-ios</TargetFramework>
+    <AssemblyName>CatapultWars</AssemblyName>
+    <RootNamespace>CatapultGame</RootNamespace>
+    <Nullable>disable</Nullable>
+    <SupportedOSPlatformVersion>12.0</SupportedOSPlatformVersion>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.iOS" Version="3.8.*" />
+    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.*" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\CatapultWars.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.wav" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+</Project>

+ 16 - 0
CatapultWars/Platforms/iOS/Info.plist

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleIdentifier</key>
+	<string>com.yourcompany.Catapault</string>
+	<key>CFBundleName</key>
+	<string>Catapault</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>10.6</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>

+ 15 - 0
CatapultWars/Platforms/iOS/Program.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace CatapultGame
+{
+    public static class Program
+    {
+        static void Main(string[] args)
+        {
+            using (var game = new CatapultGame())
+            {
+                game.Run();
+            }
+        }
+    }
+}

+ 71 - 0
CatapultWars/README.md

@@ -0,0 +1,71 @@
+# CatapultWars MonoGame Sample
+
+This is a modernized, cross-platform MonoGame 3.8.4 sample project, organized for .NET 8 and Visual Studio/VS Code. It demonstrates a catapult game with support for Windows, DesktopGL, and Android (iOS project included, but not tested).
+
+## Project Structure
+
+```
+CatapaultWars/
+├── Core/                # Shared game logic, assets, and content
+├── Platforms/
+│   ├── Windows/         # Windows-specific entry point and project
+│   ├── Desktop/         # DesktopGL-specific entry point and project
+│   ├── Android/         # Android-specific entry point and project
+│   └── iOS/             # iOS-specific entry point and project
+├── Content/             # Pre-built .xnb assets (used directly)
+├── .vscode/             # VS Code tasks and launch configs
+└── README.md            # This file
+```
+
+## How to Build and Run
+
+### Prerequisites
+- .NET 8 SDK or newer
+- MonoGame 3.8.4 NuGet packages (local or public feed)
+- Visual Studio 2022+ or VS Code
+
+### Windows
+```
+dotnet build Platforms/Windows/CatapultWars.Windows.csproj
+# To run:
+dotnet run --project Platforms/Windows/CatapultWars.Windows.csproj
+```
+
+### DesktopGL
+```
+dotnet build Platforms/Desktop/CatapultWars.DesktopGL.csproj
+# To run:
+dotnet run --project Platforms/Desktop/CatapultWars.DesktopGL.csproj
+```
+
+### Android
+```
+dotnet build Platforms/Android/CatapultWars.Android.csproj
+```
+
+### iOS
+```
+dotnet build Platforms/iOS/CatapultWars.iOS.csproj
+```
+
+### Visual Studio
+- Open `CatapaultWars.sln` and set the desired platform project as startup.
+
+### VS Code
+- Use the provided `.vscode/tasks.json` and `.vscode/launch.json` for build/run/debug.
+
+## Notes
+- All shared code is in `/Core` and referenced by each platform project.
+- No Content.mgcb file is used; the game loads pre-built `.xnb` assets directly from `/Core/Content`.
+- Android and iOS projects may require additional setup for deployment/emulation.
+- If you encounter missing entry point errors, ensure each `/Platforms/[Platform]/Program.cs` exists and is correct for that platform.
+
+## Supported Platforms
+- Windows (DirectX)
+- DesktopGL (cross-platform)
+- Android (entry point: `Activity1.cs`)
+- iOS (Info.plist included, not tested)
+
+---
+
+For any issues, please check the project structure and ensure all dependencies are restored from the correct NuGet source.