Ejemplo n.º 1
0
        /// <summary>
        /// Helper function used by the <c>...RadiusTeleportEffect</c>s. Tests each tile in <paramref name="targetTiles"/> to ensure that <paramref name="unit"/> is capable of teleporting there, then adds valid tiles to the <paramref name="unit"/>'s movement range.
        /// </summary>
        /// <exception cref="UnmatchedMovementTypeException"></exception>
        protected void AddTeleportTargetsToUnitRange(Unit unit, IList <Tile> targetTiles)
        {
            IList <TerrainTypeMovementCostSetEffect> moveCostSets = unit.SkillList.Select(s => s.Effect).OfType <TerrainTypeMovementCostSetEffect>().ToList();

            foreach (Tile tile in targetTiles)
            {
                //Ensure that this unit can move to this tile
                int moveCost;
                if (!tile.TerrainTypeObj.MovementCosts.TryGetValue(unit.GetUnitMovementType(), out moveCost))
                {
                    throw new UnmatchedMovementTypeException(unit.GetUnitMovementType(), tile.TerrainTypeObj.MovementCosts.Keys.ToList());
                }

                //If unit is blocked from this tile, check for a skill that would allow it to access it
                if (moveCost == 99)
                {
                    TerrainTypeMovementCostSetEffect movCostSet = moveCostSets.FirstOrDefault(s => tile.TerrainTypeObj.Groupings.Contains(s.TerrainTypeGrouping));
                    if (movCostSet == null || !movCostSet.CanOverride99MoveCost)
                    {
                        continue;
                    }
                }

                //Check for an enemy unit already occupying this tile
                if (tile.UnitData.Unit != null && tile.UnitData.Unit.AffiliationObj.Grouping != unit.AffiliationObj.Grouping)
                {
                    continue;
                }

                //If no issues arose, add the tile to the unit's movement range
                if (!unit.Ranges.Movement.Contains(tile.Coordinate))
                {
                    unit.Ranges.Movement.Add(tile.Coordinate);
                }
            }
        }
