/// <summary> /// Adds an Ice Spike status animation linked to the Entity's position. /// </summary> /// <param name="statusAnims">The Entity's list of Status Animations.</param> /// <param name="pos">The Entity's position.</param> private void HandleFreezeEffect(CStatusAnimations statusAnims, CPosition pos) { CStatusAnimation newStatusAnim; Bitmap bmp; Animation anim; AnimationScript animScript; float xOffset; float yOffset; if (pos.Width <= 21) { bmp = SwinGame.BitmapNamed("SmallIceSpike"); } else { bmp = SwinGame.BitmapNamed("BigIceSpike"); } xOffset = (pos.Width / 2) - (bmp.CellWidth / 2); yOffset = pos.Height - bmp.CellHeight; anim = SwinGame.CreateAnimation("Freeze", SwinGame.AnimationScriptNamed("IceSpikeAnim")); animScript = SwinGame.AnimationScriptNamed("IceSpikeAnim"); newStatusAnim = new CStatusAnimation(typeof(CFrozen), xOffset, yOffset, bmp, anim, animScript); statusAnims.Anims.Add(newStatusAnim); }
/// <summary> /// Adds a Poison Cloud status animation linked to the Entity's position. /// </summary> /// <param name="statusAnims">The Entity's list of Status Animations.</param> /// <param name="pos">The Entity's position.</param> private void HandlePoisonEffect(CStatusAnimations statusAnims, CPosition pos) { CStatusAnimation newStatusAnim; Bitmap bmp; Animation anim; AnimationScript animScript; float xOffset; float yOffset; if (pos.Width <= 21) { bmp = SwinGame.BitmapNamed("SmallPoisonCloud"); } else { bmp = SwinGame.BitmapNamed("BigPoisonCloud"); } xOffset = (pos.Width / 2) - (bmp.CellWidth / 2); yOffset = 0; anim = SwinGame.CreateAnimation("Poison", SwinGame.AnimationScriptNamed("PoisonZoneAnim")); animScript = SwinGame.AnimationScriptNamed("PoisonZoneAnim"); newStatusAnim = new CStatusAnimation(typeof(CPoison), xOffset, yOffset, bmp, anim, animScript); statusAnims.Anims.Add(newStatusAnim); }
/// <summary> /// Creates a Circle to represent the attack radius of the AI. If the Position Component of the /// AI's target is within this Circle, then the AI is in range. /// </summary> /// <param name="entID">The Entity to check range for.</param> /// <param name="AI">AI component of the Entity to check range for.</param> /// <param name="pos">Position component of the Entity to check range for.</param> protected void CheckRange(ulong entID, CAI AI, CPosition pos) { Circle attackRadius = SwinGame.CreateCircle(pos.Centre.X, pos.Centre.Y, AI.Range + (pos.Width / 2)); CPosition targetPos = World.GetComponent <CPosition>(AI.TargetID); AI.IsInRange = SwinGame.CircleRectCollision(attackRadius, targetPos.Rect); }
/// <summary> /// Creates an Entity with all Components of a Freeze Zone and adds it to the World. /// This Entity represents the Freeze Zone created when a Freezing Bullet reaches its target. /// </summary> /// <param name="pos">The position the Freeze Zone occupies.</param> public static void CreateFreezeZone(CPosition pos) { //Create Entity and add to world. ulong newEntity = _world.NextEntityID; /// <summary> /// Adjust position for Sprite size so Freeze Zone spawns at centre of bullet. /// </summary> float atX = pos.X - (FREEZE_ZONE_SIZE / 2); float atY = pos.Y - (FREEZE_ZONE_SIZE / 2); /// <summary> /// Creates a new Entity with just an Animation component to be processed by the Animation Rendering System. /// </summary> Bitmap bmp = SwinGame.BitmapNamed("FreezingBulletSplash"); Animation anim = SwinGame.CreateAnimation("Splash", SwinGame.AnimationScriptNamed("FreezingBulletSplashAnim")); AnimationScript animScript = SwinGame.AnimationScriptNamed("FreezingBulletSplashAnim"); CreateAnimationEntity(atX, atY, anim, bmp, animScript); //Create components and pass to world to send to Systems List <Component> components = new List <Component>(); components.Add(new CPlayerTeam()); components.Add(new CPosition(pos.Centre.X - (FREEZE_ZONE_SIZE / 2), pos.Centre.Y - (FREEZE_ZONE_SIZE / 2), FREEZE_ZONE_SIZE)); components.Add(new CCollidable()); components.Add(new CAppliesDebuff()); components.Add(new CFrozen(FREEZING_BULLET_FREEZE_DURATION, 0)); _world.AddEntity(newEntity, components); }
/// <summary> /// Adds the passed in Entity to each Spatial Hash Player cell it resides in. /// </summary> /// <param name="entID">The Entity to add to Player cells.</param> private void AddEntityToCells(ulong entID, Dictionary <int, List <ulong> > cells) { CPosition pos = World.GetComponent <CPosition>(entID); List <int> cellsEntityIsIn; if (pos.Width > _cellSize || pos.Height > _cellSize) { cellsEntityIsIn = GetCellsBigEntityIsIn(pos); } else { cellsEntityIsIn = GetCellsNormalSizeEntityIsIn(pos); } foreach (int i in cellsEntityIsIn) { if (i >= 0 && i < _numCells) { if (!cells[i].Contains(entID)) { cells[i].Add(entID); } } } }
/// <summary> /// Attemps to create a Freezing Bullet heading towards the passed in point. /// This will only occur if the Ability is off cooldown. /// </summary> /// <param name="pt">The point the Freezing Bullet will head towards.</param> public void CastFreezingBullet(Point2D pt) { CPlayer player = World.GetComponent <CPlayer>(PLAYER_ENTITY_ID); if (AbilityIsReady(player.TimeOfLastFreezingBullet, FREEZING_BULLET_COOLDOWN)) { CPosition playerPos = World.GetComponent <CPosition>(PLAYER_ENTITY_ID); EntityFactory.CreateFreezingBullet(playerPos.Centre.X, playerPos.Centre.Y, pt.X, pt.Y); player.TimeOfLastFreezingBullet = World.GameTime; } }
/// <summary> /// Checks each corner of the passed in Position Component and adds the corresponding /// cell ID to a List of cell IDs the Entity resides in. /// </summary> /// <returns>A List of cell IDs the Entity resides in.</returns> /// <param name="pos">The Position Component of the Entity.</param> private List <int> GetCellsNormalSizeEntityIsIn(CPosition pos) { List <int> result = new List <int>(); result.Add(CellAt(pos.X, pos.Y)); //Top Left result.Add(CellAt(pos.X + pos.Width, pos.Y)); //Top Right result.Add(CellAt(pos.X, pos.Y + pos.Height)); //Bottom Left result.Add(CellAt(pos.X + pos.Width, pos.Y + pos.Height)); //Bottom Right return(result); }
/// <summary> /// Evaluates the Player AI's current position against the positions of each Enemy Entity. /// The Enemy which is closest to the Player AI becomes the Player AI's target. /// </summary> /// <param name="playerAI">The Player AI Entity's AI Component.</param> /// <param name="playerPos">The Player AI Entity's Position Component.</param> private void GetClosestTarget(CAI playerAI, CPosition playerPos) { /// <summary> /// How far away the Enemy Entity is on the x-axis. /// </summary> float xOffset; /// <summary> /// How far away the Enemy Entity is on the y-axis. /// </summary> float yOffset; /// <summary> /// The direct diagonal distance from the Enemy Entity. /// </summary> float distance; /// <summary> /// The Enemy Entity closest to the Player AI. This will become the AI's target. /// </summary> ulong closestTarget; /// <summary> /// Represents the Entities to be considered as targets and their distance from the Player AI. /// </summary> Dictionary <ulong, float> _targetDistances = new Dictionary <ulong, float>(); foreach (KeyValuePair <ulong, Point2D> enemy in _enemyPositions) { xOffset = enemy.Value.X - playerPos.X; yOffset = enemy.Value.Y - playerPos.Y; distance = (float)Math.Sqrt((xOffset * xOffset) + (yOffset * yOffset)); //Only consider Entities which are in range for the AI to attack. if (distance <= playerAI.Range) { _targetDistances.Add(enemy.Key, distance); } } //If there are targets to consider. if (_targetDistances.Count > 0) { /// <summary> /// Returns the corresponding key for the smallest value in the dictionary. /// </summary> closestTarget = _targetDistances.Aggregate((l, r) => l.Value < r.Value ? l : r).Key; playerAI.TargetID = closestTarget; playerAI.HasTarget = true; } }
/// <summary> /// Creates an Entity with all Components of an Arrow and adds it to the World. /// This Entity represents the Arrows used by Archers of both teams. /// </summary> /// <param name="x">The x coordinate where the Entity will be created.</param> /// <param name="y">The y coordinate where the Entity will be created.</param> /// <param name="bow">The bow determining speed and damage of the Arrow.</param> /// <param name="targetPos">The position of the Entity the Arrow is heading towards.</param> /// <typeparam name="T">The team the Arrow belongs to</typeparam> public static void CreateArrow <T>(float x, float y, CBow bow, CPosition targetPos) where T : CTeam { //Create Entity and add to world ulong newEntity = _world.NextEntityID; //Create components and pass to world to send to Systems List <Component> components = new List <Component>(); components.Add(new CPosition(x, y, ARROW_SIZE)); components.Add(new CDamage(bow.ArrowDamage)); components.Add(new CCollidable()); components.Add(new CProjectile()); components.Add(new CDamagesOnImpact(true)); //Calculate the centre of the passed in position to be the Arrow's target. float targetCentreX = targetPos.X + (targetPos.Width / 2); float targetCentreY = targetPos.Y + (targetPos.Height / 2); //Calculate appropriate velocities for Arrow to reach target. Vector velocity = Utils.GetVectorBetweenPoints(x, y, targetCentreX, targetCentreY, bow.ArrowSpeed); components.Add(new CVelocity(velocity.X, velocity.Y, bow.ArrowSpeed)); if (typeof(T) == typeof(CPlayerTeam)) { components.Add(new CPlayerTeam()); components.Add(new CRenderable(SwinGame.BitmapNamed("PlayerArrow"))); } else if (typeof(T) == typeof(CEnemyTeam)) { components.Add(new CEnemyTeam()); components.Add(new CRenderable(SwinGame.BitmapNamed("Arrow"))); } _world.AddEntity(newEntity, components); }
/// <summary> /// Checks the Entity in chunks equal to _cellSize and adds the corresponding /// cell ID to a list of cell IDs the Entity resides in. /// </summary> /// <returns>The cells the Entity is in.</returns> /// <param name="pos">The position of the Entity.</param> private List <int> GetCellsBigEntityIsIn(CPosition pos) { List <int> result = new List <int>(); float x; float y; int numXCells = (int)Math.Ceiling((double)pos.Width / _cellSize); int numYCells = (int)Math.Ceiling((double)pos.Height / _cellSize); for (int i = 0; i < numXCells; i++) { //If not checking final X-Axis cell if (i < numXCells - 1) { x = pos.X + (i * _cellSize); } else { x = pos.X + pos.Width; //Check right edge } for (int j = 0; j < numYCells; j++) { //If not checking final Y-Axis cell if (j < numYCells - 1) { y = pos.Y + (j * _cellSize); } else { y = pos.Y + pos.Height; //Check bottom edge } result.Add(CellAt(x, y)); } } return(result); }
/// <summary> /// Specifies whether or not the Freezing Bullet has reached its target. /// </summary> /// <returns><c>true</c> if the Freezing Bullet has reached its target, <c>false</c> otherwise.</returns> /// <param name="bulletPos">The position of the Freezing Bullet.</param> /// <param name="bullet">The Freezing Bullet component, contains target coordinates.</param> private bool ReachedTarget(CPosition bulletPos, CFreezingBullet bullet) { return(SwinGame.PointInRect(bullet.TargetX, bullet.TargetY, bulletPos.Rect)); }
/// <summary> /// Specifies whether or not the projectile is within the boundaries of the screen. /// </summary> /// <returns><c>true</c>, if the projectile is on the screen, <c>false</c> otherwise.</returns> /// <param name="pos">The projectile's position.</param> protected bool ProjectileOnScreen(CPosition pos) { return(SwinGame.RectOnScreen(pos.Rect)); }
/// <summary> /// Indicates whether or not two Entities are colliding, according to their Position Components. /// </summary> /// <returns><c>true</c> if the Entities are colliding, <c>false</c> otherwise.</returns> /// <param name="p1">The first Entity's Position Component.</param> /// <param name="p2">The second Entity's Position Component.</param> private bool AreColliding(CPosition p1, CPosition p2) { return(SwinGame.RectanglesIntersect(p1.Rect, p2.Rect)); }