Browse Source

Add new sample CatapultNetWars which adds Networking support to the CatapultWars Sample

Kenneth Pouncey 13 years ago
parent
commit
8416316e81
82 changed files with 8123 additions and 0 deletions
  1. BIN
      Samples/MacOS/CatapultNetWars/Background.png
  2. 595 0
      Samples/MacOS/CatapultNetWars/Catapult/Catapult.cs
  3. 191 0
      Samples/MacOS/CatapultNetWars/Catapult/Projectile.cs
  4. 105 0
      Samples/MacOS/CatapultNetWars/CatapultGame.cs
  5. 140 0
      Samples/MacOS/CatapultNetWars/CatapultNetWars.csproj
  6. 37 0
      Samples/MacOS/CatapultNetWars/CatapultNetWars.sln
  7. BIN
      Samples/MacOS/CatapultNetWars/Content/Fonts/HUDFont.xnb
  8. BIN
      Samples/MacOS/CatapultNetWars/Content/Fonts/MenuFont.xnb
  9. BIN
      Samples/MacOS/CatapultNetWars/Content/Sounds/BoulderHit.xnb
  10. BIN
      Samples/MacOS/CatapultNetWars/Content/Sounds/CatapultExplosion.xnb
  11. BIN
      Samples/MacOS/CatapultNetWars/Content/Sounds/CatapultFire.xnb
  12. BIN
      Samples/MacOS/CatapultNetWars/Content/Sounds/Lose.xnb
  13. BIN
      Samples/MacOS/CatapultNetWars/Content/Sounds/RopeStretch.xnb
  14. BIN
      Samples/MacOS/CatapultNetWars/Content/Sounds/Win.xnb
  15. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Ammo/rock_ammo.xnb
  16. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/blank.xnb
  17. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/cloud1.xnb
  18. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/cloud2.xnb
  19. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/defeat.xnb
  20. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/gameplay_screen.xnb
  21. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/instructions.xnb
  22. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/mountain.xnb
  23. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/sky.xnb
  24. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/title_screen.xnb
  25. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/victory.xnb
  26. 63 0
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/AnimationsDef.xml
  27. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/blueDestroyed/blueCatapult_destroyed.xnb
  28. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/blueFire/blueCatapult_fire.xnb
  29. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/blueIdle/blueIdle.xnb
  30. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/bluePullback/blueCatapult_Pullback.xnb
  31. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Fire_Miss/fire_miss.xnb
  32. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Hit_Smoke/smoke.xnb
  33. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redDestroyed/redCatapult_destroyed.xnb
  34. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redFire/redCatapult_fire.xnb
  35. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redIdle/redIdle.xnb
  36. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redPullback/redCatapult_Pullback.xnb
  37. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/HUD/Arrow.xnb
  38. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/HUD/ammoType.xnb
  39. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/HUD/hudBackground.xnb
  40. BIN
      Samples/MacOS/CatapultNetWars/Content/Textures/HUD/windArrow.xnb
  41. BIN
      Samples/MacOS/CatapultNetWars/Content/cat.xnb
  42. BIN
      Samples/MacOS/CatapultNetWars/Content/chat_able.xnb
  43. BIN
      Samples/MacOS/CatapultNetWars/Content/chat_mute.xnb
  44. BIN
      Samples/MacOS/CatapultNetWars/Content/chat_ready.xnb
  45. BIN
      Samples/MacOS/CatapultNetWars/Content/chat_talking.xnb
  46. BIN
      Samples/MacOS/CatapultNetWars/Content/gradient.xnb
  47. BIN
      Samples/MacOS/CatapultNetWars/Game.ico
  48. 28 0
      Samples/MacOS/CatapultNetWars/IMessageDisplay.cs
  49. 16 0
      Samples/MacOS/CatapultNetWars/Info.plist
  50. 228 0
      Samples/MacOS/CatapultNetWars/MessageDisplayComponent.cs
  51. 110 0
      Samples/MacOS/CatapultNetWars/Networking/AvailableSessionMenuEntry.cs
  52. 211 0
      Samples/MacOS/CatapultNetWars/Networking/CreateOrFindSessionScreen.cs
  53. 147 0
      Samples/MacOS/CatapultNetWars/Networking/JoinSessionScreen.cs
  54. 277 0
      Samples/MacOS/CatapultNetWars/Networking/LobbyScreen.cs
  55. 169 0
      Samples/MacOS/CatapultNetWars/Networking/NetworkBusyScreen.cs
  56. 90 0
      Samples/MacOS/CatapultNetWars/Networking/NetworkErrorScreen.cs
  57. 481 0
      Samples/MacOS/CatapultNetWars/Networking/NetworkSessionComponent.cs
  58. 53 0
      Samples/MacOS/CatapultNetWars/Networking/OperationCompletedEventArgs.cs
  59. 188 0
      Samples/MacOS/CatapultNetWars/Networking/ProfileSignInScreen.cs
  60. 79 0
      Samples/MacOS/CatapultNetWars/Players/AI.cs
  61. 222 0
      Samples/MacOS/CatapultNetWars/Players/Human.cs
  62. 94 0
      Samples/MacOS/CatapultNetWars/Players/Player.cs
  63. 67 0
      Samples/MacOS/CatapultNetWars/Program.cs
  64. 433 0
      Samples/MacOS/CatapultNetWars/Resources.Designer.cs
  65. BIN
      Samples/MacOS/CatapultNetWars/Resources.resources
  66. 250 0
      Samples/MacOS/CatapultNetWars/Resources.resx
  67. 394 0
      Samples/MacOS/CatapultNetWars/ScreenManager/GameScreen.cs
  68. 347 0
      Samples/MacOS/CatapultNetWars/ScreenManager/InputState.cs
  69. 187 0
      Samples/MacOS/CatapultNetWars/ScreenManager/MenuEntry.cs
  70. 324 0
      Samples/MacOS/CatapultNetWars/ScreenManager/MenuScreen.cs
  71. 53 0
      Samples/MacOS/CatapultNetWars/ScreenManager/MouseGestureType.cs
  72. 42 0
      Samples/MacOS/CatapultNetWars/ScreenManager/PlayerIndexEventArgs.cs
  73. 451 0
      Samples/MacOS/CatapultNetWars/ScreenManager/ScreenManager.cs
  74. 71 0
      Samples/MacOS/CatapultNetWars/Screens/BackgroundScreen.cs
  75. 733 0
      Samples/MacOS/CatapultNetWars/Screens/GameplayScreen.cs
  76. 162 0
      Samples/MacOS/CatapultNetWars/Screens/InstructionsScreen.cs
  77. 306 0
      Samples/MacOS/CatapultNetWars/Screens/LoadingScreen.cs
  78. 118 0
      Samples/MacOS/CatapultNetWars/Screens/MainMenuScreen.cs
  79. 168 0
      Samples/MacOS/CatapultNetWars/Screens/MessageBoxScreen.cs
  80. 119 0
      Samples/MacOS/CatapultNetWars/Screens/PauseScreen.cs
  81. 164 0
      Samples/MacOS/CatapultNetWars/Utility/Animation.cs
  82. 210 0
      Samples/MacOS/CatapultNetWars/Utility/AudioManager.cs

BIN
Samples/MacOS/CatapultNetWars/Background.png


+ 595 - 0
Samples/MacOS/CatapultNetWars/Catapult/Catapult.cs

@@ -0,0 +1,595 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Catapult.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region File Information
+//-----------------------------------------------------------------------------
+// Animation.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 System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+//using Microsoft.Devices;
+using System.Xml.Linq;
+#endregion
+
+namespace CatapultGame
+{
+    #region Catapult states definition enum
+    [Flags]
+    public enum CatapultState
+    {
+        Idle = 0x0,
+        Aiming = 0x1,
+        Firing = 0x2,
+        ProjectileFlying = 0x4,
+        ProjectileHit = 0x8,
+        Hit = 0x10,
+        Reset = 0x20,
+        Stalling = 0x40
+    }
+    #endregion
+
+    public class Catapult : DrawableGameComponent
+    {
+        #region Variables/Fields and Properties
+        // 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;
+        #endregion
+
+        #region Initialization
+        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 = XDocument.Load("Content/Textures/Catapults/AnimationsDef.xml");
+            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();
+        }
+        #endregion
+
+        #region Update and Render
+        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);
+        }
+        #endregion
+
+        #region Hit
+        /// <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;
+        }
+        #endregion
+
+        public void Fire(float velocity)
+        {
+            projectile.Fire(velocity, velocity);
+        }
+
+        #region Helper Functions
+        /// <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);
+        }
+        #endregion
+
+    }
+}

+ 191 - 0
Samples/MacOS/CatapultNetWars/Catapult/Projectile.cs

@@ -0,0 +1,191 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Projectile.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region File Information
+//-----------------------------------------------------------------------------
+// Projectile.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 System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace CatapultGame
+{
+    class Projectile : DrawableGameComponent
+    {
+        #region Fields/Properties
+        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;
+            }
+        }
+        #endregion
+
+        #region Initialization
+        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);
+        }
+        #endregion
+
+        #region Render
+        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);
+        }
+        #endregion
+
+        #region Public functionality
+        /// <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;
+        }
+        #endregion
+    }
+}

+ 105 - 0
Samples/MacOS/CatapultNetWars/CatapultGame.cs

@@ -0,0 +1,105 @@
+#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.GamerServices;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Media;
+using Microsoft.Xna.Framework.Net;
+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;
+
+		// By preloading any assets used by UI rendering, we avoid framerate glitches
+		// when they suddenly need to be loaded in the middle of a menu transition.
+		static readonly string[] preloadAssets =
+		{
+			"gradient",
+			"cat",
+			"chat_ready",
+			"chat_able",
+			"chat_talking",
+			"chat_mute",
+		};
+
+        #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);
+
+			Components.Add (new MessageDisplayComponent (this));
+			Components.Add (new GamerServicesComponent (this));
+			
+			//Add two new screens
+			screenManager.AddScreen (new BackgroundScreen (), null);
+			screenManager.AddScreen (new MainMenuScreen (), null);
+
+			// Listen for invite notification events.
+			NetworkSession.InviteAccepted += (sender, e) => NetworkSessionComponent.InviteAccepted (screenManager, e);
+
+			IsMouseVisible = true;
+#if !WINDOWS && !XBOX && !MACOS
+			//Switch to full screen for best game experience
+			graphics.IsFullScreen = true;
+#endif
+			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
+	}
+}

+ 140 - 0
Samples/MacOS/CatapultNetWars/CatapultNetWars.csproj

@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+    <ProductVersion>10.0.0</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{1F4BF77D-34DA-417D-96DE-24213A05729B}</ProjectGuid>
+    <ProjectTypeGuids>{948B3504-5B70-4649-8FE4-BDE1FB46EC69};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>CatapultGame</RootNamespace>
+    <AssemblyName>Catapault</AssemblyName>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG; MACOS</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+    <DebugType>none</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="MonoMac" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Info.plist" />
+    <None Include="Background.png" />
+    <None Include="Game.ico" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath)\Mono\MonoMac\v0.0\Mono.MonoMac.targets" />
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\..\..\..\Users\Jimmy\Public\Share\MonoMacSource\kjpgit\MonoGame\MonoGame.Framework\MonoGame.Framework.MacOS.csproj">
+      <Project>{36C538E6-C32A-4A8D-A39C-566173D7118E}</Project>
+      <Name>MonoGame.Framework.MacOS</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="CatapultGame.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Catapult\Catapult.cs" />
+    <Compile Include="Catapult\Projectile.cs" />
+    <Compile Include="Players\AI.cs" />
+    <Compile Include="Players\Human.cs" />
+    <Compile Include="Players\Player.cs" />
+    <Compile Include="ScreenManager\GameScreen.cs" />
+    <Compile Include="ScreenManager\InputState.cs" />
+    <Compile Include="ScreenManager\MenuEntry.cs" />
+    <Compile Include="ScreenManager\MenuScreen.cs" />
+    <Compile Include="ScreenManager\PlayerIndexEventArgs.cs" />
+    <Compile Include="ScreenManager\ScreenManager.cs" />
+    <Compile Include="Screens\BackgroundScreen.cs" />
+    <Compile Include="Screens\GameplayScreen.cs" />
+    <Compile Include="Screens\InstructionsScreen.cs" />
+    <Compile Include="Screens\MainMenuScreen.cs" />
+    <Compile Include="Screens\PauseScreen.cs" />
+    <Compile Include="Utility\Animation.cs" />
+    <Compile Include="Utility\AudioManager.cs" />
+    <Compile Include="ScreenManager\MouseGestureType.cs" />
+    <Compile Include="IMessageDisplay.cs" />
+    <Compile Include="MessageDisplayComponent.cs" />
+    <Compile Include="Networking\AvailableSessionMenuEntry.cs" />
+    <Compile Include="Networking\CreateOrFindSessionScreen.cs" />
+    <Compile Include="Networking\JoinSessionScreen.cs" />
+    <Compile Include="Networking\LobbyScreen.cs" />
+    <Compile Include="Networking\NetworkBusyScreen.cs" />
+    <Compile Include="Networking\NetworkErrorScreen.cs" />
+    <Compile Include="Networking\NetworkSessionComponent.cs" />
+    <Compile Include="Networking\OperationCompletedEventArgs.cs" />
+    <Compile Include="Networking\ProfileSignInScreen.cs" />
+    <Compile Include="Resources.Designer.cs" />
+    <Compile Include="Screens\LoadingScreen.cs" />
+    <Compile Include="Screens\MessageBoxScreen.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Content\Fonts\HUDFont.xnb" />
+    <Content Include="Content\Fonts\MenuFont.xnb" />
+    <Content Include="Content\Sounds\BoulderHit.xnb" />
+    <Content Include="Content\Sounds\CatapultExplosion.xnb" />
+    <Content Include="Content\Sounds\CatapultFire.xnb" />
+    <Content Include="Content\Sounds\Lose.xnb" />
+    <Content Include="Content\Sounds\RopeStretch.xnb" />
+    <Content Include="Content\Sounds\Win.xnb" />
+    <Content Include="Content\Textures\Ammo\rock_ammo.xnb" />
+    <Content Include="Content\Textures\Backgrounds\blank.xnb" />
+    <Content Include="Content\Textures\Backgrounds\cloud1.xnb" />
+    <Content Include="Content\Textures\Backgrounds\cloud2.xnb" />
+    <Content Include="Content\Textures\Backgrounds\defeat.xnb" />
+    <Content Include="Content\Textures\Backgrounds\gameplay_screen.xnb" />
+    <Content Include="Content\Textures\Backgrounds\instructions.xnb" />
+    <Content Include="Content\Textures\Backgrounds\mountain.xnb" />
+    <Content Include="Content\Textures\Backgrounds\sky.xnb" />
+    <Content Include="Content\Textures\Backgrounds\title_screen.xnb" />
+    <Content Include="Content\Textures\Backgrounds\victory.xnb" />
+    <Content Include="Content\Textures\Catapults\Blue\blueDestroyed\blueCatapult_destroyed.xnb" />
+    <Content Include="Content\Textures\Catapults\Blue\blueFire\blueCatapult_fire.xnb" />
+    <Content Include="Content\Textures\Catapults\Blue\blueIdle\blueIdle.xnb" />
+    <Content Include="Content\Textures\Catapults\Blue\bluePullback\blueCatapult_Pullback.xnb" />
+    <Content Include="Content\Textures\Catapults\Fire_Miss\fire_miss.xnb" />
+    <Content Include="Content\Textures\Catapults\Hit_Smoke\smoke.xnb" />
+    <Content Include="Content\Textures\Catapults\AnimationsDef.xml" />
+    <Content Include="Content\Textures\Catapults\Red\redDestroyed\redCatapult_destroyed.xnb" />
+    <Content Include="Content\Textures\Catapults\Red\redFire\redCatapult_fire.xnb" />
+    <Content Include="Content\Textures\Catapults\Red\redIdle\redIdle.xnb" />
+    <Content Include="Content\Textures\Catapults\Red\redPullback\redCatapult_Pullback.xnb" />
+    <Content Include="Content\Textures\HUD\Arrow.xnb" />
+    <Content Include="Content\Textures\HUD\ammoType.xnb" />
+    <Content Include="Content\Textures\HUD\hudBackground.xnb" />
+    <Content Include="Content\Textures\HUD\windArrow.xnb" />
+    <Content Include="Content\gradient.xnb" />
+    <Content Include="Content\cat.xnb" />
+    <Content Include="Content\chat_able.xnb" />
+    <Content Include="Content\chat_mute.xnb" />
+    <Content Include="Content\chat_ready.xnb" />
+    <Content Include="Content\chat_talking.xnb" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Networking\" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources.resx" />
+  </ItemGroup>
+</Project>

+ 37 - 0
Samples/MacOS/CatapultNetWars/CatapultNetWars.sln