Ejemplo n.º 2
0
        private void RecurseUnitRange(UnitRangeParameters parms, IList <MovementCoordSet> currCoords, string visitedCoords, Coordinate lastWarpUsed)
        {
            //Base case
            //Don't exceed the maximum range and don't go off the map
            if (currCoords.Any(c => c.RemainingMov < 0 ||
                               c.Coordinate.X < 1 ||
                               c.Coordinate.Y < 1 ||
                               c.Coordinate.X > this.Map.TileWidth ||
                               c.Coordinate.Y > this.Map.TileHeight)
                )
            {
                return;
            }

            for (int i = 0; i < currCoords.Count; i++)
            {
                MovementCoordSet currCoord = currCoords[i];
                Tile             tile      = this.Map.GetTileByCoord(currCoords[i].Coordinate);

                //If there is a Unit occupying this tile, check for affiliation collisions
                //Check if this tile blocks units of a certain affiliation
                if (UnitIsBlocked(parms.Unit, tile.UnitData.Unit, parms.IgnoresAffiliations) ||
                    (tile.TerrainTypeObj.RestrictAffiliations.Any() && !tile.TerrainTypeObj.RestrictAffiliations.Contains(parms.Unit.AffiliationObj.Grouping))
                    )
                {
                    return;
                }

                //Test that the unit can move to this tile
                int moveCost;
                if (!tile.TerrainTypeObj.MovementCosts.TryGetValue(parms.Unit.GetUnitMovementType(), out moveCost))
                {
                    throw new UnmatchedMovementTypeException(parms.Unit.GetUnitMovementType(), tile.TerrainTypeObj.MovementCosts.Keys.ToList());
                }

                //Apply movement cost modifiers
                TerrainTypeMovementCostSetEffect      movCostSet  = parms.MoveCostSets.FirstOrDefault(s => tile.TerrainTypeObj.Groupings.Contains(s.TerrainTypeGrouping));
                TerrainTypeMovementCostModifierEffect moveCostMod = parms.MoveCostModifiers.FirstOrDefault(s => tile.TerrainTypeObj.Groupings.Contains(s.TerrainTypeGrouping));
                if (movCostSet != null && (movCostSet.CanOverride99MoveCost || moveCost < 99))
                {
                    moveCost = movCostSet.Value;
                }
                else if (moveCostMod != null && moveCost < 99)
                {
                    moveCost += moveCostMod.Value;
                }

                if (tile.UnitData.Unit != null && tile.UnitData.Unit.AffiliationObj.Grouping == parms.Unit.AffiliationObj.Grouping && moveCost < 99)
                {
                    //If tile is occupied by an ally, test if they have a skill that sets the move cost
                    //Only applies if value is less than natural move cost for unit
                    OriginAllyMovementCostSetEffect allyMovCostSet = tile.UnitData.Unit.SkillList.Select(s => s.Effect).OfType <OriginAllyMovementCostSetEffect>().FirstOrDefault();
                    if (allyMovCostSet != null && allyMovCostSet.MovementCost < moveCost)
                    {
                        moveCost = allyMovCostSet.MovementCost;
                    }
                }

                //Min/max value enforcement
                moveCost = Math.Max(0, moveCost);
                if (moveCost >= 99)
                {
                    return;
                }

                //Don't check or subtract move cost for the starting tile
                if (visitedCoords.Length > 0)
                {
                    if (moveCost > currCoord.RemainingMov)
                    {
                        return;
                    }
                    currCoord.RemainingMov = currCoord.RemainingMov - moveCost;
                }
            }

            //If none of the tiles fail the move cost checks above...
            //Add current anchor tile to visited coords
            visitedCoords += "_" + currCoords.First().Coordinate.ToString() + "_";

            //Document tile movement
            IList <Tile> tiles = currCoords.Select(c => this.Map.GetTileByCoord(c.Coordinate)).ToList();

            if (!tiles.Any(t => t.TerrainTypeObj.CannotStopOn))
            {
                foreach (Tile tile in tiles)
                {
                    if (!parms.Unit.Ranges.Movement.Contains(tile.Coordinate))
                    {
                        parms.Unit.Ranges.Movement.Add(tile.Coordinate);
                    }
                }
            }

            //Units may move onto obstructed tiles, but no further.
            if (tiles.Any(t => UnitIsBlocked(parms.Unit, t.UnitData.ObstructingUnits, parms.IgnoresAffiliations)))
            {
                return;
            }


            //Navigate in each cardinal direction, do not repeat tiles in this path
            //Left
            //Coordinate left = new Coordinate(currCoord.X - 1, currCoord.Y);
            IList <MovementCoordSet> left = currCoords.Select(c => new MovementCoordSet(c.RemainingMov, new Coordinate(c.Coordinate.X - 1, c.Coordinate.Y))).ToList();

            if (!visitedCoords.Contains("_" + left.First().Coordinate.ToString() + "_"))
            {
                RecurseUnitRange(parms, left, visitedCoords, lastWarpUsed);
            }

            //Right
            //Coordinate right = new Coordinate(currCoord.X + 1, currCoord.Y);
            IList <MovementCoordSet> right = currCoords.Select(c => new MovementCoordSet(c.RemainingMov, new Coordinate(c.Coordinate.X + 1, c.Coordinate.Y))).ToList();

            if (!visitedCoords.Contains("_" + right.First().Coordinate.ToString() + "_"))
            {
                RecurseUnitRange(parms, right, visitedCoords, lastWarpUsed);
            }

            //Up
            //Coordinate up = new Coordinate(currCoord.X, currCoord.Y - 1);
            IList <MovementCoordSet> up = currCoords.Select(c => new MovementCoordSet(c.RemainingMov, new Coordinate(c.Coordinate.X, c.Coordinate.Y - 1))).ToList();

            if (!visitedCoords.Contains("_" + up.First().Coordinate.ToString() + "_"))
            {
                RecurseUnitRange(parms, up, visitedCoords, lastWarpUsed);
            }

            //Down
            //Coordinate down = new Coordinate(currCoord.X, currCoord.Y + 1);
            IList <MovementCoordSet> down = currCoords.Select(c => new MovementCoordSet(c.RemainingMov, new Coordinate(c.Coordinate.X, c.Coordinate.Y + 1))).ToList();

            if (!visitedCoords.Contains("_" + down.First().Coordinate.ToString() + "_"))
            {
                RecurseUnitRange(parms, down, visitedCoords, lastWarpUsed);
            }


            //If any tile is a warp entrance, calculate the remaining range from each warp exit too.
            IEnumerable <Tile> warps = tiles.Where(t => t.TerrainTypeObj.WarpType == WarpType.Entrance || t.TerrainTypeObj.WarpType == WarpType.Dual &&
                                                   (lastWarpUsed == null || t.Coordinate != lastWarpUsed));

            foreach (Tile warp in warps)
            {
                //Calculate warp cost
                WarpMovementCostSetEffect      warpCostSet = parms.WarpCostSets.FirstOrDefault(s => warp.TerrainTypeObj.Groupings.Contains(s.TerrainTypeGrouping));
                WarpMovementCostModifierEffect warpCostMod = parms.WarpCostModifiers.FirstOrDefault(s => warp.TerrainTypeObj.Groupings.Contains(s.TerrainTypeGrouping));

                int warpCost = warp.TerrainTypeObj.WarpCost;
                if (warpCostSet != null)
                {
                    warpCost = warpCostSet.Value;
                }
                else if (warpCostMod != null)
                {
                    warpCost += warpCostMod.Value;
                }

                if (warpCost < 0)
                {
                    warpCost = 0;
                }

                foreach (Tile warpExit in warp.WarpData.WarpGroup.Where(t => warp.Coordinate != t.Coordinate && (t.TerrainTypeObj.WarpType == WarpType.Exit || t.TerrainTypeObj.WarpType == WarpType.Dual)))
                {
                    //Calculate range from warp exit in all possible unit orientations, starting with the anchor tile
                    Coordinate currAnchor = currCoords.First().Coordinate;
                    for (int y = 0; y < parms.Unit.Location.UnitSize; y++)
                    {
                        for (int x = 0; x < parms.Unit.Location.UnitSize; x++)
                        {
                            RecurseUnitRange(parms,
                                             currCoords.Select(c => new MovementCoordSet(c.RemainingMov - warpCost, new Coordinate(warpExit.Coordinate.X + Math.Abs(currAnchor.X - c.Coordinate.X) - x, warpExit.Coordinate.Y + Math.Abs(currAnchor.Y - c.Coordinate.Y) - y))).ToList(),
                                             string.Empty,
                                             warpExit.Coordinate);
                        }
                    }
                }
            }
        }