/// <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> /// 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> /// Checks the Game Time against the last time the AI attacked. If the time difference is greater /// than the attack cooldown of the AI, then the AI is ready to attack and their Attack animation begins. /// </summary> /// <param name="entID">Ent identifier.</param> protected void CheckCooldown(ulong entID) { CAI AI = World.GetComponent <CAI>(entID); AI.AttackIsReady = World.GameTime - AI.LastAttackTime >= AI.Cooldown; if (AI.AttackIsReady) { /// <summary> /// Begin the Attack animation for the AI /// </summary> CAnimation anim = World.GetComponent <CAnimation>(entID); SwinGame.AssignAnimation(anim.Anim, "Attack", anim.AnimScript); } }
/// <summary> /// Performs an Attack for the specified Entity. The Entity's /// attack Type is evaluated and the appropriate attack is performed. /// </summary> /// <param name="entID">The Attacking Entity.</param> /// <typeparam name="T">The Team the Entity belongs to.</typeparam> protected void Attack <T>(ulong entID) where T : CTeam { CAI AI = World.GetComponent <CAI>(entID); CDamage damage; CPosition pos; CPosition targetPos; /// <summary> /// The AI has just attacked, so its cooldown is started. /// </summary> AI.LastAttackTime = World.GameTime; AI.AttackIsReady = false; /// <summary> /// If the Attack Type is Melee, register an attack with the Damage System. /// If the Attack Type is Gun, create an Arrow Entity to travel towards the Target. /// </summary> switch (AI.AttackType) { case AttackType.Melee: { /// <summary> /// The System where Attacks are registered. /// </summary> DamageSystem damageSystem = World.GetSystem <DamageSystem>(); damage = World.GetComponent <CDamage>(entID); damageSystem.RegisterAttack(AI.TargetID, damage.Damage); break; } case AttackType.Bow: { pos = World.GetComponent <CPosition>(entID); CBow bow = World.GetComponent <CBow>(entID); targetPos = World.GetComponent <CPosition>(AI.TargetID); EntityFactory.CreateArrow <T>(pos.Centre.X, pos.Centre.Y, bow, targetPos); break; } } }