@@ -0,0 +1,37 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatapultNetWars", "CatapultNetWars.csproj", "{1F4BF77D-34DA-417D-96DE-24213A05729B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Framework.MacOS", "..\..\..\..\..\..\..\Users\Jimmy\Public\Share\MonoMacSource\kjpgit\MonoGame\MonoGame.Framework\MonoGame.Framework.MacOS.csproj", "{36C538E6-C32A-4A8D-A39C-566173D7118E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lidgren.Network.MacOS", "..\..\..\..\..\..\..\Users\Jimmy\Public\Share\MonoMacSource\kjpgit\MonoGame\ThirdParty\Lidgren.Network\Lidgren.Network.MacOS.csproj", "{AE483C29-042E-4226-BA52-D247CE7676DA}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x86 = Debug|x86
+		Release|x86 = Release|x86
+		Distribution|Any CPU = Distribution|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1F4BF77D-34DA-417D-96DE-24213A05729B}.Debug|x86.ActiveCfg = Debug|x86
+		{1F4BF77D-34DA-417D-96DE-24213A05729B}.Debug|x86.Build.0 = Debug|x86
+		{1F4BF77D-34DA-417D-96DE-24213A05729B}.Release|x86.ActiveCfg = Release|x86
+		{1F4BF77D-34DA-417D-96DE-24213A05729B}.Release|x86.Build.0 = Release|x86
+		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Debug|x86.Build.0 = Debug|Any CPU
+		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Distribution|Any CPU.ActiveCfg = Distribution|Any CPU
+		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Distribution|Any CPU.Build.0 = Distribution|Any CPU
+		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Release|x86.ActiveCfg = Release|Any CPU
+		{36C538E6-C32A-4A8D-A39C-566173D7118E}.Release|x86.Build.0 = Release|Any CPU
+		{AE483C29-042E-4226-BA52-D247CE7676DA}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{AE483C29-042E-4226-BA52-D247CE7676DA}.Debug|x86.Build.0 = Debug|Any CPU
+		{AE483C29-042E-4226-BA52-D247CE7676DA}.Distribution|Any CPU.ActiveCfg = Debug|Any CPU
+		{AE483C29-042E-4226-BA52-D247CE7676DA}.Distribution|Any CPU.Build.0 = Debug|Any CPU
+		{AE483C29-042E-4226-BA52-D247CE7676DA}.Release|x86.ActiveCfg = Release|Any CPU
+		{AE483C29-042E-4226-BA52-D247CE7676DA}.Release|x86.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(MonoDevelopProperties) = preSolution
+		StartupItem = CatapultNetWars.csproj
+	EndGlobalSection
+EndGlobal

BIN
Samples/MacOS/CatapultNetWars/Content/Fonts/HUDFont.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Fonts/MenuFont.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Sounds/BoulderHit.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Sounds/CatapultExplosion.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Sounds/CatapultFire.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Sounds/Lose.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Sounds/RopeStretch.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Sounds/Win.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Ammo/rock_ammo.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/blank.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/cloud1.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/cloud2.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/defeat.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/gameplay_screen.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/instructions.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/mountain.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/sky.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/title_screen.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Backgrounds/victory.xnb


+ 63 - 0
Samples/MacOS/CatapultNetWars/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>
+

BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/blueDestroyed/blueCatapult_destroyed.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/blueFire/blueCatapult_fire.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/blueIdle/blueIdle.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Blue/bluePullback/blueCatapult_Pullback.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Fire_Miss/fire_miss.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Hit_Smoke/smoke.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redDestroyed/redCatapult_destroyed.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redFire/redCatapult_fire.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redIdle/redIdle.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/Catapults/Red/redPullback/redCatapult_Pullback.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/HUD/Arrow.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/HUD/ammoType.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/HUD/hudBackground.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/Textures/HUD/windArrow.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/cat.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/chat_able.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/chat_mute.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/chat_ready.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/chat_talking.xnb


BIN
Samples/MacOS/CatapultNetWars/Content/gradient.xnb


BIN
Samples/MacOS/CatapultNetWars/Game.ico


+ 28 - 0
Samples/MacOS/CatapultNetWars/IMessageDisplay.cs

@@ -0,0 +1,28 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// IMessageDisplay.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Interface used to display notification messages when interesting events occur,
+    /// for instance when gamers join or leave the network session. This interface
+    /// is registered as a service, so any piece of code wanting to display a message
+    /// can look it up from Game.Services, without needing to worry about how the
+    /// message display is implemented. In this sample, the MessageDisplayComponent
+    /// class implement this IMessageDisplay service.
+    /// </summary>
+    interface IMessageDisplay : IDrawable, IUpdateable
+    {
+        void ShowMessage(string message, params object[] parameters);
+    }
+}

+ 16 - 0
Samples/MacOS/CatapultNetWars/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>

+ 228 - 0
Samples/MacOS/CatapultNetWars/MessageDisplayComponent.cs

@@ -0,0 +1,228 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MessageDisplayComponent.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Component implements the IMessageDisplay interface. This is used to show
+    /// notification messages when interesting events occur, for instance when
+    /// gamers join or leave the network session
+    /// </summary>
+    class MessageDisplayComponent : DrawableGameComponent, IMessageDisplay
+    {
+        #region Fields
+
+        SpriteBatch spriteBatch;
+        SpriteFont font;
+
+        // List of the currently visible notification messages.
+        List<NotificationMessage> messages = new List<NotificationMessage>();
+
+        // Coordinates threadsafe access to the message list.
+        object syncObject = new object();
+
+        // Tweakable settings control how long each message is visible.
+        static readonly TimeSpan fadeInTime = TimeSpan.FromSeconds(0.25);
+        static readonly TimeSpan showTime = TimeSpan.FromSeconds(5);
+        static readonly TimeSpan fadeOutTime = TimeSpan.FromSeconds(0.5);
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new message display component.
+        /// </summary>
+        public MessageDisplayComponent(Game game)
+            : base(game)
+        {
+            // Register ourselves to implement the IMessageDisplay service.
+            game.Services.AddService(typeof(IMessageDisplay), this);
+        }
+
+
+
+        /// <summary>
+        /// Load graphics content for the message display.
+        /// </summary>
+        protected override void LoadContent()
+        {
+            spriteBatch = new SpriteBatch(GraphicsDevice);
+
+            //font = Game.Content.Load<SpriteFont>("menufont");
+            font = Game.Content.Load<SpriteFont>("Fonts/MenuFont");
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <summary>
+        /// Updates the message display component.
+        /// </summary>
+        public override void Update(GameTime gameTime)
+        {
+            lock (syncObject)
+            {
+                int index = 0;
+                float targetPosition = 0;
+
+                // Update each message in turn.
+                while (index < messages.Count)
+                {
+                    NotificationMessage message = messages[index];
+
+                    // Gradually slide the message toward its desired position.
+                    float positionDelta = targetPosition - message.Position;
+
+                    float velocity = (float)gameTime.ElapsedGameTime.TotalSeconds * 2;
+
+                    message.Position += positionDelta * Math.Min(velocity, 1);
+
+                    // Update the age of the message.
+                    message.Age += gameTime.ElapsedGameTime;
+
+                    if (message.Age < showTime + fadeOutTime)
+                    {
+                        // This message is still alive.
+                        index++;
+
+                        // Any subsequent messages should be positioned below
+                        // this one, unless it has started to fade out.
+                        if (message.Age < showTime)
+                            targetPosition++;
+                    }
+                    else
+                    {
+                        // This message is old, and should be removed.
+                        messages.RemoveAt(index);
+                    }
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Draws the message display component.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            lock (syncObject)
+            {
+                // Early out if there are no messages to display.
+                if (messages.Count == 0)
+                    return;
+
+                Vector2 position = new Vector2(GraphicsDevice.Viewport.Width - 100, 0);
+
+                spriteBatch.Begin();
+
+                // Draw each message in turn.
+                foreach (NotificationMessage message in messages)
+                {
+                    const float scale = 0.75f;
+
+                    // Compute the alpha of this message.
+                    float alpha = 1;
+
+                    if (message.Age < fadeInTime)
+                    {
+                        // Fading in.
+                        alpha = (float)(message.Age.TotalSeconds /
+                                        fadeInTime.TotalSeconds);
+                    }
+                    else if (message.Age > showTime)
+                    {
+                        // Fading out.
+                        TimeSpan fadeOut = showTime + fadeOutTime - message.Age;
+
+                        alpha = (float)(fadeOut.TotalSeconds /
+                                        fadeOutTime.TotalSeconds);
+                    }
+
+                    // Compute the message position.
+                    position.Y = 80 + message.Position * font.LineSpacing * scale;
+
+                    // Compute an origin value to right align each message.
+                    Vector2 origin = font.MeasureString(message.Text);
+                    origin.Y = 0;
+
+                    // Draw the message text, with a drop shadow.
+                    spriteBatch.DrawString(font, message.Text, position + Vector2.One,
+                                           Color.Black * alpha, 0,
+                                           origin, scale, SpriteEffects.None, 0);
+
+                    spriteBatch.DrawString(font, message.Text, position,
+                                           Color.White * alpha, 0,
+                                           origin, scale, SpriteEffects.None, 0);
+                }
+
+                spriteBatch.End();
+            }
+        }
+
+
+        #endregion
+
+        #region Implement IMessageDisplay
+
+
+        /// <summary>
+        /// Shows a new notification message.
+        /// </summary>
+        public void ShowMessage(string message, params object[] parameters)
+        {
+            string formattedMessage = string.Format(message, parameters);
+
+            lock (syncObject)
+            {
+                float startPosition = messages.Count;
+
+                messages.Add(new NotificationMessage(formattedMessage, startPosition));
+            }
+        }
+
+
+        #endregion
+
+        #region Nested Types
+
+
+        /// <summary>
+        /// Helper class stores the position and text of a single notification message.
+        /// </summary>
+        class NotificationMessage
+        {
+            public string Text;
+            public float Position;
+            public TimeSpan Age;
+
+
+            public NotificationMessage(string text, float position)
+            {
+                Text = text;
+                Position = position;
+                Age = TimeSpan.Zero;
+            }
+        }
+
+
+        #endregion
+    }
+}

+ 110 - 0
Samples/MacOS/CatapultNetWars/Networking/AvailableSessionMenuEntry.cs

@@ -0,0 +1,110 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// AvailableSessionMenuEntry.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Net;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Helper class customizes the standard MenuEntry class
+    /// for displaying AvailableNetworkSession objects.
+    /// </summary>
+    class AvailableSessionMenuEntry : MenuEntry
+    {
+        #region Fields
+
+        AvailableNetworkSession availableSession;
+        bool gotQualityOfService;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <summary>
+        /// Gets the available network session corresponding to this menu entry.
+        /// </summary>
+        public AvailableNetworkSession AvailableSession
+        {
+            get { return availableSession; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a menu entry describing an available network session.
+        /// </summary>
+        public AvailableSessionMenuEntry(AvailableNetworkSession availableSession)
+            : base(GetMenuItemText(availableSession))
+        {
+            this.availableSession = availableSession;
+        }
+
+
+        /// <summary>
+        /// Formats session information to create the menu text string.
+        /// </summary>
+        static string GetMenuItemText(AvailableNetworkSession session)
+        {
+            int totalSlots = session.CurrentGamerCount +
+                             session.OpenPublicGamerSlots;
+
+            return string.Format("{0} ({1}/{2})", session.HostGamertag,
+                                                  session.CurrentGamerCount,
+                                                  totalSlots);
+        }
+
+
+        #endregion
+
+        #region Update
+
+
+        /// <summary>
+        /// Updates the menu item text, adding information about the network
+        /// quality of service as soon as that becomes available.
+        /// </summary>
+        public override void Update(MenuScreen screen, bool isSelected,
+                                                       GameTime gameTime)
+        {
+            base.Update(screen, isSelected, gameTime);
+
+            // Quality of service data can take some time to query, so it will not
+            // be filled in straight away when NetworkSession.Find returns. We want
+            // to display the list of available sessions straight away, and then
+            // fill in the quality of service data whenever that becomes available,
+            // so we keep checking until this data shows up.
+            if (screen.IsActive && !gotQualityOfService)
+            {
+                QualityOfService qualityOfService = availableSession.QualityOfService;
+
+                if (qualityOfService.IsAvailable)
+                {
+                    TimeSpan pingTime = qualityOfService.AverageRoundtripTime;
+
+                    Text += string.Format(" - {0:0} ms", pingTime.TotalMilliseconds);
+
+                    gotQualityOfService = true;
+                }
+            }
+        }
+
+
+        #endregion
+    }
+}

+ 211 - 0
Samples/MacOS/CatapultNetWars/Networking/CreateOrFindSessionScreen.cs

@@ -0,0 +1,211 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// CreateOrFindSessionScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Net;
+using Microsoft.Xna.Framework.GamerServices;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+	/// <summary>
+	/// This menu screen lets the user choose whether to create a new
+	/// network session, or search for an existing session to join.
+	/// </summary>
+	class CreateOrFindSessionScreen : MenuScreen
+	{
+	#region Fields
+
+		NetworkSessionType sessionType;
+
+	#endregion
+
+	#region Initialization
+
+
+		/// <summary>
+		/// Constructor fills in the menu contents.
+		/// </summary>
+		public CreateOrFindSessionScreen (NetworkSessionType sessionType)
+		//: base(GetMenuTitle(sessionType))
+		: base(string.Empty)
+			{
+			this.sessionType = sessionType;
+
+			// Create our menu entries.
+			MenuEntry createSessionMenuEntry = new MenuEntry (Resources.CreateSession);
+			MenuEntry findSessionsMenuEntry = new MenuEntry (Resources.FindSessions);
+			MenuEntry backMenuEntry = new MenuEntry (Resources.Back);
+
+			// Hook up menu event handlers.
+			createSessionMenuEntry.Selected += CreateSessionMenuEntrySelected;
+			findSessionsMenuEntry.Selected += FindSessionsMenuEntrySelected;
+			backMenuEntry.Selected += OnCancel;
+
+			// Add entries to the menu.
+			MenuEntries.Add (createSessionMenuEntry);
+			MenuEntries.Add (findSessionsMenuEntry);
+			MenuEntries.Add (backMenuEntry);
+		}
+
+
+		/// <summary>
+		/// Helper chooses an appropriate menu title for the specified session type.
+		/// </summary>
+		static string GetMenuTitle (NetworkSessionType sessionType)
+		{
+			switch (sessionType) {
+			case NetworkSessionType.PlayerMatch:
+				return Resources.PlayerMatch;
+
+			case NetworkSessionType.SystemLink:
+				return Resources.SystemLink;
+
+			default:
+				throw new NotSupportedException ();
+			}
+		}
+
+
+	#endregion
+
+	#region Event Handlers
+
+
+		/// <summary>
+		/// Event handler for when the Create Session menu entry is selected.
+		/// </summary>
+		void CreateSessionMenuEntrySelected (object sender, PlayerIndexEventArgs e)
+		{
+			try			{
+				// Which local profiles should we include in this session?
+				IEnumerable<SignedInGamer> localGamers = 
+			NetworkSessionComponent.ChooseGamers (sessionType, 
+							ControllingPlayer.Value);
+
+				// Begin an asynchronous create network session operation.
+				IAsyncResult asyncResult = NetworkSession.BeginCreate (
+						sessionType, localGamers, 
+						NetworkSessionComponent.MaxGamers, 
+						0, null, null, null);
+
+				// Activate the network busy screen, which will display
+				// an animation until this operation has completed.
+				NetworkBusyScreen busyScreen = new NetworkBusyScreen (asyncResult);
+
+				busyScreen.OperationCompleted += CreateSessionOperationCompleted;
+
+				ScreenManager.AddScreen (busyScreen, ControllingPlayer);
+			} catch (Exception exception) {
+				NetworkErrorScreen errorScreen = new NetworkErrorScreen (exception);
+
+				ScreenManager.AddScreen (errorScreen, ControllingPlayer);
+			}
+		}
+
+		enum SessionProperty{ GameMode, SkillLevel, ScoreToWin }
+		
+		enum GameMode{ Practice, Timed, CaptureTheFlag }
+		
+		enum SkillLevel{ Beginner, Intermediate, Advanced }
+		
+		/// <summary>
+		/// Event handler for when the asynchronous create network session
+		/// operation has completed.
+		/// </summary>
+		void CreateSessionOperationCompleted (object sender,
+					OperationCompletedEventArgs e)
+		{
+			try {
+				// End the asynchronous create network session operation.
+				NetworkSession networkSession = NetworkSession.EndCreate (e.AsyncResult);
+				
+				// Create a component that will manage the session we just created.
+				NetworkSessionComponent.Create (ScreenManager, networkSession);
+
+				// Go to the lobby screen. We pass null as the controlling player,
+				// because the lobby screen accepts input from all local players
+				// who are in the session, not just a single controlling player.
+				ScreenManager.AddScreen (new LobbyScreen (networkSession), null);
+			} catch (Exception exception) {
+				NetworkErrorScreen errorScreen = new NetworkErrorScreen (exception);
+
+				ScreenManager.AddScreen (errorScreen, ControllingPlayer);
+			}
+		}
+
+
+		/// <summary>
+		/// Event handler for when the Find Sessions menu entry is selected.
+		/// </summary>
+		void FindSessionsMenuEntrySelected (object sender, PlayerIndexEventArgs e)
+		{
+			try			{
+				// Which local profiles should we include in this session?
+				IEnumerable<SignedInGamer> localGamers = 
+			NetworkSessionComponent.ChooseGamers (sessionType, 
+							ControllingPlayer.Value);
+
+				// Begin an asynchronous find network sessions operation.
+				IAsyncResult asyncResult = NetworkSession.BeginFind (sessionType, 
+							localGamers, null, null, null);
+
+				// Activate the network busy screen, which will display
+				// an animation until this operation has completed.
+				NetworkBusyScreen busyScreen = new NetworkBusyScreen (asyncResult);
+
+				busyScreen.OperationCompleted += FindSessionsOperationCompleted;
+
+				ScreenManager.AddScreen (busyScreen, ControllingPlayer);
+			} catch (Exception exception) {
+				NetworkErrorScreen errorScreen = new NetworkErrorScreen (exception);
+
+				ScreenManager.AddScreen (errorScreen, ControllingPlayer);
+			}
+		}
+
+
+		/// <summary>
+		/// Event handler for when the asynchronous find network sessions
+		/// operation has completed.
+		/// </summary>
+		void FindSessionsOperationCompleted (object sender,
+					OperationCompletedEventArgs e)
+		{
+			GameScreen nextScreen;
+
+			try			{
+				// End the asynchronous find network sessions operation.
+				AvailableNetworkSessionCollection availableSessions = 
+						NetworkSession.EndFind (e.AsyncResult);
+
+				if (availableSessions.Count == 0) {
+					// If we didn't find any sessions, display an error.
+					availableSessions.Dispose ();
+
+					nextScreen = new MessageBoxScreen (Resources.NoSessionsFound, false);
+				} else {
+					// If we did find some sessions, proceed to the JoinSessionScreen.
+					nextScreen = new JoinSessionScreen (availableSessions);
+				}
+			} catch (Exception exception) {
+				nextScreen = new NetworkErrorScreen (exception);
+			}
+
+			ScreenManager.AddScreen (nextScreen, ControllingPlayer);
+		}
+
+
+	#endregion
+	}
+}

+ 147 - 0
Samples/MacOS/CatapultNetWars/Networking/JoinSessionScreen.cs

@@ -0,0 +1,147 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// JoinSessionScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Net;
+using Microsoft.Xna.Framework.GamerServices;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// This menu screen displays a list of available network sessions,
+    /// and lets the user choose which one to join.
+    /// </summary>
+    class JoinSessionScreen : MenuScreen
+    {
+        #region Fields
+
+        const int MaxSearchResults = 8;
+
+        AvailableNetworkSessionCollection availableSessions;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a menu screen listing the available network sessions.
+        /// </summary>
+        public JoinSessionScreen(AvailableNetworkSessionCollection availableSessions)
+            : base(Resources.JoinSession)
+        {
+            this.availableSessions = availableSessions;
+
+            foreach (AvailableNetworkSession availableSession in availableSessions)
+            {
+                // Create menu entries for each available session.
+                MenuEntry menuEntry = new AvailableSessionMenuEntry(availableSession);
+                menuEntry.Selected += AvailableSessionMenuEntrySelected;
+                MenuEntries.Add(menuEntry);
+
+                // Matchmaking can return up to 25 available sessions at a time, but
+                // we don't have room to fit that many on the screen. In a perfect
+                // world we should make the menu scroll if there are too many, but it
+                // is easier to just not bother displaying more than we have room for.
+                if (MenuEntries.Count >= MaxSearchResults)
+                    break;
+            }
+
+            // Add the Back menu entry.
+            MenuEntry backMenuEntry = new MenuEntry(Resources.Back);
+            backMenuEntry.Selected += BackMenuEntrySelected;
+            MenuEntries.Add(backMenuEntry);
+        }
+
+
+        #endregion
+           
+        #region Event Handlers
+
+
+        /// <summary>
+        /// Event handler for when an available session menu entry is selected.
+        /// </summary>
+        void AvailableSessionMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            // Which menu entry was selected?
+            AvailableSessionMenuEntry menuEntry = (AvailableSessionMenuEntry)sender;
+            AvailableNetworkSession availableSession = menuEntry.AvailableSession;
+
+            try
+            {
+                // Begin an asynchronous join network session operation.
+                IAsyncResult asyncResult = NetworkSession.BeginJoin(availableSession,
+                                                                    null, null);
+
+                // Activate the network busy screen, which will display
+                // an animation until this operation has completed.
+                NetworkBusyScreen busyScreen = new NetworkBusyScreen(asyncResult);
+
+                busyScreen.OperationCompleted += JoinSessionOperationCompleted;
+
+                ScreenManager.AddScreen(busyScreen, ControllingPlayer);
+            }
+            catch (Exception exception)
+            {
+                NetworkErrorScreen errorScreen = new NetworkErrorScreen(exception);
+
+                ScreenManager.AddScreen(errorScreen, ControllingPlayer);
+            }
+        }
+
+
+        /// <summary>
+        /// Event handler for when the asynchronous join network session
+        /// operation has completed.
+        /// </summary>
+        void JoinSessionOperationCompleted(object sender, OperationCompletedEventArgs e)
+        {
+            try
+            {
+                // End the asynchronous join network session operation.
+                NetworkSession networkSession = NetworkSession.EndJoin(e.AsyncResult);
+
+                // Create a component that will manage the session we just joined.
+                NetworkSessionComponent.Create(ScreenManager, networkSession);
+
+                // Go to the lobby screen. We pass null as the controlling player,
+                // because the lobby screen accepts input from all local players
+                // who are in the session, not just a single controlling player.
+                ScreenManager.AddScreen(new LobbyScreen(networkSession), null);
+
+                availableSessions.Dispose();
+            }
+            catch (Exception exception)
+            {
+                NetworkErrorScreen errorScreen = new NetworkErrorScreen(exception);
+
+                ScreenManager.AddScreen(errorScreen, ControllingPlayer);
+            }
+        }
+
+
+        /// <summary>
+        /// Event handler for when the Back menu entry is selected.
+        /// </summary>
+        void BackMenuEntrySelected(object sender, PlayerIndexEventArgs e)
+        {
+            availableSessions.Dispose();
+
+            ExitScreen();
+        }
+
+
+        #endregion
+    }
+}

