123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- #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 = null;
- #if ANDROID
- using(var stream = Game.Activity.Assets.Open(@"Content/Textures/Catapults/AnimationsDef.xml"))
- {
- doc = XDocument.Load(stream);
- }
- #else
- doc = XDocument.Load("Content/Textures/Catapults/AnimationsDef.xml");
- #endif
- 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 = Math.Min(aimAnimation.FrameCount, 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
- }
- }
|