private void RecurseItemRange(ItemRangeParameters parms, Coordinate currCoord, int remainingRange, string visitedCoords, ref IList <Coordinate> atkRange, ref IList <Coordinate> utilRange) { //Base case //Don't exceed the maximum range and don't go off the map if (remainingRange < 0 || currCoord.X < 1 || currCoord.Y < 1 || currCoord.X > this.Map.TileWidth || currCoord.Y > this.Map.TileHeight ) { return; } Tile tile = this.Map.GetTileByCoord(currCoord); //Check if ranges can pass through this tile if (tile.TerrainTypeObj.BlocksItems) { return; } visitedCoords += "_" + currCoord.ToString() + "_"; //Check for items that can reach this tile if (!parms.Unit.Ranges.Movement.Contains(currCoord)) { int horzDisplacement = Math.Abs(currCoord.X - parms.StartCoord.X); int verticalDisplacement = Math.Abs(currCoord.Y - parms.StartCoord.Y); int totalDisplacement = currCoord.DistanceFrom(parms.StartCoord); int pathLength = parms.LargestRange - remainingRange; List <UnitItemRange> validRanges = new List <UnitItemRange>(); validRanges.AddRange(parms.Ranges.Where(r => r.Shape == ItemRangeShape.Standard && ((r.MinRange <= totalDisplacement && //tile greater than min range away from unit r.MinRange <= pathLength && //tile greater than min range down the path r.MaxRange >= totalDisplacement && //tile less than max range from unit r.MaxRange >= pathLength) || //tile less than max range down path (totalDisplacement == 1 && pathLength == 1 && r.AllowMeleeRange) //unit can specially allow melee range for an item ) )); validRanges.AddRange(parms.Ranges.Where(r => r.Shape == ItemRangeShape.Square && (((r.MinRange <= verticalDisplacement || r.MinRange <= horzDisplacement) && r.MaxRange >= verticalDisplacement && r.MaxRange >= horzDisplacement) || (totalDisplacement == 1 && pathLength == 1 && r.AllowMeleeRange) //unit can specially allow melee range for an item ))); validRanges.AddRange(parms.Ranges.Where(r => (r.Shape == ItemRangeShape.Cross || r.Shape == ItemRangeShape.Star) && (((horzDisplacement == 0 && //tile vertically within range r.MinRange <= verticalDisplacement && r.MaxRange >= verticalDisplacement) || (verticalDisplacement == 0 && //tile horizontally within range r.MinRange <= horzDisplacement && r.MaxRange >= horzDisplacement) && totalDisplacement == pathLength) || //straight paths only (totalDisplacement == 1 && pathLength == 1 && r.AllowMeleeRange) ) )); validRanges.AddRange(parms.Ranges.Where(r => (r.Shape == ItemRangeShape.Saltire || r.Shape == ItemRangeShape.Star) && ((horzDisplacement == verticalDisplacement && r.MinRange <= verticalDisplacement && r.MaxRange >= verticalDisplacement && r.MinRange <= horzDisplacement && r.MaxRange >= horzDisplacement && totalDisplacement == pathLength) || //straight paths only (totalDisplacement == 1 && pathLength == 1 && r.AllowMeleeRange) ) )); //Add to attacking range if (validRanges.Any(r => r.DealsDamage) && !atkRange.Contains(currCoord)) { atkRange.Add(currCoord); } //Add to util range else if (validRanges.Any(r => !r.DealsDamage) && !utilRange.Contains(currCoord)) { utilRange.Add(currCoord); } } //Navigate in each cardinal direction, do not repeat tiles in this path //Left if (parms.RangeDirection == ItemRangeDirection.Northwest || parms.RangeDirection == ItemRangeDirection.Southwest) { Coordinate left = new Coordinate(currCoord.X - 1, currCoord.Y); if (!visitedCoords.Contains("_" + left.ToString() + "_")) { RecurseItemRange(parms, left, remainingRange - 1, visitedCoords, ref atkRange, ref utilRange); } } //Right if (parms.RangeDirection == ItemRangeDirection.Northeast || parms.RangeDirection == ItemRangeDirection.Southeast) { Coordinate right = new Coordinate(currCoord.X + 1, currCoord.Y); if (!visitedCoords.Contains("_" + right.ToString() + "_")) { RecurseItemRange(parms, right, remainingRange - 1, visitedCoords, ref atkRange, ref utilRange); } } //Up if (parms.RangeDirection == ItemRangeDirection.Northwest || parms.RangeDirection == ItemRangeDirection.Northeast) { Coordinate up = new Coordinate(currCoord.X, currCoord.Y - 1); if (!visitedCoords.Contains("_" + up.ToString() + "_")) { RecurseItemRange(parms, up, remainingRange - 1, visitedCoords, ref atkRange, ref utilRange); } } //Down if (parms.RangeDirection == ItemRangeDirection.Southwest || parms.RangeDirection == ItemRangeDirection.Southeast) { Coordinate down = new Coordinate(currCoord.X, currCoord.Y + 1); if (!visitedCoords.Contains("_" + down.ToString() + "_")) { RecurseItemRange(parms, down, remainingRange - 1, visitedCoords, ref atkRange, ref utilRange); } } }
public void CalculateUnitRanges() { foreach (Unit unit in this.Units) { try { //Ignore hidden units if (!unit.Location.OriginTiles.Any()) { continue; } //Calculate movement range int movementVal = unit.Stats.General[this.Map.Constants.UnitMovementStatName].FinalValue; OverrideMovementEffect overrideMovEffect = unit.StatusConditions.Select(s => s.StatusObj.Effect).OfType <OverrideMovementEffect>().FirstOrDefault(); if (overrideMovEffect != null) { movementVal = overrideMovEffect.MovementValue; } UnitRangeParameters unitParms = new UnitRangeParameters(unit); RecurseUnitRange(unitParms, unit.Location.OriginTiles.Select(o => new MovementCoordSet(movementVal, o.Coordinate)).ToList(), string.Empty, null); //Calculate item ranges IList <Coordinate> atkRange = new List <Coordinate>(); IList <Coordinate> utilRange = new List <Coordinate>(); IList <UnitItemRange> itemRanges = unit.Inventory.Where(i => i != null && i.CanEquip && !i.IsUsePrevented && (i.ModifiedMinRangeValue > 0 || i.ModifiedMaxRangeValue > 0)) .Select(i => new UnitItemRange(i.ModifiedMinRangeValue, i.ModifiedMaxRangeValue, i.Item.Range.Shape, i.Item.DealsDamage, i.AllowMeleeRange)) .ToList(); //Check for whole map ranges if (itemRanges.Any(r => r.MaxRange >= 99)) { bool applyAtk = itemRanges.Any(r => r.DealsDamage && r.MaxRange >= 99); bool applyUtil = itemRanges.Any(r => !r.DealsDamage && r.MaxRange >= 99); ApplyWholeMapItemRange(unit, applyAtk, applyUtil, ref atkRange, ref utilRange); //Remove all relevant ranges from list //Since we cover the whole map we don't need to address these individually later if (applyAtk) { while (itemRanges.Any(r => r.DealsDamage)) { itemRanges.Remove(itemRanges.First(r => r.DealsDamage)); } } if (applyUtil) { while (itemRanges.Any(r => !r.DealsDamage)) { itemRanges.Remove(itemRanges.First(r => !r.DealsDamage)); } } } //Check for regular ranges if (itemRanges.Any()) { foreach (Coordinate coord in unit.Ranges.Movement) { foreach (ItemRangeDirection direction in RANGE_DIRECTIONS) { //Calculate attack range ItemRangeParameters rangeParms = new ItemRangeParameters(unit, coord, itemRanges, direction); RecurseItemRange(rangeParms, coord, rangeParms.LargestRange, string.Empty, ref atkRange, ref utilRange ); } } } unit.Ranges.Attack = atkRange; unit.Ranges.Utility = utilRange; } catch (Exception ex) { throw new RangeCalculationException(unit, ex); } } }