+ 277 - 0
Samples/MacOS/CatapultNetWars/Networking/LobbyScreen.cs

@@ -0,0 +1,277 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LobbyScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Net;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+	/// <summary>
+	/// The lobby screen provides a place for gamers to congregate before starting
+	/// the actual gameplay. It displays a list of all the gamers in the session,
+	/// and indicates which ones are currently talking. Each gamer can press a button
+	/// to mark themselves as ready: gameplay will begin after everyone has done this.
+	/// </summary>
+	class LobbyScreen : GameScreen
+	{
+	#region Fields
+
+		NetworkSession networkSession;
+		Texture2D isReadyTexture;
+		Texture2D hasVoiceTexture;
+		Texture2D isTalkingTexture;
+		Texture2D voiceMutedTexture;
+
+	#endregion
+
+	#region Initialization
+
+
+		/// <summary>
+		/// Constructs a new lobby screen.
+		/// </summary>
+		public LobbyScreen (NetworkSession networkSession)
+			{
+			this.networkSession = networkSession;
+
+			TransitionOnTime = TimeSpan.FromSeconds (0.5);
+			TransitionOffTime = TimeSpan.FromSeconds (0.5);
+			
+		}
+
+
+		/// <summary>
+		/// Loads graphics content used by the lobby screen.
+		/// </summary>
+		public override void LoadContent ()
+		{
+			ContentManager content = ScreenManager.Game.Content;
+
+			isReadyTexture = content.Load<Texture2D> ("chat_ready");
+			hasVoiceTexture = content.Load<Texture2D> ("chat_able");
+			isTalkingTexture = content.Load<Texture2D> ("chat_talking");
+			voiceMutedTexture = content.Load<Texture2D> ("chat_mute");
+		}
+
+
+	#endregion
+
+	#region Update
+
+
+		/// <summary>
+		/// Updates the lobby screen.
+		/// </summary>
+		public override void Update (GameTime gameTime, bool otherScreenHasFocus, 
+							bool coveredByOtherScreen)
+		{
+			base.Update (gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+			if (!IsExiting) {
+				if (networkSession.SessionState == NetworkSessionState.Playing) {
+					// Check if we should leave the lobby and begin gameplay.
+					// We pass null as the controlling player, because the networked
+					// gameplay screen accepts input from any local players who
+					// are in the session, not just a single controlling player.
+					LoadingScreen.Load (ScreenManager, true, null,
+					new InstructionsScreen());
+				} else if (networkSession.IsHost && networkSession.IsEveryoneReady) {
+					// The host checks whether everyone has marked themselves
+					// as ready, and starts the game in response.
+					networkSession.StartGame ();
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Handles user input for all the local gamers in the session. Unlike most
+		/// screens, which use the InputState class to combine input data from all
+		/// gamepads, the lobby needs to individually mark specific players as ready,
+		/// so it loops over all the local gamers and reads their inputs individually.
+		/// </summary>
+		public override void HandleInput (InputState input)
+		{
+			foreach (LocalNetworkGamer gamer in networkSession.LocalGamers) {
+				PlayerIndex playerIndex = gamer.SignedInGamer.PlayerIndex;
+
+				PlayerIndex unwantedOutput;
+
+				if (input.IsMenuSelect (playerIndex, out unwantedOutput)) {
+					HandleMenuSelect (gamer);
+				} else if (input.IsMenuCancel (playerIndex, out unwantedOutput)) {
+					HandleMenuCancel (gamer);
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Handle MenuSelect inputs by marking ourselves as ready.
+		/// </summary>
+		void HandleMenuSelect (LocalNetworkGamer gamer)
+		{
+			if (!gamer.IsReady) {
+				gamer.IsReady = true;
+			} else if (gamer.IsHost) {
+				// The host has an option to force starting the game, even if not
+				// everyone has marked themselves ready. If they press select twice
+				// in a row, the first time marks the host ready, then the second
+				// time we ask if they want to force start.
+				MessageBoxScreen messageBox = new MessageBoxScreen (
+						Resources.ConfirmForceStartGame);
+
+				messageBox.Accepted += ConfirmStartGameMessageBoxAccepted;
+
+				ScreenManager.AddScreen (messageBox, gamer.SignedInGamer.PlayerIndex);
+			}
+		}
+
+
+		/// <summary>
+		/// Event handler for when the host selects ok on the "are you sure
+		/// you want to start even though not everyone is ready" message box.
+		/// </summary>
+		void ConfirmStartGameMessageBoxAccepted (object sender, PlayerIndexEventArgs e)
+		{
+			if (networkSession.SessionState == NetworkSessionState.Lobby) {
+				networkSession.StartGame ();
+			}
+		}
+
+
+		/// <summary>
+		/// Handle MenuCancel inputs by clearing our ready status, or if it is
+		/// already clear, prompting if the user wants to leave the session.
+		/// </summary>
+		void HandleMenuCancel (LocalNetworkGamer gamer)
+		{
+			if (gamer.IsReady) {
+				gamer.IsReady = false;
+			} else {
+				PlayerIndex playerIndex = gamer.SignedInGamer.PlayerIndex;
+
+				NetworkSessionComponent.LeaveSession (ScreenManager, playerIndex);
+			}
+		}
+
+
+	#endregion
+
+	#region Draw
+
+
+		/// <summary>
+		/// Draws the lobby screen.
+		/// </summary>
+		public override void Draw (GameTime gameTime)
+		{
+			SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+			SpriteFont font = ScreenManager.Font;
+
+			Vector2 position = new Vector2 (100, 150);
+
+			// Make the lobby slide into place during transitions.
+			float transitionOffset = (float)Math.Pow (TransitionPosition, 2);
+
+			if (ScreenState == ScreenState.TransitionOn)
+				position.X -= transitionOffset * 256;
+			else
+				position.X += transitionOffset * 512;
+
+			spriteBatch.Begin ();
+
+			// Draw all the gamers in the session.
+			int gamerCount = 0;
+
+			foreach (NetworkGamer gamer in networkSession.AllGamers) {
+				DrawGamer (gamer, position);
+
+				// Advance to the next screen position, wrapping into two
+				// columns if there are more than 8 gamers in the session.
+				if (++gamerCount == 8) {
+					position.X += 433;
+					position.Y = 150;
+				} else
+					position.Y += ScreenManager.Font.LineSpacing;
+			}
+
+			// Draw the screen title.
+			string title = Resources.Lobby;
+
+			Vector2 titlePosition = new Vector2 (533, 80);
+			Vector2 titleOrigin = font.MeasureString (title) / 2;
+			Color titleColor = new Color (192, 192, 192) * TransitionAlpha;
+			float titleScale = 1.25f;
+
+			titlePosition.Y -= transitionOffset * 100;
+
+			spriteBatch.DrawString (font, title, titlePosition, titleColor, 0, 
+				titleOrigin, titleScale, SpriteEffects.None, 0);
+
+			spriteBatch.End ();
+		}
+
+
+		/// <summary>
+		/// Helper draws the gamertag and status icons for a single NetworkGamer.
+		/// </summary>
+		void DrawGamer (NetworkGamer gamer, Vector2 position)
+		{
+			SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+			SpriteFont font = ScreenManager.Font;
+
+			Vector2 iconWidth = new Vector2 (34, 0);
+			Vector2 iconOffset = new Vector2 (0, 12);
+
+			Vector2 iconPosition = position + iconOffset;
+
+			// Draw the "is ready" icon.
+			if (gamer.IsReady) {
+				spriteBatch.Draw (isReadyTexture, iconPosition, 
+				Color.Lime * TransitionAlpha);
+			}
+
+			iconPosition += iconWidth;
+
+			// Draw the "is muted", "is talking", or "has voice" icon.
+			if (gamer.IsMutedByLocalUser) {
+				spriteBatch.Draw (voiceMutedTexture, iconPosition, 
+				Color.Red * TransitionAlpha);
+			} else if (gamer.IsTalking) {
+				spriteBatch.Draw (isTalkingTexture, iconPosition, 
+				Color.Yellow * TransitionAlpha);
+			} else if (gamer.HasVoice) {
+				spriteBatch.Draw (hasVoiceTexture, iconPosition, 
+				Color.White * TransitionAlpha);
+			}
+
+			// Draw the gamertag, normally in white, but yellow for local players.
+			string text = gamer.Gamertag;
+
+			if (gamer.IsHost)
+				text += Resources.HostSuffix;
+
+			Color color = (gamer.IsLocal) ? Color.Yellow : Color.White;
+
+			spriteBatch.DrawString (font, text, position + iconWidth * 2, 
+				color * TransitionAlpha);
+		}
+
+
+	#endregion
+	}
+}

+ 169 - 0
Samples/MacOS/CatapultNetWars/Networking/NetworkBusyScreen.cs

@@ -0,0 +1,169 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// NetworkBusyScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+	/// <summary>
+	/// When an asynchronous network operation (for instance searching for or joining a
+	/// session) is in progress, we want to display some sort of busy indicator to let
+	/// the user know the game hasn't just locked up. We also want to make sure they
+	/// can't pick some other menu option before the current operation has finished.
+	/// This screen takes care of both requirements in a single stroke. It monitors
+	/// the IAsyncResult returned by an asynchronous network call, displaying a busy
+	/// indicator for as long as the call is still in progress. When it notices the
+	/// IAsyncResult has completed, it raises an event to let the game know it should
+	/// proceed to the next step, after which the busy screen automatically goes away.
+	/// Because this screen is on top of all others for as long as the asynchronous
+	/// operation is in progress, it automatically takes over all user input,
+	/// preventing any other menu entries being selected until the operation completes.
+	/// </summary>
+	class NetworkBusyScreen : GameScreen
+	{
+	#region Fields
+
+		IAsyncResult asyncResult;
+		Texture2D gradientTexture;
+		Texture2D catTexture;
+
+	#endregion
+
+	#region Events
+
+		public event EventHandler<OperationCompletedEventArgs> OperationCompleted;
+
+	#endregion
+
+	#region Initialization
+
+
+		/// <summary>
+		/// Constructs a network busy screen for the specified asynchronous operation.
+		/// </summary>
+		public NetworkBusyScreen (IAsyncResult asyncResult)
+			{
+			this.asyncResult = asyncResult;
+
+			IsPopup = true;
+
+			TransitionOnTime = TimeSpan.FromSeconds (0.1);
+			TransitionOffTime = TimeSpan.FromSeconds (0.2);
+		}
+
+
+		/// <summary>
+		/// Loads graphics content for this screen. This uses the shared ContentManager
+		/// provided by the Game class, so the content will remain loaded forever.
+		/// Whenever a subsequent NetworkBusyScreen tries to load this same content,
+		/// it will just get back another reference to the already loaded data.
+		/// </summary>
+		public override void LoadContent ()
+		{
+			ContentManager content = ScreenManager.Game.Content;
+
+			gradientTexture = content.Load<Texture2D> ("gradient");
+			catTexture = content.Load<Texture2D> ("cat");
+		}
+
+
+	#endregion
+
+	#region Update and Draw
+
+
+		/// <summary>
+		/// Updates the NetworkBusyScreen.
+		/// </summary>
+		public override void Update (GameTime gameTime, bool otherScreenHasFocus, 
+							bool coveredByOtherScreen)
+		{
+			base.Update (gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+			// Has our asynchronous operation completed?
+			if ((asyncResult != null) && asyncResult.IsCompleted) {
+				// If so, raise the OperationCompleted event.
+				if (OperationCompleted != null) {
+					OperationCompleted (this, 
+					new OperationCompletedEventArgs (asyncResult));
+				}
+
+				ExitScreen ();
+
+				asyncResult = null;
+			}
+		}
+
+
+		/// <summary>
+		/// Draws the NetworkBusyScreen.
+		/// </summary>
+		public override void Draw (GameTime gameTime)
+		{
+			SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+			SpriteFont font = ScreenManager.Font;
+
+			string message = Resources.NetworkBusy;
+
+			const int hPad = 32 ; 
+			const int vPad = 16 ; 
+
+			// Center the message text in the viewport.
+			Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+			Vector2 viewportSize = new Vector2 (viewport.Width, viewport.Height);
+			Vector2 textSize = font.MeasureString (message);
+
+			// Add enough room to spin a cat.
+			Vector2 catSize = new Vector2 (catTexture.Width);
+
+			textSize.X = Math.Max (textSize.X, catSize.X);
+			textSize.Y += catSize.Y + vPad;
+
+			Vector2 textPosition = (viewportSize - textSize) / 2;
+
+			// The background includes a border somewhat larger than the text itself.
+			Rectangle backgroundRectangle = new Rectangle ((int)textPosition.X - hPad,
+							(int)textPosition.Y - vPad,
+							(int)textSize.X + hPad * 2,
+							(int)textSize.Y + vPad * 2);
+
+			// Fade the popup alpha during transitions.
+			Color color = Color.White * TransitionAlpha;
+
+			spriteBatch.Begin ();
+
+			// Draw the background rectangle.
+			spriteBatch.Draw (gradientTexture, backgroundRectangle, color);
+
+			// Draw the message box text.
+			spriteBatch.DrawString (font, message, textPosition, color);
+
+			// Draw the spinning cat progress indicator.
+			float catRotation = (float)gameTime.TotalGameTime.TotalSeconds * 3;
+
+			Vector2 catPosition = new Vector2 (textPosition.X + textSize.X / 2,
+						textPosition.Y + textSize.Y - 
+								catSize.Y / 2);
+
+			spriteBatch.Draw (catTexture, catPosition, null, color, catRotation, 
+				catSize / 2, 1, SpriteEffects.None, 0);
+
+			spriteBatch.End ();
+		}
+
+
+	#endregion
+	}
+}

+ 90 - 0
Samples/MacOS/CatapultNetWars/Networking/NetworkErrorScreen.cs

@@ -0,0 +1,90 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// NetworkErrorScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Diagnostics;
+using Microsoft.Xna.Framework.GamerServices;
+using Microsoft.Xna.Framework.Net;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Specialized message box subclass, used to display network error messages.
+    /// </summary>
+    class NetworkErrorScreen : MessageBoxScreen
+    {
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs an error message box from the specified exception.
+        /// </summary>
+        public NetworkErrorScreen(Exception exception)
+            : base(GetErrorMessage(exception), false)
+        { }
+
+
+        /// <summary>
+        /// Converts a network exception into a user friendly error message.
+        /// </summary>
+        static string GetErrorMessage(Exception exception)
+        {
+            Debug.WriteLine("Network operation threw " + exception);
+
+            // Is this a GamerPrivilegeException?
+            if (exception is GamerPrivilegeException)
+            {
+                if (Guide.IsTrialMode)
+                    return Resources.ErrorTrialMode;
+                else
+                    return Resources.ErrorGamerPrivilege;
+            }
+
+            // Is it a NetworkSessionJoinException?
+            NetworkSessionJoinException joinException = exception as
+                                                            NetworkSessionJoinException;
+
+            if (joinException != null)
+            {
+                switch (joinException.JoinError)
+                {
+                    case NetworkSessionJoinError.SessionFull:
+                        return Resources.ErrorSessionFull;
+
+                    case NetworkSessionJoinError.SessionNotFound:
+                        return Resources.ErrorSessionNotFound;
+
+                    case NetworkSessionJoinError.SessionNotJoinable:
+                        return Resources.ErrorSessionNotJoinable;
+                }
+            }
+
+            // Is this a NetworkNotAvailableException?
+            if (exception is NetworkNotAvailableException)
+            {
+                return Resources.ErrorNetworkNotAvailable;
+            }
+
+            // Is this a NetworkException?
+            if (exception is NetworkException)
+            {
+                return Resources.ErrorNetwork;
+            }
+
+            // Otherwise just a generic error message.
+            return Resources.ErrorUnknown;
+        }
+
+
+        #endregion
+    }
+}

+ 481 - 0
Samples/MacOS/CatapultNetWars/Networking/NetworkSessionComponent.cs

@@ -0,0 +1,481 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// NetworkSessionComponent.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Linq;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Net;
+using Microsoft.Xna.Framework.GamerServices;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+	/// <summary>
+	/// Component in charge of owning and updating the current NetworkSession object.
+	/// This is responsible for calling NetworkSession.Update at regular intervals,
+	/// and also exposes the NetworkSession as a game service which can easily be
+	/// looked up by any other code that needs to access it.
+	/// </summary>
+	class NetworkSessionComponent : GameComponent
+	{
+	#region Fields
+
+		public const int MaxGamers = 16;
+		public const int MaxLocalGamers = 4;
+		ScreenManager screenManager;
+		NetworkSession networkSession;
+		IMessageDisplay messageDisplay;
+		bool notifyWhenPlayersJoinOrLeave;
+		string sessionEndMessage;
+
+	#endregion
+
+	#region Initialization
+
+
+		/// <summary>
+		/// The constructor is private: external callers should use the Create method.
+		/// </summary>
+		NetworkSessionComponent (ScreenManager screenManager, 
+				NetworkSession networkSession)
+		: base(screenManager.Game)
+			{
+			this.screenManager = screenManager;
+			this.networkSession = networkSession;
+
+			// Hook up our session event handlers.
+			networkSession.GamerJoined += GamerJoined;
+			networkSession.GamerLeft += GamerLeft;
+			networkSession.SessionEnded += NetworkSessionEnded;
+		}
+
+
+		/// <summary>
+		/// Creates a new NetworkSessionComponent.
+		/// </summary>
+		public static void Create (ScreenManager screenManager,
+				NetworkSession networkSession)
+		{
+			Game game = screenManager.Game;
+
+			// Register this network session as a service.
+			game.Services.AddService (typeof(NetworkSession), networkSession);
+
+			// Create a NetworkSessionComponent, and add it to the Game.
+			game.Components.Add (new NetworkSessionComponent (screenManager,
+							networkSession));
+		}
+
+
+		/// <summary>
+		/// Initializes the component.
+		/// </summary>
+		public override void Initialize ()
+		{
+			base.Initialize ();
+
+			// Look up the IMessageDisplay service, which will
+			// be used to report gamer join/leave notifications.
+			messageDisplay = (IMessageDisplay)Game.Services.GetService (
+							typeof(IMessageDisplay));
+
+			if (messageDisplay != null)
+				notifyWhenPlayersJoinOrLeave = true;
+		}
+
+
+		/// <summary>
+		/// Shuts down the component.
+		/// </summary>
+		protected override void Dispose (bool disposing)
+		{
+			if (disposing) {
+				// Remove the NetworkSessionComponent.
+				Game.Components.Remove (this);
+
+				// Remove the NetworkSession service.
+				Game.Services.RemoveService (typeof(NetworkSession));
+
+				// Dispose the NetworkSession.
+				if (networkSession != null) {
+					networkSession.Dispose ();
+					networkSession = null;
+				}
+			}
+
+			base.Dispose (disposing);
+		}
+
+
+	#endregion
+
+	#region Update
+
+
+		/// <summary>
+		/// Updates the network session.
+		/// </summary>
+		public override void Update (GameTime gameTime)
+		{
+			if (networkSession == null)
+				return;
+
+			try			{
+				networkSession.Update ();
+
+				// Has the session ended?
+				if (networkSession.SessionState == NetworkSessionState.Ended) {
+					LeaveSession ();
+				}
+			} catch (Exception exception) {
+				// Handle any errors from the network session update.
+				Debug.WriteLine ("NetworkSession.Update threw " + exception);
+
+				sessionEndMessage = Resources.ErrorNetwork;
+
+				LeaveSession ();
+			}
+		}
+
+
+	#endregion
+
+	#region Event Handlers
+
+
+		/// <summary>
+		/// Event handler called when a gamer joins the session.
+		/// Displays a notification message.
+		/// </summary>
+		void GamerJoined (object sender, GamerJoinedEventArgs e)
+		{
+			if (notifyWhenPlayersJoinOrLeave) {
+				messageDisplay.ShowMessage (Resources.MessageGamerJoined, 
+					e.Gamer.Gamertag);
+			}
+		}
+
+
+		/// <summary>
+		/// Event handler called when a gamer leaves the session.
+		/// Displays a notification message.
+		/// </summary>
+		void GamerLeft (object sender, GamerLeftEventArgs e)
+		{
+			if (notifyWhenPlayersJoinOrLeave) {
+				messageDisplay.ShowMessage (Resources.MessageGamerLeft, 
+					e.Gamer.Gamertag);
+			}
+		}
+
+
+		/// <summary>
+		/// Event handler called when the network session ends.
+		/// Stores the end reason, so this can later be displayed to the user.
+		/// </summary>
+		void NetworkSessionEnded (object sender, NetworkSessionEndedEventArgs e)
+		{
+			switch (e.EndReason) {
+			case NetworkSessionEndReason.ClientSignedOut:
+				sessionEndMessage = null;
+				break;
+
+			case NetworkSessionEndReason.HostEndedSession:
+				sessionEndMessage = Resources.ErrorHostEndedSession;
+				break;
+
+			case NetworkSessionEndReason.RemovedByHost:
+				sessionEndMessage = Resources.ErrorRemovedByHost;
+				break;
+
+			case NetworkSessionEndReason.Disconnected:
+			default:
+				sessionEndMessage = Resources.ErrorDisconnected;
+				break;
+			}
+
+			notifyWhenPlayersJoinOrLeave = false;
+		}
+
+
+		/// <summary>
+		/// Event handler called when the system delivers an invite notification.
+		/// This can occur when the user accepts an invite that was sent to them by
+		/// a friend (pull mode), or if they choose the "Join Session In Progress"
+		/// option in their friends screen (push mode). The handler leaves the
+		/// current session (if any), then joins the session referred to by the
+		/// invite. It is not necessary to prompt the user before doing this, as
+		/// the Guide will already have taken care of the necessary confirmations
+		/// before the invite was delivered to you.
+		/// </summary>
+		public static void InviteAccepted (ScreenManager screenManager,
+					InviteAcceptedEventArgs e)
+		{
+			// If we are already in a network session, leave it now.
+			NetworkSessionComponent self = FindSessionComponent (screenManager.Game);
+
+			if (self != null)
+				self.Dispose ();
+
+			try			{
+				// Which local profiles should we include in this session?
+				IEnumerable<SignedInGamer> localGamers = 
+			ChooseGamers (NetworkSessionType.PlayerMatch, e.Gamer.PlayerIndex);
+
+				// Begin an asynchronous join-from-invite operation.
+				IAsyncResult asyncResult = NetworkSession.BeginJoinInvited (localGamers, 
+									null, null);
+
+				// Use the loading screen to replace whatever screens were previously
+				// active. This will completely reset the screen state, regardless of
+				// whether we were in the menus or playing a game when the invite was
+				// delivered. When the loading screen finishes, it will activate the
+				// network busy screen, which displays an animation as it waits for
+				// the join operation to complete.
+				NetworkBusyScreen busyScreen = new NetworkBusyScreen (asyncResult);
+
+				busyScreen.OperationCompleted += JoinInvitedOperationCompleted;
+
+				LoadingScreen.Load (screenManager, false, null, new BackgroundScreen (), 
+							busyScreen);
+			} catch (Exception exception) {
+				NetworkErrorScreen errorScreen = new NetworkErrorScreen (exception);
+
+				LoadingScreen.Load (screenManager, false, null, new BackgroundScreen (), 
+							new MainMenuScreen (), 
+							errorScreen);
+			}
+		}
+
+
+		/// <summary>
+		/// Event handler for when the asynchronous join-from-invite
+		/// operation has completed.
+		/// </summary>
+		static void JoinInvitedOperationCompleted (object sender,
+						OperationCompletedEventArgs e)
+		{
+			ScreenManager screenManager = ((GameScreen)sender).ScreenManager;
+
+			try			{
+				// End the asynchronous join-from-invite operation.
+				NetworkSession networkSession = 
+					NetworkSession.EndJoinInvited (e.AsyncResult);
+
+				// Create a component that will manage the session we just created.
+				NetworkSessionComponent.Create (screenManager, networkSession);
+
+				// Go to the lobby screen.
+				screenManager.AddScreen (new LobbyScreen (networkSession), null);
+			} catch (Exception exception) {
+				screenManager.AddScreen (new MainMenuScreen (), null);
+				screenManager.AddScreen (new NetworkErrorScreen (exception), null);
+			}
+		}
+
+
+	#endregion
+
+	#region Methods
+
+
+		/// <summary>
+		/// Checks whether the specified session type is online.
+		/// Online sessions cannot be used by local profiles, or if
+		/// parental controls are enabled, or when running in trial mode.
+		/// </summary>
+		public static bool IsOnlineSessionType (NetworkSessionType sessionType)
+		{
+			switch (sessionType) {
+			case NetworkSessionType.Local:
+			case NetworkSessionType.SystemLink:
+				return false;
+
+			case NetworkSessionType.PlayerMatch:
+			//case NetworkSessionType.Ranked:
+				return true;
+
+			default:
+				throw new NotSupportedException ();
+			}
+		}
+
+
+		/// <summary>
+		/// Decides which local gamer profiles should be included in a network session.
+		/// This is passed the index of the primary gamer (the profile who selected the
+		/// relevant menu option, or who is responding to an invite). The primary gamer
+		/// will always be included in the session. Other gamers may also be added if
+		/// there are suitable profiles signed in. To control how many gamers can be
+		/// returned by this method, adjust the MaxLocalGamers constant.
+		/// </summary>
+		public static IEnumerable<SignedInGamer> ChooseGamers (
+							NetworkSessionType sessionType,
+							PlayerIndex playerIndex)
+		{
+			List<SignedInGamer> gamers = new List<SignedInGamer> ();
+
+			// Look up the primary gamer, and make sure they are signed in.
+			SignedInGamer primaryGamer = Gamer.SignedInGamers [playerIndex];
+
+			if (primaryGamer == null)
+				throw new GamerPrivilegeException ();
+
+			gamers.Add (primaryGamer);
+
+			// Check whether any other profiles should also be included.
+			foreach (SignedInGamer gamer in Gamer.SignedInGamers) {
+				// Never include more profiles than the MaxLocalGamers constant.
+				if (gamers.Count >= MaxLocalGamers)
+					break;
+
+				// Don't want two copies of the primary gamer!
+				if (gamer == primaryGamer)
+					continue;
+
+				// If this is an online session, make sure the profile is signed
+				// in to Live, and that it has the privilege for online gameplay.
+				if (IsOnlineSessionType (sessionType)) {
+					if (!gamer.IsSignedInToLive)
+						continue;
+
+					if (!gamer.Privileges.AllowOnlineSessions)
+						continue;
+				}
+
+				if (primaryGamer.IsGuest && !gamer.IsGuest && gamers [0] == primaryGamer) {
+					// Special case: if the primary gamer is a guest profile,
+					// we should insert some other non-guest at the start of the
+					// output list, because guests aren't allowed to host sessions.
+					gamers.Insert (0, gamer);
+				} else {
+					gamers.Add (gamer);
+				}
+			}
+
+			return gamers;
+		}
+
+
+		/// <summary>
+		/// Public method called when the user wants to leave the network session.
+		/// Displays a confirmation message box, then disposes the session, removes
+		/// the NetworkSessionComponent, and returns them to the main menu screen.
+		/// </summary>
+		public static void LeaveSession (ScreenManager screenManager,
+					PlayerIndex playerIndex)
+		{
+			NetworkSessionComponent self = FindSessionComponent (screenManager.Game);
+
+			if (self != null) {
+				// Display a message box to confirm the user really wants to leave.
+				string message;
+
+				if (self.networkSession.IsHost)
+					message = Resources.ConfirmEndSession;
+				else
+					message = Resources.ConfirmLeaveSession;
+
+				MessageBoxScreen confirmMessageBox = new MessageBoxScreen (message);
+
+				// Hook the messge box ok event to actually leave the session.
+				confirmMessageBox.Accepted += delegate
+		{
+			self.LeaveSession ();
+		};
+
+				screenManager.AddScreen (confirmMessageBox, playerIndex);
+			}
+		}
+
+
+		/// <summary>
+		/// Internal method for leaving the network session. This disposes the 
+		/// session, removes the NetworkSessionComponent, and returns the user
+		/// to the main menu screen.
+		/// </summary>
+		void LeaveSession ()
+		{
+			// Destroy this NetworkSessionComponent.
+			Dispose ();
+
+			// If we have a sessionEndMessage string explaining why the session has
+			// ended (maybe this was a network disconnect, or perhaps the host kicked
+			// us out?) create a message box to display this reason to the user.
+			MessageBoxScreen messageBox;
+
+			if (!string.IsNullOrEmpty (sessionEndMessage))
+				messageBox = new MessageBoxScreen (sessionEndMessage, false);
+			else
+				messageBox = null;
+
+			// At this point we want to return the user all the way to the main
+			// menu screen. But what if they just joined a session? In that case
+			// they went through this flow of screens:
+			//
+			//  - MainMenuScreen
+			//  - CreateOrFindSessionsScreen
+			//  - JoinSessionScreen (if joining, skipped if creating a new session)
+			//  - LobbyScreeen
+			//
+			// If we have these previous screens on the history stack, and the user
+			// backs out of the LobbyScreen, the right thing is just to pop off the
+			// intermediate screens, returning them to the existing MainMenuScreen
+			// instance without bothering to reload everything. But if the user is
+			// in gameplay, or has been in gameplay and then returned to the lobby,
+			// the screen stack will have been emptied.
+			//
+			// To do the right thing in both cases, we scan through the screen history
+			// stack looking for a MainMenuScreen. If we find one, we pop any
+			// subsequent screens so as to return back to it, while if we don't
+			// find it, we just reset everything via the LoadingScreen.
+
+			GameScreen[] screens = screenManager.GetScreens ();
+
+			// Look for the MainMenuScreen.
+			for (int i = 0; i < screens.Length; i++) {
+				if (screens [i] is MainMenuScreen) {
+					// If we found one, pop everything since then to return back to it.
+					for (int j = i + 1; j < screens.Length; j++)
+						screens [j].ExitScreen ();
+
+					// Display the why-did-the-session-end message box.
+					if (messageBox != null)
+						screenManager.AddScreen (messageBox, null);
+
+					return;
+				}
+			}
+
+			// If we didn't find an existing MainMenuScreen, reload everything.
+			// The why-did-the-session-end message box will be displayed after
+			// the loading screen has completed.
+			LoadingScreen.Load (screenManager, false, null, new BackgroundScreen (), 
+							new MainMenuScreen (), 
+							messageBox);
+		}
+
+
+		/// <summary>
+		/// Searches through the Game.Components collection to
+		/// find the NetworkSessionComponent (if any exists).
+		/// </summary>
+		static NetworkSessionComponent FindSessionComponent (Game game)
+		{
+			return game.Components.OfType<NetworkSessionComponent> ().FirstOrDefault ();
+		}
+
+
+	#endregion
+	}
+}

+ 53 - 0
Samples/MacOS/CatapultNetWars/Networking/OperationCompletedEventArgs.cs

@@ -0,0 +1,53 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// OperationCompletedEventArgs.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Custom EventArgs class used by the NetworkBusyScreen.OperationCompleted event.
+    /// </summary>
+    class OperationCompletedEventArgs : EventArgs
+    {
+        #region Properties
+
+
+        /// <summary>
+        /// Gets or sets the IAsyncResult associated with
+        /// the network operation that has just completed.
+        /// </summary>
+        public IAsyncResult AsyncResult
+        {
+            get { return asyncResult; }
+            set { asyncResult = value; }
+        }
+
+        IAsyncResult asyncResult;
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new event arguments class.
+        /// </summary>
+        public OperationCompletedEventArgs(IAsyncResult asyncResult)
+        {
+            this.asyncResult = asyncResult;
+        }
+
+
+        #endregion
+    }
+}

+ 188 - 0
Samples/MacOS/CatapultNetWars/Networking/ProfileSignInScreen.cs

@@ -0,0 +1,188 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ProfileSignInScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Net;
+using Microsoft.Xna.Framework.GamerServices;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// In order to play a networked game, you must have a player profile signed in.
+    /// If you want to play on Live, that has to be a Live profile. Rather than just
+    /// failing with an error message, it is nice if we can automatically bring up the
+    /// Guide screen when we detect that no suitable profiles are currently signed in,
+    /// so the user can easily correct the problem. This screen checks the sign in
+    /// state, and brings up the Guide user interface if there is a problem with it.
+    /// It then raises an event as soon as a valid profile has been signed in.
+    /// 
+    /// There are two scenarios for how this can work. If no good profile is signed in:
+    /// 
+    ///     - MainMenuScreen activates the ProfileSignInScreen
+    ///     - ProfileSignInScreen activates the Guide user interface
+    ///     - User signs in a profile
+    ///     - ProfileSignInScreen raises the ProfileSignedIn event
+    ///     - This advances to the CreateOrFindSessionScreen
+    /// 
+    /// Alternatively, there might already be a valid profile signed in. In this case:
+    /// 
+    ///     - MainMenuScreen activates the ProfileSignInScreen
+    ///     - ProfileSignInScreen notices everything is already good
+    ///     - ProfileSignInScreen raises the ProfileSignedIn event
+    ///     - This advances to the CreateOrFindSessionScreen
+    /// 
+    /// In this second case, the ProfileSignInScreen is only active for a single
+    /// Update, so the user just sees a transition directly from the MainMenuScreen
+    /// to the CreateOrFindSessionScreen.
+    /// </summary>
+    class ProfileSignInScreen : GameScreen
+    {
+        #region Fields
+
+        NetworkSessionType sessionType;
+        bool haveShownGuide;
+        bool haveShownMarketplace;
+
+        #endregion
+
+        #region Events
+
+        public event EventHandler<EventArgs> ProfileSignedIn;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new profile sign in screen.
+        /// </summary>
+        public ProfileSignInScreen(NetworkSessionType sessionType)
+        {
+            this.sessionType = sessionType;
+
+            IsPopup = true;
+        }
+
+
+        #endregion
+
+        #region Update
+
+
+        /// <summary>
+        /// Updates the profile sign in screen.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+            if (ValidProfileSignedIn())
+            {
+                // As soon as we detect a suitable profile is signed in,
+                // we raise the profile signed in event, then go away.
+                if (ProfileSignedIn != null)
+                    ProfileSignedIn(this, EventArgs.Empty);
+
+                ExitScreen();
+            }
+            else if (IsActive && !Guide.IsVisible)
+            {
+                // If we are in trial mode, and they want to play online, and a profile
+                // is signed in, take them to marketplace so they can purchase the game.
+                if ((Guide.IsTrialMode) &&
+                    (NetworkSessionComponent.IsOnlineSessionType(sessionType)) &&
+                    (Gamer.SignedInGamers[ControllingPlayer.Value] != null) &&
+                    (!haveShownMarketplace))
+                {
+                    ShowMarketplace();
+
+                    haveShownMarketplace = true;
+                }
+                else if (!haveShownGuide && !haveShownMarketplace)
+                {
+                    // No suitable profile is signed in, and we haven't already shown
+                    // the Guide. Let's show it now, so they can sign in a profile.
+                    Guide.ShowSignIn(1,
+                            NetworkSessionComponent.IsOnlineSessionType(sessionType));
+
+                    haveShownGuide = true;
+                }
+                else
+                {
+                    // Hmm. No suitable profile is signed in, but we already showed
+                    // the Guide, and the Guide isn't still visible. There is only
+                    // one thing that can explain this: they must have cancelled the
+                    // Guide without signing in a profile. We'd better just exit,
+                    // which will leave us on the same menu as before.
+                    ExitScreen();
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Helper checks whether a valid player profile is signed in.
+        /// </summary>
+        bool ValidProfileSignedIn()
+        {
+            // If there is no profile signed in, that is never good.
+            SignedInGamer gamer = Gamer.SignedInGamers[ControllingPlayer.Value];
+
+            if (gamer == null)
+                return false;
+
+            // If we want to play in a Live session, also make sure the profile is
+            // signed in to Live, and that it has the privilege for online gameplay.
+            if (NetworkSessionComponent.IsOnlineSessionType(sessionType))
+            {
+                if (!gamer.IsSignedInToLive)
+                    return false;
+
+                if (!gamer.Privileges.AllowOnlineSessions)
+                    return false;
+            }
+
+            // Okeydokey, this looks good.
+            return true;
+        }
+
+
+        /// <summary>
+        /// LIVE networking is not supported in trial mode. Rather than just giving
+        /// the user an error, this function asks if they want to purchase the full
+        /// game, then takes them to Marketplace where they can do that. Once the
+        /// Guide is active, the user can either make the purchase, or cancel it.
+        /// When the Guide closes, ProfileSignInScreen.Update will notice that
+        /// Guide.IsVisible has gone back to false, at which point it will check if
+        /// the game is still in trial mode, and either exit the screen or proceed
+        /// forward accordingly.
+        /// </summary>
+        void ShowMarketplace()
+        {
+            MessageBoxScreen confirmMarketplaceMessageBox =
+                                    new MessageBoxScreen(Resources.ConfirmMarketplace);
+
+            confirmMarketplaceMessageBox.Accepted += delegate
+            {
+                Guide.ShowMarketplace(ControllingPlayer.Value);
+            };
+
+            ScreenManager.AddScreen(confirmMarketplaceMessageBox, ControllingPlayer);
+        }
+
+
+        #endregion
+    }
+}

+ 79 - 0
Samples/MacOS/CatapultNetWars/Players/AI.cs

@@ -0,0 +1,79 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// AI.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region File Information
+//-----------------------------------------------------------------------------
+// AI.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 System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace CatapultGame
+{
+    class AI : Human
+    {
+        #region Fields
+        Random random;
+        #endregion
+
+        #region Initialization
+        public AI(Game game)
+            : base(game)
+        {
+        }
+
+        public AI(Game game, SpriteBatch screenSpriteBatch)
+            : base(game, screenSpriteBatch, PlayerSide.Right)
+        {
+            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();
+			IsAI = true;
+            //Catapult.Initialize();
+
+            base.Initialize();
+        }
+        #endregion
+
+        #region Update
+        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);
+        }
+        #endregion
+    }
+}

+ 222 - 0
Samples/MacOS/CatapultNetWars/Players/Human.cs

@@ -0,0 +1,222 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Human.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region File Information
+//-----------------------------------------------------------------------------
+// Human.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 System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input.Touch;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+	enum PlayerSide
+	{
+		Left,
+		Right
+	}
+
+	class Human : Player
+	{
+#region Fields/Constants
+		// Drag variables to hold first and last gesture samples
+		GestureSample? prevSample;
+		GestureSample? firstSample;
+		public bool IsAI { get; protected set; }
+		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;
+		PlayerSide playerSide;
+		SpriteEffects spriteEffect = SpriteEffects.None;
+#endregion
+
+#region Initialization
+		public Human (Game game) : base(game)
+		{
+		}
+
+		public Human (Game game, SpriteBatch screenSpriteBatch, PlayerSide playerSide) : base(game, screenSpriteBatch)
+		{
+			string idleTextureName = "";
+			this.playerSide = playerSide;
+
+			if (playerSide == PlayerSide.Left) {
+				catapultPosition = new Vector2 (140, 332);
+				idleTextureName = "Textures/Catapults/Blue/blueIdle/blueIdle";
+			} else {
+				catapultPosition = new Vector2 (600, 332);
+				spriteEffect = SpriteEffects.FlipHorizontally;
+				idleTextureName = "Textures/Catapults/Red/redIdle/redIdle";
+			}
+
+			Catapult = new Catapult (game, screenSpriteBatch,
+						idleTextureName, catapultPosition, spriteEffect,
+						playerSide == PlayerSide.Left ? false : true);
+		}
+
+		public override void Initialize ()
+		{
+			arrow = curGame.Content.Load<Texture2D> ("Textures/HUD/arrow");
+
+			Catapult.Initialize ();
+
+			base.Initialize ();
+		}
+#endregion
+
+#region Handle Input
+		/// <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 && !IsAI) {
+
+				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 ();
+				}
+			}
+		}
+
+#endregion
+
+#region Draw
+		public override void Draw (GameTime gameTime)
+		{
+			if (isDragging)
+				DrawDragArrow (arrowScale);
+
+			base.Draw (gameTime);
+		}
+
+		public void DrawDragArrow (float arrowScale)
+		{
+			if (playerSide == PlayerSide.Left) {
+				spriteBatch.Draw (arrow, catapultPosition + new Vector2 (0, -40),
+						null, Color.Blue, 0,
+						Vector2.Zero, new Vector2 (arrowScale, 0.1f), spriteEffect, 0);
+			} else {
+				spriteBatch.Draw (arrow, catapultPosition + new Vector2 (-arrow.Width * arrowScale + 40, -40),
+						null, Color.Red, 0,
+						Vector2.Zero, new Vector2 (arrowScale, 0.1f), spriteEffect, 0);
+			}
+		}
+#endregion
+
+		// used to set the arrow scale from networking
+		internal float ArrowScale
+		{
+			get { return arrowScale; }
+			set { arrowScale = value; }
+		}
+		/// <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;
+		}
+	}
+}

