public static Dictionary <Type, ElementProximity> ComputeProximity(Player self, List <Element> elements) { var closest = new Dictionary <Type, ElementProximity>(); foreach (var elem in elements) { if (elem.Id == self.Id) { continue; } ElementProximity proximity = null; Type type = elem.GetType(); // consolidate player types if (elem is Player) { type = typeof(Player); } // consolidate obstacle types if (elem is Obstacle) { type = typeof(Obstacle); } if (!closest.TryGetValue(type, out proximity)) { proximity = new ElementProximity() { Name = type.Name, Id = elem.Id, Distance = float.MaxValue }; closest.Add(type, proximity); } // calculate the distance between thees var distance = Collision.DistanceBetweenPoints(self.X, self.Y, elem.X, elem.Y); var angle = Collision.CalculateAngleFromPoint(self.X, self.Y, elem.X, elem.Y); // retain only the closest if (distance < proximity.Distance) { proximity.Id = elem.Id; proximity.Angle = angle; proximity.Distance = distance; } } return(closest); }
public override ActionEnum Action(List <Element> elements, float angleToCenter, bool inZone, ref float xdelta, ref float ydelta, ref float zdelta, ref float angle) { var playerCount = 0; float playerX = 0; float playerY = 0; bool shouldMelee = false; // find proximity to all types var closest = AITraining.ComputeProximity(this, elements); // gather details about if there is a crowd foreach (var elem in elements) { if (elem.Id == Id) { continue; // found myself } if (!(elem is Player)) { continue; // only care about players } playerCount++; playerX += elem.X; playerY += elem.Y; } // calculate the average center (if there are players near by) if (playerCount >= 1) { playerX /= (float)playerCount; playerY /= (float)playerCount; } // choose an action - set the rules in reverse order in order to set precedence var action = ActionEnum.None; xdelta = ydelta = angle = 0; if (PreviousAngle < 0) { // choose an angle at random PreviousAngle = Rand.Next() % 360; } angle = PreviousAngle; // 3) Shield if (Shield < Constants.MaxShield) { ElementProximity helmet = null; if (closest.TryGetValue(typeof(Shield), out helmet)) { // there is health either close or touching if (IsTouching(helmet, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = helmet.Id; } else { // choose action via another decision angle = helmet.Angle; } } } // 2) Health if (Health < Constants.MaxHealth) { ElementProximity bandage = null; if (closest.TryGetValue(typeof(Health), out bandage)) { // there is health either close or touching if (IsTouching(bandage, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = bandage.Id; } else { // choose action via another decision angle = bandage.Angle; } } } // 1) Have weapon if (Primary != null && Primary is RangeWeapon) { var weapon = Primary as RangeWeapon; ElementProximity ammo = null; // need ammo if (weapon.Ammo < MinAmmo && closest.TryGetValue(typeof(Ammo), out ammo)) { // there is ammo either close or touching if (IsTouching(ammo, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = ammo.Id; } else { // choose action via another decision angle = ammo.Angle; } } // needs reload if (!weapon.RoundsInClip(out int rounds) && rounds == 0 && weapon.HasAmmo()) { // choose to reload action = ActionEnum.Reload; // choose direction via another decision } ElementProximity ak47 = null; // pick up ak47 if (!(Primary is AK47) && closest.TryGetValue(typeof(AK47), out ak47)) { // there is an AK47 either close or touching if (IsTouching(ak47, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = ak47.Id; } else { // choose action via another decision angle = ak47.Angle; } } ElementProximity player = null; // shoot a player if (weapon.CanShoot() && closest.TryGetValue(typeof(Player), out player)) { // choose to shoot action = ActionEnum.Attack; // move towards the player angle = player.Angle; } } // 0) No weapon if (Primary == null || !(Primary is RangeWeapon)) { // AI does not have a weapon, so go ahead and melee (if no other action) shouldMelee = true; // 0.b if near a player, melee ElementProximity player = null; if (closest.TryGetValue(typeof(Player), out player)) { if (IsTouching(player, Fists.Distance)) { // choose to melee action = ActionEnum.Attack; // turn towards the player angle = player.Angle; } } // 0.a is there a weapon within view ElementProximity weapon = null; if (!closest.TryGetValue(typeof(AK47), out weapon)) { if (!closest.TryGetValue(typeof(Pistol), out weapon)) { if (!closest.TryGetValue(typeof(Shotgun), out weapon)) { } } } if (weapon != null) { // there is a weapon either close or touching if (IsTouching(weapon, Width / 2)) { // choose to pickup action = ActionEnum.Pickup; // set direction via another decision PreviousPickupId = weapon.Id; } else { // choose action via another decision angle = weapon.Angle; } } } // if there are too many players, then run away if (playerCount >= 5) { // choose an angle opposite from where the center of the other players are angle = Collision.CalculateAngleFromPoint(X, Y, playerX, playerY); // go the opposite way angle = (angle + 180) % 360; } // choose defaults if (action == ActionEnum.None) { // default to move action = ActionEnum.Move; } // check if we are in the Zone if (inZone) { // we should be moving towards the center if (action == ActionEnum.Move) { // eek we are in the zone, indicate that we should be moving towards the center angle = angleToCenter; } } // check if we seem to be stuck if (IsStuck()) { // take some corrective action if (ShowDiagnostics) { System.Diagnostics.Debug.WriteLine("AI seems stuck"); } // try something new angle = Rand.Next() % 360; CorrectiveAngle = 0; } // check if our last movement was obstructed float moveAngle = (angle + CorrectiveAngle) % 360; if (CorrectiveAngle > 0) { CorrectiveAngle -= 15; } if (CorrectiveAngle < 0) { CorrectiveAngle = 0; } // save angle for next time PreviousAngle = moveAngle; // set course float x1, y1, x2, y2; Collision.CalculateLineByAngle(X, Y, moveAngle, 1, out x1, out y1, out x2, out y2); xdelta = x2 - x1; ydelta = y2 - y1; // normalize var sum = (float)(Math.Abs(xdelta) + Math.Abs(ydelta)); xdelta = xdelta / sum; ydelta = ydelta / sum; if (Math.Abs(xdelta) + Math.Abs(ydelta) > 1.0001) { throw new Exception("Invalid xdelta,ydelta : " + xdelta + "," + ydelta); } if (ShowDiagnostics) { System.Diagnostics.Debug.WriteLine("AI {0} {1} {2} {3}", action, angle, xdelta, ydelta); } // if our action is to move... do a melee while moving if (action == ActionEnum.Move && shouldMelee) { return(ActionEnum.Attack); } else { return(action); } }
public bool IsTouching(ElementProximity element, float radius) { return(element.Distance < radius); }