+ 94 - 0
Samples/MacOS/CatapultNetWars/Players/Player.cs

@@ -0,0 +1,94 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Player.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region File Information
+//-----------------------------------------------------------------------------
+// Player.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 System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace CatapultGame
+{
+    internal class Player : DrawableGameComponent
+    {
+        #region Variables/Fields
+        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; }
+        #endregion
+
+        #region Initialization
+        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();
+        }
+        #endregion
+
+        #region Update and Render
+        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);
+        }
+        #endregion
+    }
+}

+ 67 - 0
Samples/MacOS/CatapultNetWars/Program.cs

@@ -0,0 +1,67 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Program.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+#endregion
+
+namespace CatapultGame
+{
+#if WINDOWS || XBOX
+    static class Program
+    {
+        /// <summary>
+        /// The main entry point for the application.
+        /// </summary>
+        static void Main(string[] args)
+        {
+            using (CatapultGame game = new CatapultGame())
+            {
+                game.Run();
+            }
+        }
+    }
+#elif MACOS
+
+
+	static class Program
+	{	
+		/// <summary>
+		/// The main entry point for the application.
+		/// </summary>
+		static void Main (string[] args)
+		{
+			MonoMac.AppKit.NSApplication.Init ();
+			
+			using (var p = new MonoMac.Foundation.NSAutoreleasePool ()) {
+				MonoMac.AppKit.NSApplication.SharedApplication.Delegate = new AppDelegate();
+				MonoMac.AppKit.NSApplication.Main(args);
+			}
+		}
+	}
+	
+	class AppDelegate : MonoMac.AppKit.NSApplicationDelegate
+	{
+		
+		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification)
+		{
+			using (CatapultGame game = new CatapultGame()) {
+				game.Run ();
+			}
+		}
+		
+		public override bool ApplicationShouldTerminateAfterLastWindowClosed (MonoMac.AppKit.NSApplication sender)
+		{
+			return true;
+		}
+	}
+#endif
+
+}
+

+ 433 - 0
Samples/MacOS/CatapultNetWars/Resources.Designer.cs

@@ -0,0 +1,433 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.1
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace CatapultGame {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CatapultGame.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Back.
+        /// </summary>
+        internal static string Back {
+            get {
+                return ResourceManager.GetString("Back", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Are you sure you want to end this session?.
+        /// </summary>
+        internal static string ConfirmEndSession {
+            get {
+                return ResourceManager.GetString("ConfirmEndSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Are you sure you want to exit this sample?.
+        /// </summary>
+        internal static string ConfirmExitSample {
+            get {
+                return ResourceManager.GetString("ConfirmExitSample", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Are you sure you want to start the game,
+        ///even though not all players are ready?.
+        /// </summary>
+        internal static string ConfirmForceStartGame {
+            get {
+                return ResourceManager.GetString("ConfirmForceStartGame", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Are you sure you want to leave this session?.
+        /// </summary>
+        internal static string ConfirmLeaveSession {
+            get {
+                return ResourceManager.GetString("ConfirmLeaveSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Online gameplay is not available in trial mode.
+        ///Would you like to purchase this game?.
+        /// </summary>
+        internal static string ConfirmMarketplace {
+            get {
+                return ResourceManager.GetString("ConfirmMarketplace", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Are you sure you want to quit this game?.
+        /// </summary>
+        internal static string ConfirmQuitGame {
+            get {
+                return ResourceManager.GetString("ConfirmQuitGame", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Create Session.
+        /// </summary>
+        internal static string CreateSession {
+            get {
+                return ResourceManager.GetString("CreateSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to End Session.
+        /// </summary>
+        internal static string EndSession {
+            get {
+                return ResourceManager.GetString("EndSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Lost connection to the network session.
+        /// </summary>
+        internal static string ErrorDisconnected {
+            get {
+                return ResourceManager.GetString("ErrorDisconnected", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to You must sign in a suitable gamer profile
+        ///in order to access this functionality.
+        /// </summary>
+        internal static string ErrorGamerPrivilege {
+            get {
+                return ResourceManager.GetString("ErrorGamerPrivilege", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Host ended the session.
+        /// </summary>
+        internal static string ErrorHostEndedSession {
+            get {
+                return ResourceManager.GetString("ErrorHostEndedSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to There was an error while
+        ///accessing the network.
+        /// </summary>
+        internal static string ErrorNetwork {
+            get {
+                return ResourceManager.GetString("ErrorNetwork", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Networking is turned
+        ///off or not connected.
+        /// </summary>
+        internal static string ErrorNetworkNotAvailable {
+            get {
+                return ResourceManager.GetString("ErrorNetworkNotAvailable", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Host kicked you out of the session.
+        /// </summary>
+        internal static string ErrorRemovedByHost {
+            get {
+                return ResourceManager.GetString("ErrorRemovedByHost", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to This session is already full.
+        /// </summary>
+        internal static string ErrorSessionFull {
+            get {
+                return ResourceManager.GetString("ErrorSessionFull", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Session not found. It may have ended,
+        ///or there may be no network connectivity
+        ///between the local machine and session host.
+        /// </summary>
+        internal static string ErrorSessionNotFound {
+            get {
+                return ResourceManager.GetString("ErrorSessionNotFound", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to You must wait for the host to return to
+        ///the lobby before you can join this session.
+        /// </summary>
+        internal static string ErrorSessionNotJoinable {
+            get {
+                return ResourceManager.GetString("ErrorSessionNotJoinable", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to This functionality is not available in trial mode.
+        /// </summary>
+        internal static string ErrorTrialMode {
+            get {
+                return ResourceManager.GetString("ErrorTrialMode", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to An unknown error occurred.
+        /// </summary>
+        internal static string ErrorUnknown {
+            get {
+                return ResourceManager.GetString("ErrorUnknown", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Exit.
+        /// </summary>
+        internal static string Exit {
+            get {
+                return ResourceManager.GetString("Exit", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Find Sessions.
+        /// </summary>
+        internal static string FindSessions {
+            get {
+                return ResourceManager.GetString("FindSessions", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to  (host).
+        /// </summary>
+        internal static string HostSuffix {
+            get {
+                return ResourceManager.GetString("HostSuffix", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Join Session.
+        /// </summary>
+        internal static string JoinSession {
+            get {
+                return ResourceManager.GetString("JoinSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Leave Session.
+        /// </summary>
+        internal static string LeaveSession {
+            get {
+                return ResourceManager.GetString("LeaveSession", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Loading.
+        /// </summary>
+        internal static string Loading {
+            get {
+                return ResourceManager.GetString("Loading", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Lobby.
+        /// </summary>
+        internal static string Lobby {
+            get {
+                return ResourceManager.GetString("Lobby", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Main Menu.
+        /// </summary>
+        internal static string MainMenu {
+            get {
+                return ResourceManager.GetString("MainMenu", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to 
+        ///A button, Space, Enter = ok
+        ///B button, Esc = cancel.
+        /// </summary>
+        internal static string MessageBoxUsage {
+            get {
+                return ResourceManager.GetString("MessageBoxUsage", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} joined.
+        /// </summary>
+        internal static string MessageGamerJoined {
+            get {
+                return ResourceManager.GetString("MessageGamerJoined", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} left.
+        /// </summary>
+        internal static string MessageGamerLeft {
+            get {
+                return ResourceManager.GetString("MessageGamerLeft", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Networking....
+        /// </summary>
+        internal static string NetworkBusy {
+            get {
+                return ResourceManager.GetString("NetworkBusy", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No sessions found.
+        /// </summary>
+        internal static string NoSessionsFound {
+            get {
+                return ResourceManager.GetString("NoSessionsFound", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Paused.
+        /// </summary>
+        internal static string Paused {
+            get {
+                return ResourceManager.GetString("Paused", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to LIVE.
+        /// </summary>
+        internal static string PlayerMatch {
+            get {
+                return ResourceManager.GetString("PlayerMatch", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Quit Game.
+        /// </summary>
+        internal static string QuitGame {
+            get {
+                return ResourceManager.GetString("QuitGame", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Resume Game.
+        /// </summary>
+        internal static string ResumeGame {
+            get {
+                return ResourceManager.GetString("ResumeGame", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Return to Lobby.
+        /// </summary>
+        internal static string ReturnToLobby {
+            get {
+                return ResourceManager.GetString("ReturnToLobby", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Single Player.
+        /// </summary>
+        internal static string SinglePlayer {
+            get {
+                return ResourceManager.GetString("SinglePlayer", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to System Link.
+        /// </summary>
+        internal static string SystemLink {
+            get {
+                return ResourceManager.GetString("SystemLink", resourceCulture);
+            }
+        }
+    }
+}

BIN
Samples/MacOS/CatapultNetWars/Resources.resources


+ 250 - 0
Samples/MacOS/CatapultNetWars/Resources.resx

@@ -0,0 +1,250 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="Back" xml:space="preserve">
+    <value>Back</value>
+  </data>
+  <data name="ConfirmEndSession" xml:space="preserve">
+    <value>Are you sure you want to end this session?</value>
+  </data>
+  <data name="ConfirmExitSample" xml:space="preserve">
+    <value>Are you sure you want to exit this sample?</value>
+  </data>
+  <data name="ConfirmForceStartGame" xml:space="preserve">
+    <value>Are you sure you want to start the game,
+even though not all players are ready?</value>
+  </data>
+  <data name="ConfirmLeaveSession" xml:space="preserve">
+    <value>Are you sure you want to leave this session?</value>
+  </data>
+  <data name="ConfirmMarketplace" xml:space="preserve">
+    <value>Online gameplay is not available in trial mode.
+Would you like to purchase this game?</value>
+  </data>
+  <data name="ConfirmQuitGame" xml:space="preserve">
+    <value>Are you sure you want to quit this game?</value>
+  </data>
+  <data name="CreateSession" xml:space="preserve">
+    <value>Create Session</value>
+  </data>
+  <data name="EndSession" xml:space="preserve">
+    <value>End Session</value>
+  </data>
+  <data name="ErrorDisconnected" xml:space="preserve">
+    <value>Lost connection to the network session</value>
+  </data>
+  <data name="ErrorGamerPrivilege" xml:space="preserve">
+    <value>You must sign in a suitable gamer profile
+in order to access this functionality</value>
+  </data>
+  <data name="ErrorHostEndedSession" xml:space="preserve">
+    <value>Host ended the session</value>
+  </data>
+  <data name="ErrorNetwork" xml:space="preserve">
+    <value>There was an error while
+accessing the network</value>
+  </data>
+  <data name="ErrorNetworkNotAvailable" xml:space="preserve">
+    <value>Networking is turned
+off or not connected</value>
+  </data>
+  <data name="ErrorRemovedByHost" xml:space="preserve">
+    <value>Host kicked you out of the session</value>
+  </data>
+  <data name="ErrorSessionFull" xml:space="preserve">
+    <value>This session is already full</value>
+  </data>
+  <data name="ErrorSessionNotFound" xml:space="preserve">
+    <value>Session not found. It may have ended,
+or there may be no network connectivity
+between the local machine and session host</value>
+  </data>
+  <data name="ErrorSessionNotJoinable" xml:space="preserve">
+    <value>You must wait for the host to return to
+the lobby before you can join this session</value>
+  </data>
+  <data name="ErrorTrialMode" xml:space="preserve">
+    <value>This functionality is not available in trial mode</value>
+  </data>
+  <data name="ErrorUnknown" xml:space="preserve">
+    <value>An unknown error occurred</value>
+  </data>
+  <data name="Exit" xml:space="preserve">
+    <value>Exit</value>
+  </data>
+  <data name="FindSessions" xml:space="preserve">
+    <value>Find Sessions</value>
+  </data>
+  <data name="HostSuffix" xml:space="preserve">
+    <value> (host)</value>
+  </data>
+  <data name="JoinSession" xml:space="preserve">
+    <value>Join Session</value>
+  </data>
+  <data name="LeaveSession" xml:space="preserve">
+    <value>Leave Session</value>
+  </data>
+  <data name="Loading" xml:space="preserve">
+    <value>Loading</value>
+  </data>
+  <data name="Lobby" xml:space="preserve">
+    <value>Lobby</value>
+  </data>
+  <data name="MainMenu" xml:space="preserve">
+    <value>Main Menu</value>
+  </data>
+  <data name="MessageBoxUsage" xml:space="preserve">
+    <value>
+A button, Space, Enter = ok
+B button, Esc = cancel</value>
+  </data>
+  <data name="MessageGamerJoined" xml:space="preserve">
+    <value>{0} joined</value>
+  </data>
+  <data name="MessageGamerLeft" xml:space="preserve">
+    <value>{0} left</value>
+  </data>
+  <data name="NetworkBusy" xml:space="preserve">
+    <value>Networking...</value>
+  </data>
+  <data name="NoSessionsFound" xml:space="preserve">
+    <value>No sessions found</value>
+  </data>
+  <data name="Paused" xml:space="preserve">
+    <value>Paused</value>
+  </data>
+  <data name="PlayerMatch" xml:space="preserve">
+    <value>LIVE</value>
+  </data>
+  <data name="QuitGame" xml:space="preserve">
+    <value>Quit Game</value>
+  </data>
+  <data name="ResumeGame" xml:space="preserve">
+    <value>Resume Game</value>
+  </data>
+  <data name="ReturnToLobby" xml:space="preserve">
+    <value>Return to Lobby</value>
+  </data>
+  <data name="SinglePlayer" xml:space="preserve">
+    <value>Single Player</value>
+  </data>
+  <data name="SystemLink" xml:space="preserve">
+    <value>System Link</value>
+  </data>
+</root>

+ 394 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/GameScreen.cs

@@ -0,0 +1,394 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input.Touch;
+using Microsoft.Xna.Framework.Net;
+using System.IO;
+#endregion
+
+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
+    {
+        #region Properties
+
+
+        /// <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;
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Load graphics content for the screen.
+        /// </summary>
+        public virtual void LoadContent() { }
+
+
+        /// <summary>
+        /// Unload content for the screen.
+        /// </summary>
+        public virtual void UnloadContent() { }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <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) { }
+
+
+        #endregion
+
+        #region Public Methods
+
+        /// <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;
+            }
+        }
+
+
+        #endregion
+
+        #region Helper Methods
+        /// <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);
+        }
+        #endregion
+    }
+}

+ 347 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/InputState.cs

@@ -0,0 +1,347 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// InputState.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using Microsoft.Xna.Framework.Input.Touch;
+#endregion
+
+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
+	{
+        #region Fields
+
+		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> ();
+
+        #endregion
+
+        #region Initialization
+
+
+		/// <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];
+		}
+
+
+        #endregion
+
+        #region Public Methods
+
+
+		/// <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);
+		}
+
+
+        #endregion
+	}
+}

+ 187 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/MenuEntry.cs

@@ -0,0 +1,187 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MenuEntry.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#endregion
+
+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
+    {
+        #region Fields
+
+        /// <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;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <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; }
+        }
+
+
+        #endregion
+
+        #region Events
+
+
+        /// <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));
+        }
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructs a new menu entry with the specified text.
+        /// </summary>
+        public MenuEntry(string text)
+        {
+            this.text = text;
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+        /// <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.White : Color.Yellow;
+
+            // 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;
+        }
+
+        #endregion
+    }
+}

+ 324 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/MenuScreen.cs

@@ -0,0 +1,324 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MenuScreen.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+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;
+#endregion
+
+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
+    {
+        #region Fields
+
+        // the number of pixels to pad above and below menu entries for touch input
+        //const int menuEntryPadding = 35;
+        const int menuEntryPadding = 10;
+
+        List<MenuEntry> menuEntries = new List<MenuEntry>();
+        int selectedEntry = 0;
+        string menuTitle;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <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; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <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);
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+        /// <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);
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <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();
+        }
+
+
+        #endregion
+    }
+}

+ 53 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/MouseGestureType.cs

@@ -0,0 +1,53 @@
+#region License
+// /*
+// 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.
+// */
+#endregion License
+using System;
+
+namespace GameStateManagement
+{
+	[Flags]
+	public enum MouseGestureType
+	{
+		None = 0,
+		LeftClick = 1,
+		DragComplete = 2,		        
+		FreeDrag = 4,
+		Move = 8,
+	}
+}

+ 42 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/PlayerIndexEventArgs.cs

@@ -0,0 +1,42 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PlayerIndexEventArgs.cs
+//
+// XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+#endregion
+
+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;
+    }
+}

+ 451 - 0
Samples/MacOS/CatapultNetWars/ScreenManager/ScreenManager.cs

@@ -0,0 +1,451 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// ScreenManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+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;
+#endregion
+
+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
+    {
+        #region Fields
+
+        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;
+
+        #endregion
+
+        #region Properties
+
+
+        /// <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; }
+        }
+
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <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();
+            }
+        }
+
+
+        #endregion
+
+        #region Update and Draw
+
+
+        /// <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);
+
+            Console.WriteLine(string.Join(", ", screenNames.ToArray()));
+        }
+
+		int count = 0;
+        /// <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);
+            }
+        }
+
+
+        #endregion
+
+        #region Public Methods
+
+
+        /// <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));
+            }
+        }
+
+        #endregion
+    }
+}

+ 71 - 0
Samples/MacOS/CatapultNetWars/Screens/BackgroundScreen.cs

@@ -0,0 +1,71 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// BackgroundScreen.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 System.Text;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+    class BackgroundScreen : GameScreen
+    {
+        #region Fields
+        Texture2D background;
+        #endregion
+
+        #region Initialization
+        public BackgroundScreen()
+        {
+            TransitionOnTime = TimeSpan.FromSeconds(0.0);
+            TransitionOffTime = TimeSpan.FromSeconds(0.5);
+        }
+        #endregion
+
+        #region Loading
+        public override void LoadContent()
+        {
+            background = Load<Texture2D>("Textures/Backgrounds/title_screen");           
+        }
+        #endregion
+
+        #region Render
+        /// <summary>
+        /// Updates the background screen. Unlike most screens, this should not
+        /// transition off even if it has been covered by another screen: it is
+        /// supposed to be covered, after all! This overload forces the
+        /// coveredByOtherScreen parameter to false in order to stop the base
+        /// Update method wanting to transition off.
+        /// </summary>
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
+                                                       bool coveredByOtherScreen)
+        {
+            base.Update(gameTime, otherScreenHasFocus, false);
+        }
+
+        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();
+        }
+        #endregion
+    }
+}

+ 733 - 0
Samples/MacOS/CatapultNetWars/Screens/GameplayScreen.cs

@@ -0,0 +1,733 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// GameplayScreen.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 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;
+using Microsoft.Xna.Framework.Net;
+
+#endregion
+
+namespace CatapultGame
+{
+	class GameplayScreen : GameScreen
+	{
+#region Fields
+
+		enum MessageType : byte
+		{
+			NewGame = 1,
+			CatapultFiring = 2,
+			CatapultAiming = 3,
+			UpdateEnvironment = 4,
+		}
+
+		internal NetworkSession NetworkSession { get; private set; }
+		PacketReader packetReader = new PacketReader ();
+		PacketWriter packetWriter = new PacketWriter ();
+		internal bool IsNetworking { get; private set; }
+
+		// 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 playerOneHUDPosition;
+		Vector2 computerHUDPosition;
+		Vector2 windArrowPosition;
+
+		// Gameplay members
+		Human playerOne;
+		Human playerTwo;
+
+		Vector2 wind;
+		bool changeTurn;
+		bool isFirstPlayerTurn;
+		bool gameOver;
+		Random random;
+		const int minWind = 0;
+		const int maxWind = 10;
+
+		// Helper members
+		bool isDragging;
+#endregion
+
+#region Initialization
+		public GameplayScreen ()
+		{
+			EnabledGestures = GestureType.FreeDrag |
+					GestureType.DragComplete |
+					GestureType.Tap;
+
+			random = new Random ();
+		}
+#endregion
+
+#region Content Loading/Unloading
+		/// <summary>
+		/// Loads the game assets and initializes "players"
+		/// </summary>
+		public override void LoadContent ()
+		{
+			base.LoadContent ();
+
+			// Start the game
+			Start ();
+		}
+
+		public void LoadAssets ()
+		{
+
+			NetworkSession = ScreenManager.Game.Services.GetService (typeof(NetworkSession)) as NetworkSession;
+			IsNetworking = NetworkSession != null;
+
+			// 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
+			playerOneHUDPosition = new Vector2 (7, 7);
+			computerHUDPosition = new Vector2 (613, 7);
+			windArrowPosition = new Vector2 (345, 46);
+
+
+			// Initialize human & other players
+			if (IsNetworking) {
+
+				if (NetworkSession.RemoteGamers.Count > 0) {
+					if (NetworkSession.IsHost) {
+						playerOne = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Left);
+						playerOne.Initialize ();
+						playerOne.Name = NetworkSession.LocalGamers [0].Gamertag + " (host)";
+						playerTwo = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Right);
+						playerTwo.Initialize ();
+						playerTwo.Name = NetworkSession.RemoteGamers [0].Gamertag;
+					} else {
+						playerOne = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Left);
+						playerOne.Initialize ();
+						playerOne.Name = NetworkSession.RemoteGamers [0].Gamertag + " (host)";
+						playerTwo = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Right);
+						playerTwo.Initialize ();
+						playerTwo.Name = NetworkSession.LocalGamers [0].Gamertag;
+					}
+				}
+				else {
+					playerOne = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Left);
+					playerOne.Initialize ();
+					playerOne.Name = NetworkSession.LocalGamers [0].Gamertag + " (host)";
+					playerTwo = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Right);
+					playerTwo.Initialize ();
+					playerTwo.Name = "Player 2";
+					IsNetworking = false;
+					NetworkSession = null;
+				}
+			} else {
+				playerOne = new Human (ScreenManager.Game, ScreenManager.SpriteBatch, PlayerSide.Left);
+				playerOne.Initialize ();
+				playerOne.Name = "Player 1";
+				playerTwo = new AI (ScreenManager.Game, ScreenManager.SpriteBatch);
+				playerTwo.Initialize ();
+				playerTwo.Name = "Player 2";
+			}
+
+			// Identify enemies
+			playerOne.Enemy = playerTwo;
+			playerTwo.Enemy = playerOne;
+		}
+#endregion
+
+#region Update
+		/// <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 ((playerOne.Catapult.GameOver || playerTwo.Catapult.GameOver) &&
+ 				(gameOver == false)) {
+
+				gameOver = true;
+
+				if (IsNetworking) {
+					if (NetworkSession.IsHost) {
+						if (playerOne.Score > playerTwo.Score) {
+							AudioManager.PlaySound ("gameOver_Win");
+						} else {
+							AudioManager.PlaySound ("gameOver_Lose");
+						}
+					}
+					else {
+						if (playerOne.Score > playerTwo.Score) {
+							AudioManager.PlaySound ("gameOver_Lose");
+						} else {
+							AudioManager.PlaySound ("gameOver_Win");
+						}
+					}
+				}
+				else {
+					if (playerOne.Score > playerTwo.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 ((playerOne.Catapult.CurrentState == CatapultState.Reset ||
+				playerTwo.Catapult.CurrentState == CatapultState.Reset) &&
+				!(playerOne.Catapult.AnimationRunning ||
+				playerTwo.Catapult.AnimationRunning)) {
+
+				changeTurn = true;
+
+				if (playerOne.IsActive == true) { //Last turn was a human turn?
+					playerOne.IsActive = false;
+					playerTwo.IsActive = true;
+					isFirstPlayerTurn = false;
+					playerOne.Catapult.CurrentState = CatapultState.Idle;
+					if (playerTwo.IsAI)
+						playerTwo.Catapult.CurrentState = CatapultState.Aiming;
+					else
+						playerTwo.Catapult.CurrentState = CatapultState.Idle;
+				} else { //It was an AI turn
+					playerOne.IsActive = true;
+					playerTwo.IsActive = false;
+					isFirstPlayerTurn = true;
+					playerTwo.Catapult.CurrentState = CatapultState.Idle;
+					playerOne.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
+				playerOne.Catapult.Wind = playerTwo.Catapult.Wind = wind.X > 0 ? wind.Y : -wind.Y;
+				changeTurn = false;
+			}
+
+			// Update the players
+			playerOne.Update (gameTime);
+			playerTwo.Update (gameTime);
+
+			// Updates the clouds position
+			UpdateClouds (elapsed);
+			UpdateNetworkSession ();
+			base.Update (gameTime, otherScreenHasFocus, coveredByOtherScreen);
+		}
+#endregion
+
+#region Draw
+		/// <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 ();
+		}
+#endregion
+
+#region Input
+		/// <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 ();
+				}
+
+				if (input.MouseGesture.HasFlag(MouseGestureType.LeftClick))
+					FinishCurrentGame();
+
+				foreach (GestureSample gestureSample in input.Gestures) {
+					if (gestureSample.GestureType == GestureType.Tap) {
+						FinishCurrentGame ();
+					}
+				}
+
+				return;
+			}
+
+			if (NetworkSession != null) {
+				if ((isFirstPlayerTurn && !NetworkSession.IsHost)
+				    || (!isFirstPlayerTurn && NetworkSession.IsHost))
+					return;
+			}
+
+			if (input.IsPauseGame (null)) {
+				PauseCurrentGame ();
+			} else if (isFirstPlayerTurn &&
+					(playerOne.Catapult.CurrentState == CatapultState.Idle ||
+					playerOne.Catapult.CurrentState == CatapultState.Aiming)) {
+
+				// First we try with mouse input
+				playerOne.HandleInput (input);
+				isDragging = playerOne.isDragging;
+
+				// 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;
+
+					playerOne.HandleInput (gestureSample);
+				}
+			} else if (!isFirstPlayerTurn &&
+					(playerTwo.Catapult.CurrentState == CatapultState.Idle ||
+					playerTwo.Catapult.CurrentState == CatapultState.Aiming)) {
+
+				// First we try with mouse input
+				playerTwo.HandleInput (input);
+				isDragging = playerTwo.isDragging;
+
+				// 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;
+
+					playerTwo.HandleInput (gestureSample);
+				}
+			}
+		}
+
+		Vector3 catapultInfoVector = Vector3.Zero;
+		void UpdateNetworkSession ()
+		{
+			if (NetworkSession == null)
+				return;
+
+			if (NetworkSession.IsHost) {
+				packetWriter.Write((int)MessageType.UpdateEnvironment);
+				packetWriter.Write(wind);
+				packetWriter.Write(cloud1Position);
+				packetWriter.Write(cloud2Position);
+
+				// Update our locally controlled tanks, and send their
+				// latest position data to everyone in the session.
+				foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers) {
+					gamer.SendData(packetWriter, SendDataOptions.ReliableInOrder);
+				}
+			}
+
+			if (playerOne.Catapult.CurrentState == CatapultState.Aiming) {
+				SendCatapultInfo(MessageType.CatapultAiming, playerOne);
+			}
+
+			if (playerOne.Catapult.CurrentState == CatapultState.Firing) {
+
+				SendCatapultInfo(MessageType.CatapultFiring, playerOne);
+			}
+
+			if (playerTwo.Catapult.CurrentState == CatapultState.Aiming) {
+				SendCatapultInfo(MessageType.CatapultAiming, playerTwo);
+			}
+
+			if (playerTwo.Catapult.CurrentState == CatapultState.Firing) {
+				SendCatapultInfo(MessageType.CatapultFiring, playerTwo);
+			}
+
+			// Pump the underlying session object.
+			NetworkSession.Update ();
+
+			// Make sure the session has not ended.
+			if (NetworkSession == null)
+				return;
+
+			// Read any packets
+			foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers) {
+				ReadIncomingPackets (gamer);
+			}
+		}
+
+		void SendCatapultInfo (MessageType messageType,  Human player)
+		{
+			packetWriter.Write((int)messageType);
+			catapultInfoVector.X = player.Catapult.ShotStrength;
+			catapultInfoVector.Y = player.Catapult.ShotVelocity;
+			catapultInfoVector.Z = player.ArrowScale;
+			packetWriter.Write(catapultInfoVector);
+
+			// Update our locally controlled catapults, and send their
+			// latest position data to everyone in the session.
+			foreach (LocalNetworkGamer gamer in NetworkSession.LocalGamers) {
+				gamer.SendData(packetWriter, SendDataOptions.ReliableInOrder);
+			}
+		}
+
+		/// <summary>
+		/// Helper for reading incoming network packets.
+		/// </summary>
+		void ReadIncomingPackets (LocalNetworkGamer gamer)
+		{
+			// Keep reading as long as incoming packets are available.
+			while (gamer.IsDataAvailable) {
+				NetworkGamer sender;
+
+				// Read a single packet from the network.
+				gamer.ReceiveData (packetReader, out sender);
+
+				// Discard packets sent by local gamers: we already know their state!
+				if (sender.IsLocal)
+					continue;
+
+				MessageType msgType = (MessageType)packetReader.ReadInt32 ();
+				switch (msgType) {
+				case MessageType.NewGame:
+					//ReceiveNewNetworkedGame();
+					break;
+				case MessageType.CatapultAiming:
+
+					if (isFirstPlayerTurn && !NetworkSession.IsHost) {
+						playerOne.Catapult.CurrentState = CatapultState.Aiming;
+						playerOne.isDragging = true;
+
+						catapultInfoVector = packetReader.ReadVector3();
+
+						playerOne.Catapult.ShotStrength = catapultInfoVector.X;
+						playerOne.Catapult.ShotVelocity = catapultInfoVector.Y;
+						playerOne.ArrowScale = catapultInfoVector.Z;
+
+					}
+					if (!isFirstPlayerTurn && NetworkSession.IsHost) {
+
+						playerTwo.Catapult.CurrentState = CatapultState.Aiming;
+						playerTwo.isDragging = true;
+
+						catapultInfoVector = packetReader.ReadVector3();
+
+						playerTwo.Catapult.ShotStrength = catapultInfoVector.X;
+						playerTwo.Catapult.ShotVelocity = catapultInfoVector.Y;
+						playerTwo.ArrowScale = catapultInfoVector.Z;
+
+					}
+					break;
+				case MessageType.CatapultFiring:
+
+					if (isFirstPlayerTurn  && !NetworkSession.IsHost) {
+						catapultInfoVector = packetReader.ReadVector3();
+						playerOne.Catapult.Fire (catapultInfoVector.Y);
+						playerOne.Catapult.CurrentState = CatapultState.Firing;
+						playerOne.ResetDragState();
+					}
+					if (!isFirstPlayerTurn  && NetworkSession.IsHost) {
+						catapultInfoVector = packetReader.ReadVector3();
+						playerTwo.Catapult.Fire (catapultInfoVector.Y);
+						playerTwo.Catapult.CurrentState = CatapultState.Firing;
+						playerTwo.ResetDragState();
+					}
+					break;
+				case MessageType.UpdateEnvironment:
+					wind = packetReader.ReadVector2();
+					cloud1Position = packetReader.ReadVector2();
+					cloud2Position = packetReader.ReadVector2();
+					// Set new wind value to the players and
+					playerOne.Catapult.Wind = playerTwo.Catapult.Wind = wind.X > 0 ? wind.Y : -wind.Y;
+					break;
+				}
+			}
+		}
+#endregion
+
+#region Update Helpers
+		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;
+		}
+#endregion
+
+#region Draw Helpers
+		/// <summary>
+		/// Draws the player's catapult
+		/// </summary>
+		void DrawPlayer (GameTime gameTime)
+		{
+			if (!gameOver)
+				playerOne.Draw (gameTime);
+		}
+
+		/// <summary>
+		/// Draws the AI's catapult
+		/// </summary>
+		void DrawComputer (GameTime gameTime)
+		{
+			if (!gameOver)
+				playerTwo.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 (IsNetworking) {
+					if (NetworkSession.IsHost) {
+						if (playerOne.Score > playerTwo.Score) {
+							texture = victoryTexture;
+						} else {
+							texture = defeatTexture;
+						}
+					}
+					else {
+						if (playerOne.Score > playerTwo.Score) {
+							texture = defeatTexture;
+						} else {
+							texture = victoryTexture;
+						}
+					}
+				}
+				else {
+					if (playerOne.Score > playerTwo.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, playerOneHUDPosition, Color.White);
+				ScreenManager.SpriteBatch.Draw (ammoTypeTexture, playerOneHUDPosition + new Vector2 (33, 35), Color.White);
+				DrawString (hudFont, playerOne.Score.ToString (), playerOneHUDPosition + new Vector2 (123, 35), Color.White);
+				DrawString (hudFont, playerOne.Name, playerOneHUDPosition + 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, playerTwo.Score.ToString (), computerHUDPosition + new Vector2 (123, 35), Color.White);
+				DrawString (hudFont, playerTwo.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 (IsNetworking) {
+					if (isFirstPlayerTurn) {
+						if (NetworkSession.IsHost) {
+							// Prepare player one prompt message
+							text = !isDragging ? "Drag Anywhere to Fire" : "Release to Fire!";
+						}
+						else {
+							text = "Waiting for " + playerOne.Name;
+						}
+					} else {
+						if (NetworkSession.IsHost) {
+							// Prepare player one prompt message
+							text = "Waiting for " + playerTwo.Name;
+						}
+						else {
+							text = !isDragging ? "Drag Anywhere to Fire" : "Release to Fire!";
+						}
+					}
+
+				}
+				else {
+					if (isFirstPlayerTurn) {
+						// Prepare player one prompt message
+						text = !isDragging ? "Drag Anywhere to Fire" : "Release to Fire!";
+					} else {
+						// Prepare other player 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);
+		}
+#endregion
+
+#region Input Helpers
+		/// <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;
+				playerOne.Catapult.CurrentState = CatapultState.Idle;
+			}
+
+			ScreenManager.AddScreen (pauseMenuBackground, null);
+			ScreenManager.AddScreen (new PauseScreen (pauseMenuBackground, playerOne, playerTwo), null);
+		}
+#endregion
+
+#region Gameplay Helpers
+		/// <summary>
+		/// Starts a new game session, setting all game states to initial values.
+		/// </summary>
+		void Start ()
+		{
+			// Set initial wind direction
+			wind = Vector2.Zero;
+			isFirstPlayerTurn = false;
+			changeTurn = true;
+			playerTwo.Catapult.CurrentState = CatapultState.Reset;
+		}
+#endregion
+	}
+}

+ 162 - 0
Samples/MacOS/CatapultNetWars/Screens/InstructionsScreen.cs

@@ -0,0 +1,162 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// BackgroundScreen.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 System.Text;
+using System.Threading;
+
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework;
+using GameStateManagement;
+using Microsoft.Xna.Framework.Input.Touch;
+
+#if MACOS
+using MonoMac.AppKit;
+using MonoMac.Foundation;
+#endif
+#endregion
+
+namespace CatapultGame
+{
+	class InstructionsScreen : GameScreen
+	{
+#region Fields
+		Texture2D background;
+		SpriteFont font;
+		bool isLoading;
+		GameplayScreen gameplayScreen;
+		System.Threading.Thread thread;
+#endregion
+
+#region Initialization
+		public InstructionsScreen ()
+		{
+			EnabledGestures = GestureType.Tap;
+
+			TransitionOnTime = TimeSpan.FromSeconds (0);
+			TransitionOffTime = TimeSpan.FromSeconds (0.5);
+		}
+#endregion
+
+#region Loading
+		public override void LoadContent ()
+		{
+			background = Load<Texture2D> ("Textures/Backgrounds/instructions");
+			font = Load<SpriteFont> ("Fonts/MenuFont");
+		}
+#endregion
+
+		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);
+		}
+
+#region Handle input
+		public override void HandleInput (InputState input)
+		{
+			if (isLoading == true) {
+				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 MACOS
+				// 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;
+
+					// Start loading the resources in additional thread
+					thread = new System.Threading.Thread (new System.Threading.ThreadStart (gameplayScreen.LoadAssets));
+					isLoading = true;
+					thread.Start ();
+				}
+			}
+
+			base.HandleInput (input);
+		}
+
+		void LoadAssetsWorkerThread ()
+		{
+
+			// Create an Autorelease Pool or we will leak objects.
+			using (var pool = new NSAutoreleasePool()) {
+				// Make sure we invoke this on the Main Thread or OpenGL will throw an error
+				MonoMac.AppKit.NSApplication.SharedApplication.BeginInvokeOnMainThread (delegate {
+
+					gameplayScreen.LoadAssets ();
+				});
+			}
+
+		}
+#endregion
+
+#region Render
+		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 ();
+		}
+#endregion
+	}
+}

+ 306 - 0
Samples/MacOS/CatapultNetWars/Screens/LoadingScreen.cs

@@ -0,0 +1,306 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// LoadingScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using System.Threading;
+using System.Diagnostics;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Net;
+using GameStateManagement;
+#endregion
+
+namespace CatapultGame
+{
+	/// <summary>
+	/// The loading screen coordinates transitions between the menu system and the
+	/// game itself. Normally one screen will transition off at the same time as
+	/// the next screen is transitioning on, but for larger transitions that can
+	/// take a longer time to load their data, we want the menu system to be entirely
+	/// gone before we start loading the game. This is done as follows:
+	/// 
+	/// - Tell all the existing screens to transition off.
+	/// - Activate a loading screen, which will transition on at the same time.
+	/// - The loading screen watches the state of the previous screens.
+	/// - When it sees they have finished transitioning off, it activates the real
+	///   next screen, which may take a long time to load its data. The loading
+	///   screen will be the only thing displayed while this load is taking place.
+	/// </summary>
+	class LoadingScreen : GameScreen
+	{
+	#region Fields
+
+		bool loadingIsSlow;
+		bool otherScreensAreGone;
+		GameScreen[] screensToLoad;
+		Thread backgroundThread;
+		EventWaitHandle backgroundThreadExit;
+		GraphicsDevice graphicsDevice;
+		NetworkSession networkSession;
+		IMessageDisplay messageDisplay;
+		GameTime loadStartTime;
+		TimeSpan loadAnimationTimer;
+
+	#endregion
+
+	#region Initialization
+
+
+		/// <summary>
+		/// The constructor is private: loading screens should
+		/// be activated via the static Load method instead.
+		/// </summary>
+		private LoadingScreen (ScreenManager screenManager,bool loadingIsSlow, 
+				GameScreen[] screensToLoad)
+			{
+			this.loadingIsSlow = loadingIsSlow;
+			this.screensToLoad = screensToLoad;
+
+			TransitionOnTime = TimeSpan.FromSeconds (0.5);
+
+			// If this is going to be a slow load operation, create a background
+			// thread that will update the network session and draw the load screen
+			// animation while the load is taking place.
+			if (loadingIsSlow) {
+				backgroundThread = new Thread (BackgroundWorkerThread);
+				backgroundThreadExit = new ManualResetEvent (false);
+
+				graphicsDevice = screenManager.GraphicsDevice;
+
+				// Look up some services that will be used by the background thread.
+				IServiceProvider services = screenManager.Game.Services;
+
+				networkSession = (NetworkSession)services.GetService (
+							typeof(NetworkSession));
+
+				messageDisplay = (IMessageDisplay)services.GetService (
+							typeof(IMessageDisplay));
+			}
+		}
+
+
+		/// <summary>
+		/// Activates the loading screen.
+		/// </summary>
+		public static void Load (ScreenManager screenManager, bool loadingIsSlow,
+				PlayerIndex? controllingPlayer,
+				params GameScreen[] screensToLoad)
+		{
+			// Tell all the current screens to transition off.
+			foreach (GameScreen screen in screenManager.GetScreens ())
+				screen.ExitScreen ();
+
+			// Create and activate the loading screen.
+			LoadingScreen loadingScreen = new LoadingScreen (screenManager,
+							loadingIsSlow,
+							screensToLoad);
+
+			screenManager.AddScreen (loadingScreen, controllingPlayer);
+		}
+
+
+	#endregion
+
+	#region Update and Draw
+
+
+		/// <summary>
+		/// Updates the loading screen.
+		/// </summary>
+		public override void Update (GameTime gameTime, bool otherScreenHasFocus,
+							bool coveredByOtherScreen)
+		{
+			base.Update (gameTime, otherScreenHasFocus, coveredByOtherScreen);
+
+			// If all the previous screens have finished transitioning
+			// off, it is time to actually perform the load.
+			if (otherScreensAreGone) {
+				// Start up the background thread, which will update the network
+				// session and draw the animation while we are loading.
+				if (backgroundThread != null) {
+					loadStartTime = gameTime;
+					backgroundThread.Start ();
+				}
+
+				// Perform the load operation.
+				ScreenManager.RemoveScreen (this);
+
+				foreach (GameScreen screen in screensToLoad) {
+					if (screen != null) {
+						ScreenManager.AddScreen (screen, ControllingPlayer);
+					}
+				}
+
+				// Signal the background thread to exit, then wait for it to do so.
+				if (backgroundThread != null) {
+					backgroundThreadExit.Set ();
+					backgroundThread.Join ();
+				}
+
+				// Once the load has finished, we use ResetElapsedTime to tell
+				// the  game timing mechanism that we have just finished a very
+				// long frame, and that it should not try to catch up.
+				ScreenManager.Game.ResetElapsedTime ();
+			}
+		}
+
+
+		/// <summary>
+		/// Draws the loading screen.
+		/// </summary>
+		public override void Draw (GameTime gameTime)
+		{
+			// If we are the only active screen, that means all the previous screens
+			// must have finished transitioning off. We check for this in the Draw
+			// method, rather than in Update, because it isn't enough just for the
+			// screens to be gone: in order for the transition to look good we must
+			// have actually drawn a frame without them before we perform the load.
+			if ((ScreenState == ScreenState.Active) && 
+		(ScreenManager.GetScreens ().Length == 1)) {
+				otherScreensAreGone = true;
+			}
+
+			// The gameplay screen takes a while to load, so we display a loading
+			// message while that is going on, but the menus load very quickly, and
+			// it would look silly if we flashed this up for just a fraction of a
+			// second while returning from the game to the menus. This parameter
+			// tells us how long the loading is going to take, so we know whether
+			// to bother drawing the message.
+			if (loadingIsSlow) {
+				SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+				SpriteFont font = ScreenManager.Font;
+
+				string message = Resources.Loading;
+
+				// Center the text in the viewport.
+				Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+				Vector2 viewportSize = new Vector2 (viewport.Width, viewport.Height);
+				Vector2 textSize = font.MeasureString (message);
+				Vector2 textPosition = (viewportSize - textSize) / 2;
+
+				Color color = Color.White * TransitionAlpha;
+
+				// Animate the number of dots after our "Loading..." message.
+				loadAnimationTimer += gameTime.ElapsedGameTime;
+
+				int dotCount = (int)(loadAnimationTimer.TotalSeconds * 5) % 10;
+
+				message += new string ('.', dotCount);
+
+				// Draw the text.
+				spriteBatch.Begin ();
+				spriteBatch.DrawString (font, message, textPosition, color);
+				spriteBatch.End ();
+			}
+		}
+
+
+	#endregion
+
+	#region Background Thread
+
+
+		/// <summary>
+		/// Worker thread draws the loading animation and updates the network
+		/// session while the load is taking place.
+		/// </summary>
+		void BackgroundWorkerThread ()
+		{
+			long lastTime = Stopwatch.GetTimestamp ();
+
+			// EventWaitHandle.WaitOne will return true if the exit signal has
+			// been triggered, or false if the timeout has expired. We use the
+			// timeout to update at regular intervals, then break out of the
+			// loop when we are signalled to exit.
+			while (!backgroundThreadExit.WaitOne (1000 / 30)) {
+				//GameTime gameTime = GetGameTime (ref lastTime);
+
+				//DrawLoadAnimation (gameTime);
+
+				UpdateNetworkSession ();
+			}
+		}
+
+
+//		/// <summary>
+//		/// Works out how long it has been since the last background thread update.
+//		/// </summary>
+//		GameTime GetGameTime (ref long lastTime)
+//		{
+//			long currentTime = Stopwatch.GetTimestamp ();
+//			long elapsedTicks = currentTime - lastTime;
+//			lastTime = currentTime;
+//
+//			TimeSpan elapsedTime = TimeSpan.FromTicks (elapsedTicks * 
+//						TimeSpan.TicksPerSecond / 
+//						Stopwatch.Frequency);
+//
+//			return new GameTime (loadStartTime.TotalGameTime + elapsedTime, elapsedTime);
+//		}
+
+
+		/// <summary>
+		/// Calls directly into our Draw method from the background worker thread,
+		/// so as to update the load animation in parallel with the actual loading.
+		/// </summary>
+		void DrawLoadAnimation (GameTime gameTime)
+		{
+			if ((graphicsDevice == null) || graphicsDevice.IsDisposed)
+				return;
+
+			try			{
+				graphicsDevice.Clear (Color.Black);
+
+				// Draw the loading screen.
+				Draw (gameTime);
+
+				// If we have a message display component, we want to display
+				// that over the top of the loading screen, too.
+				if (messageDisplay != null) {
+					messageDisplay.Update (gameTime);
+					messageDisplay.Draw (gameTime);
+				}
+
+				graphicsDevice.Present ();
+			} catch {
+				// If anything went wrong (for instance the graphics device was lost
+				// or reset) we don't have any good way to recover while running on a
+				// background thread. Setting the device to null will stop us from
+				// rendering, so the main game can deal with the problem later on.
+				graphicsDevice = null;
+			}
+		}
+
+
+		/// <summary>
+		/// Updates the network session from the background worker thread, to avoid
+		/// disconnecting due to network timeouts even if loading takes a long time.
+		/// </summary>
+		void UpdateNetworkSession ()
+		{
+			if ((networkSession == null) || 
+		(networkSession.SessionState == NetworkSessionState.Ended))
+				return;
+
+			try			{
+				networkSession.Update ();
+			} catch {
+				// If anything went wrong, we don't have a good way to report that
+				// error while running on a background thread. Setting the session to
+				// null will stop us from updating it, so the main game can deal with
+				// the problem later on.
+				networkSession = null;
+			}
+		}
+
+
+	#endregion
+	}
+}

+ 118 - 0
Samples/MacOS/CatapultNetWars/Screens/MainMenuScreen.cs

@@ -0,0 +1,118 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MainMenuScreen.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 System.Text;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Net;
+#endregion
+
+namespace CatapultGame
+{
+    class MainMenuScreen : MenuScreen
+    {
+        #region Initialization
+        public MainMenuScreen()
+            : base(String.Empty)
+        {
+            IsPopup = true;
+
+            // Create our menu entries.
+            MenuEntry startGameMenuEntry = new MenuEntry("Play");
+            MenuEntry startMultiPlayerGameMenuEntry = new MenuEntry("Multi-Player");
+            MenuEntry exitMenuEntry = new MenuEntry("Exit");
+
+            // Hook up menu event handlers.
+            startGameMenuEntry.Selected += StartGameMenuEntrySelected;
+            startMultiPlayerGameMenuEntry.Selected += StartMultiPlayerGameMenuEntrySelected;
+            exitMenuEntry.Selected += OnCancel;
+
+            // Add entries to the menu.
+            MenuEntries.Add(startGameMenuEntry);
+            MenuEntries.Add(startMultiPlayerGameMenuEntry);
+            MenuEntries.Add(exitMenuEntry);
+        }
+
+        #endregion
+
+        #region Overrides
+        protected override void UpdateMenuEntryLocations()
+        {
+            base.UpdateMenuEntryLocations();
+
+            foreach (var entry in MenuEntries)
+            {
+                Vector2 position = entry.Position;
+
+                position.Y += 60;
+
+                entry.Position = position;
+            }
+        }
+        #endregion
+
+        #region Event Handlers for Menu Items
+        /// <summary>
+        /// Handles "Play" menu item selection
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
+        void StartGameMenuEntrySelected(object sender, EventArgs e)
+        {
+		// Lets make sure we get rid of our network session
+		// so we can start up clean
+		ScreenManager.Game.Services.RemoveService(typeof(NetworkSession));
+            ScreenManager.AddScreen(new InstructionsScreen(), null);
+        }
+
+        /// <summary>
+        /// Handles "Exit" menu item selection
+        /// </summary>
+        /// 
+        protected override void OnCancel(PlayerIndex playerIndex)
+        {
+            ScreenManager.Game.Exit();
+        }
+
+        void StartMultiPlayerGameMenuEntrySelected (object sender, PlayerIndexEventArgs e)
+        {
+		CreateOrFindSession (NetworkSessionType.SystemLink, e.PlayerIndex);
+        }
+
+		/// <summary>
+		/// Helper method shared by the Live and System Link menu event handlers.
+		/// </summary>
+		void CreateOrFindSession (NetworkSessionType sessionType,
+				PlayerIndex playerIndex)
+		{
+
+			// First, we need to make sure a suitable gamer profile is signed in.
+			ProfileSignInScreen profileSignIn = new ProfileSignInScreen (sessionType);
+
+			// Hook up an event so once the ProfileSignInScreen is happy,
+			// it will activate the CreateOrFindSessionScreen.
+			profileSignIn.ProfileSignedIn += delegate
+				{
+					GameScreen createOrFind = new CreateOrFindSessionScreen (sessionType);
+
+					ScreenManager.AddScreen (createOrFind, playerIndex);
+				};
+
+			// Activate the ProfileSignInScreen.
+			ScreenManager.AddScreen (profileSignIn, playerIndex);
+		}
+
+
+        #endregion
+    }
+}

+ 168 - 0
Samples/MacOS/CatapultNetWars/Screens/MessageBoxScreen.cs

@@ -0,0 +1,168 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// MessageBoxScreen.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using CatapultGame;
+#endregion
+
+namespace GameStateManagement
+{
+    /// <summary>
+    /// A popup message box screen, used to display "are you sure?"
+    /// confirmation messages.
+    /// </summary>
+    class MessageBoxScreen : GameScreen
+    {
+        #region Fields
+
+        string message;
+        Texture2D gradientTexture;
+
+        #endregion
+
+        #region Events
+
+        public event EventHandler<PlayerIndexEventArgs> Accepted;
+        public event EventHandler<PlayerIndexEventArgs> Cancelled;
+
+        #endregion
+
+        #region Initialization
+
+
+        /// <summary>
+        /// Constructor automatically includes the standard "A=ok, B=cancel"
+        /// usage text prompt.
+        /// </summary>
+        public MessageBoxScreen(string message)
+            : this(message, true)
+        { }
+
+
+        /// <summary>
+        /// Constructor lets the caller specify whether to include the standard
+        /// "A=ok, B=cancel" usage text prompt.
+        /// </summary>
+        public MessageBoxScreen(string message, bool includeUsageText)
+        {
+            if (includeUsageText)
+                this.message = message + Resources.MessageBoxUsage;
+            else
+                this.message = message;
+
+            IsPopup = true;
+
+            TransitionOnTime = TimeSpan.FromSeconds(0.2);
+            TransitionOffTime = TimeSpan.FromSeconds(0.2);
+        }
+
+
+        /// <summary>
+        /// Loads graphics content for this screen. This uses the shared ContentManager
+        /// provided by the Game class, so the content will remain loaded forever.
+        /// Whenever a subsequent MessageBoxScreen tries to load this same content,
+        /// it will just get back another reference to the already loaded data.
+        /// </summary>
+        public override void LoadContent()
+        {
+            ContentManager content = ScreenManager.Game.Content;
+
+            gradientTexture = content.Load<Texture2D>("gradient");
+        }
+
+
+        #endregion
+
+        #region Handle Input
+
+
+        /// <summary>
+        /// Responds to user input, accepting or cancelling the message box.
+        /// </summary>
+        public override void HandleInput(InputState input)
+        {
+            PlayerIndex playerIndex;
+
+            // We pass in our ControllingPlayer, which may either be null (to
+            // accept input from any player) or a specific index. If we pass a null
+            // controlling player, the InputState helper returns to us which player
+            // actually provided the input. We pass that through to our Accepted and
+            // Cancelled events, so they can tell which player triggered them.
+            if (input.IsMenuSelect(ControllingPlayer, out playerIndex))
+            {
+                // Raise the accepted event, then exit the message box.
+                if (Accepted != null)
+                    Accepted(this, new PlayerIndexEventArgs(playerIndex));
+
+                ExitScreen();
+            }
+            else if (input.IsMenuCancel(ControllingPlayer, out playerIndex))
+            {
+                // Raise the cancelled event, then exit the message box.
+                if (Cancelled != null)
+                    Cancelled(this, new PlayerIndexEventArgs(playerIndex));
+
+                ExitScreen();
+            }
+        }
+
+
+        #endregion
+
+        #region Draw
+
+
+        /// <summary>
+        /// Draws the message box.
+        /// </summary>
+        public override void Draw(GameTime gameTime)
+        {
+            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
+            SpriteFont font = ScreenManager.Font;
+
+            // Darken down any other screens that were drawn beneath the popup.
+            ScreenManager.FadeBackBufferToBlack(TransitionAlpha * 2 / 3);
+
+            // Center the message text in the viewport.
+            Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
+            Vector2 viewportSize = new Vector2(viewport.Width, viewport.Height);
+            Vector2 textSize = font.MeasureString(message);
+            Vector2 textPosition = (viewportSize - textSize) / 2;
+
+            // The background includes a border somewhat larger than the text itself.
+            const int hPad = 32;
+            const int vPad = 16;
+
+            Rectangle backgroundRectangle = new Rectangle((int)textPosition.X - hPad,
+                                                          (int)textPosition.Y - vPad,
+                                                          (int)textSize.X + hPad * 2,
+                                                          (int)textSize.Y + vPad * 2);
+
+            // Fade the popup alpha during transitions.
+            Color color = Color.White * TransitionAlpha;
+
+            spriteBatch.Begin();
+
+            // Draw the background rectangle.
+            spriteBatch.Draw(gradientTexture, backgroundRectangle, color);
+
+            // Draw the message box text.
+            spriteBatch.DrawString(font, message, textPosition, color);
+
+            spriteBatch.End();
+        }
+
+
+        #endregion
+    }
+}

+ 119 - 0
Samples/MacOS/CatapultNetWars/Screens/PauseScreen.cs

@@ -0,0 +1,119 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// PauseScreen.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 System.Text;
+using GameStateManagement;
+using Microsoft.Xna.Framework;
+#endregion
+
+namespace CatapultGame
+{
+    class PauseScreen : MenuScreen
+    {
+        #region Fields
+        GameScreen backgroundScreen;
+        Player human;
+        Player computer;
+        bool prevHumanIsActive;
+        bool prevCompuerIsActive;
+        #endregion
+
+        #region Initialization
+        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);
+        }
+        #endregion
+
+        #region Overrides
+        protected override void UpdateMenuEntryLocations()
+        {
+            base.UpdateMenuEntryLocations();
+
+            foreach (var entry in MenuEntries)
+            {
+                Vector2 position = entry.Position;
+
+                position.Y += 60;
+
+                entry.Position = position;
+            }
+        }
+        #endregion
+
+        #region Event Handlers for Menu Items
+        /// <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();
+
+
+        }
+        #endregion
+    }
+}

+ 164 - 0
Samples/MacOS/CatapultNetWars/Utility/Animation.cs

@@ -0,0 +1,164 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// Animation.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region File Information
+//-----------------------------------------------------------------------------
+// Animation.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 System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+#endregion
+
+namespace CatapultGame
+{
+    class Animation
+    {
+        #region Fields
+        // 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; }
+        #endregion
+
+        #region Initialization
+        /// <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;
+        }
+        #endregion
+
+        #region Update and Render
+        /// <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;
+        }
+        #endregion
+    }
+}

+ 210 - 0
Samples/MacOS/CatapultNetWars/Utility/AudioManager.cs

@@ -0,0 +1,210 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// AudioManager.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+#endregion
+
+#region Using Statements
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using System.Collections.Generic;
+using System.Linq;
+#endregion
+
+namespace CatapultGame
+{
+    /// <summary>
+    /// Component that manages audio playback for all sounds.
+    /// </summary>
+    public class AudioManager : GameComponent
+    {
+        #region Singleton
+        /// <summary>
+        /// The singleton for this type.
+        /// </summary>
+        private static AudioManager audioManager = null;
+        #endregion
+
+        #region Audio Data
+        private SoundEffectInstance musicSound;
+        private Dictionary<string, SoundEffectInstance> soundBank;
+        private string[,] soundNames;
+        #endregion
+
+        #region Initialization Methods
+
+        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);
+            }
+        }
+
+        #endregion
+
+        #region Loading Methodes
+        /// <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());
+            }
+        }
+        #endregion
+
+        #region Sound Methods
+        /// <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();
+            }
+        }
+        #endregion
+
+        #region Instance Disposal Methods
+        /// <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);
+            }
+        }
+        #endregion
+    }
+}