예제 #1
0
파일: Axe.cs 프로젝트: GBudee/deft-Sample
        protected override List <IUnitController> CheckHit(List <HexEntry> recentPath)
        {
            if (recentPath.Count < 2)
            {
                return(new List <IUnitController>());
            }

            HexEntry current = recentPath[recentPath.Count - 1];
            HexEntry last    = recentPath[recentPath.Count - 2];

            Vector2 displacement = current.BoardPos - last.BoardPos;
            Vector2 targetPos1   = current.BoardPos + HexVectorUtil.RotateClockwise(displacement);
            Vector2 targetPos2   = current.BoardPos + HexVectorUtil.RotateCounterclockwise(displacement);

            List <IUnitController> results = new List <IUnitController>();

            if (scenarioLoader.HexGrid.ContainsKey(targetPos1))
            {
                results.Add(scenarioLoader.HexGrid[targetPos1].SimOccupant);
            }
            if (scenarioLoader.HexGrid.ContainsKey(targetPos2))
            {
                results.Add(scenarioLoader.HexGrid[targetPos2].SimOccupant);
            }
            return(results);
        }
예제 #2
0
        public static List<Outcome> ChooseBestPath(List<List<HexEntry>> allPaths, HexEntry dest, IUnitController unitController,
                Func<List<HexEntry>, List<Outcome>> SimulatePath) {

            // Make sure that there actually is at least one path.
            var paths = allPaths.Where(x => x.Last() == dest);
            if (!paths.Any()) {
                return new List<Outcome>();
            }

            // Get the outcomes, then preferentially order them:
            // 1. descending # of enemies attacked
            // 2. ascending # of enemies retaliating
            // 3. ascending ZOC cost
            var bestOutcomes = paths
                .Select(x => SimulatePath(x))
                .OrderByDescending(x => (x.Last().position == dest) ? 1 : 0) // Does the unit reach the destination without dying or otherwise being prevented
                .ThenBy(x =>
                    x.Aggregate(0, (acc, y) => acc - y.combat
                     .Where(z => (z.attackType == AttackType.Melee || z.attackType == AttackType.Push) && z.target.PlayerOwner != z.source.PlayerOwner).ToList().Count // Maximize the number of times the unit attacks enemies
                     + y.combat.Where(z => (z.attackType == AttackType.Melee || z.attackType == AttackType.Push) && z.target.PlayerOwner == z.source.PlayerOwner).ToList().Count)) // Maximize the number of times the unit attacks allies
                .ThenBy(x =>
                    x.Aggregate(0, (acc, y) => acc + y.combat
                     .Where(z => z.attackType == AttackType.Retal).ToList().Count)) // Minimize the number of times the unit receives retal
                .ThenBy(x => zoneOfControlExpense(x)) // Try not to consume the unit's movement by getting ZoCed
                .ThenBy(x => movePointExpense(x, unitController)); // Try to minimize mp consumption from terrain
                    
            // The best one is now at the top
            return bestOutcomes.First();
        }
예제 #3
0
파일: Spear.cs 프로젝트: GBudee/deft-Sample
        // First-strike property is defined within EmptyWeapon

        protected override List <IUnitController> CheckHit(List <HexEntry> recentPath)
        {
            if (recentPath.Count < 2)
            {
                return(new List <IUnitController>());
            }

            HexEntry current = recentPath[recentPath.Count - 1];
            HexEntry last    = recentPath[recentPath.Count - 2];

            Vector2 displacement = current.BoardPos - last.BoardPos;
            Vector2 targetPos    = displacement + current.BoardPos;

            if (!scenarioLoader.HexGrid.ContainsKey(targetPos))
            {
                return(new List <IUnitController>());
            }
            else
            {
                return(new List <IUnitController>()
                {
                    scenarioLoader.HexGrid[targetPos].SimOccupant
                });
            }
        }
예제 #4
0
        private void MapDistancesToGoal()
        {
            HexEntry goalHex = null;

            foreach (HexEntry hex in scenarioLoader.HexGrid.Values)
            {
                if (hex.Terrain == Terrain.Goal)
                {
                    goalHex = hex;
                }

                hex.aiDistanceToGoal = int.MaxValue;
            }

            if (goalHex == null)
            {
                Debug.Log("No goal");
                return;
            }

            foreach (HexEntry hex in scenarioLoader.HexGrid.Values)
            {
                if (hex != goalHex)
                {
                    hex.aiDistanceToGoal = (int)HexVectorUtil.AxialDistance(goalHex.BoardPos, hex.BoardPos);
                }
                else
                {
                    hex.aiDistanceToGoal = -10; // Used because the distance is a direct weighting factor
                }
            }
        }
예제 #5
0
        public void TravelToHex(HexEntry hex)
        {
            if (_unitSelected != null)
            {
                if (hex.Occupant == null && selectionReachableHexes.Contains(hex))
                {
                    List <Outcome> outcomes = outcomeCache[hex];

                    outcomeExecutor.ExecuteMoves(outcomes, _unitSelected); // Immediately update all internal values to reflect the outcomes of the move
                    boardStateHistory.Add(CreateBoardState(outcomes));

                    outcomeAnimator.Interpret(outcomes); // Launch coroutine to animate move outcomes sequentially

                    if (_unitSelected.HP <= 0)
                    {
                        UnitSelected = null;
                    }
                    else
                    {
                        UnitSelected = _unitSelected;
                    }
                }
                else
                {
                    UnitSelected = null;
                }
            }
            else
            {
                Debug.Log("No unit selected");
            }
        }
예제 #6
0
 public void HidePathToHex(HexEntry hex)
 {
     if (_unitSelected != null)
     {
         outcomeVisualizer.Revert();
     }
 }
예제 #7
0
        public void ShowPathToHex(HexEntry hex)
        {
            if (_unitSelected != null)
            {
                List <Outcome> outcomes;

                if (outcomeCache.ContainsKey(hex))
                {
                    outcomes = outcomeCache[hex];
                }
                else
                {
                    outcomes = _unitSelected.Mover.MoveOutcomes(hex);
                    outcomeCache.Add(hex, outcomes);
                }
                if (outcomes.Count > 0 && outcomes[outcomes.Count - 1].position == hex)
                {
                    _unitSelected.SpriteManager.ShowExtraInfo(UnitSpriteManager.UnitInfoDisplaySource.Selection, true, hex);
                }
                else
                {
                    _unitSelected.SpriteManager.ShowExtraInfo(UnitSpriteManager.UnitInfoDisplaySource.Selection, true);// Show at true unit location
                }
                outcomeVisualizer.Interpret(outcomes, _unitSelected.Position);
            }
        }
예제 #8
0
        public List <Outcome> SimulatePush(IUnitController target, Vector2 pushDirection, int pushAmount)
        {
            List <Outcome> outcomes = new List <Outcome>();

            //outcomes.Add(new Outcome(target, target.SimPosition)); // Add current hex for interpreter convenience

            if (pushAmount > 0)
            {
                for (int i = 1; i <= pushAmount; i++)
                {
                    if (!scenarioLoader.HexGrid.ContainsKey(pushDirection + target.SimPosition.BoardPos))
                    {
                        break;
                    }
                    HexEntry nextHex = scenarioLoader.HexGrid[pushDirection + target.SimPosition.BoardPos];
                    if (nextHex.SimOccupant != null)
                    {
                        break;
                    }
                    if (UnitBaseStats.TerrainCost(target.UnitType, nextHex.Terrain) == 0)
                    {
                        break;
                    }
                    target.SimPosition = nextHex;
                    List <AttackResult> combat = target.Weapon.CombatResults(target.SimRecentPath);
                    outcomes.Add(new Outcome(target, nextHex, false, combat));
                }
            }

            return(outcomes);
        }
예제 #9
0
        private void LoadGrid()
        {
            string mapName;

            if (Vaults.mapName != null)
            {
                mapName = Vaults.mapName;
            }
            else
            {
                mapName = Config.defaultMapName;
            }

            TextAsset map = (TextAsset)Resources.Load(Config.mapLocation + mapName, typeof(TextAsset));

            string[] hexDescriptions = map.text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

            foreach (string description in hexDescriptions)
            {
                try {
                    //Parse file input
                    string desc;
                    string q = description.Substring(0, description.IndexOf(','));
                    desc = description.Substring(description.IndexOf(',') + 1, description.Length - description.IndexOf(',') - 1);
                    string r = desc.Substring(0, desc.IndexOf(','));
                    desc = desc.Substring(desc.IndexOf(',') + 1, desc.Length - desc.IndexOf(',') - 1);
                    string t = desc;

                    int qVal;
                    int rVal;
                    qVal = int.Parse(q);
                    rVal = int.Parse(r);
                    Terrain terrain = (Terrain)Enum.Parse(typeof(Terrain), t);

                    //Instantiate in-game hexes
                    Vector2    hexCoords   = new Vector2(qVal, rVal);
                    Vector2    worldCoords = HexVectorUtil.worldPositionOfHexCoord(hexCoords);
                    GameObject hex         = Instantiate(basicHex, new Vector3(worldCoords.x, worldCoords.y, 1), Quaternion.identity);
                    hex.transform.SetParent(hexContainer);

                    HexEntry newHex = new HexEntry(hex, hexCoords, terrain);
                    HexGrid.Add(hexCoords, newHex);
                    hexesByUniqueID.Add(newHex);

                    //Text stuff:
                    GameObject hexText = Instantiate(textPrefab, Vector2.Scale(worldCoords, Config.worldToCanvasCoordScaler), Quaternion.identity);
                    hexText.GetComponent <UnityEngine.UI.Text>().text = hexCoords.ToString();
                    //hexText.GetComponent<UnityEngine.UI.Text>().text = "[_ _]";
                    hexText.transform.SetParent(worldCanvas.transform, false);
                    newHex.debugText = hexText.GetComponent <UnityEngine.UI.Text>();

                    if (terrain == Terrain.Goal)
                    {
                        turnsRemainingText.transform.position = new Vector3(worldCoords.x, worldCoords.y + 0.23f, 0);
                        turnsRemainingText.gameObject.SetActive(true);
                    }
                } catch { }
            }
        }
예제 #10
0
 //ZoC utility function
 public static bool IsEnemyNeighbor(HexEntry hex, int owner) {
     foreach (HexEntry neighborHex in hex.Neighbors.Values) {
         if (neighborHex.Occupant != null
             && neighborHex.Occupant.PlayerOwner != owner)
             return true;
     }
     return false;
 }
예제 #11
0
        private void OrientArrow(HexEntry start, HexEntry dest, GameObject arrow)
        {
            Vector2 startWorldPos = HexVectorUtil.worldPositionOfHexCoord(start.BoardPos);
            Vector2 destWorldPos  = HexVectorUtil.worldPositionOfHexCoord(dest.BoardPos);

            arrow.transform.position = new Vector3(startWorldPos.x, startWorldPos.y, -3) + (Config.hitArrowPosScaler) * Vector3.Normalize(destWorldPos - startWorldPos);
            arrow.transform.rotation = Quaternion.FromToRotation(Vector2.right, destWorldPos - startWorldPos);
        }
예제 #12
0
        private void OrientSegment(HexEntry start, HexEntry dest, GameObject segment)
        {
            Vector2 startWorldPos = HexVectorUtil.worldPositionOfHexCoord(start.BoardPos);
            Vector2 destWorldPos  = HexVectorUtil.worldPositionOfHexCoord(dest.BoardPos);

            segment.transform.position = new Vector3(startWorldPos.x, startWorldPos.y, 2);
            segment.transform.rotation = Quaternion.FromToRotation(Vector2.right, destWorldPos - startWorldPos);
        }
예제 #13
0
        public void CreateFriendlyFireWarning(HexEntry hex)
        {
            GameObject newFFWarning = Instantiate(friendlyFireWarning, HexVectorUtil.worldPositionOfHexCoord(hex.BoardPos), Quaternion.identity);

            newFFWarning.GetComponent <SpriteRenderer>().color = Config.Palette.attack;
            newFFWarning.transform.SetParent(hitArrowContainer.transform);
            ffWarnings.Add(newFFWarning);
        }
예제 #14
0
        public void CreateHitArrow(HexEntry start, HexEntry dest)
        {
            GameObject newArrow = Instantiate(hitArrow, Vector3.zero, Quaternion.identity);

            newArrow.GetComponent <SpriteRenderer>().color = Config.Palette.pathArrow;
            newArrow.transform.SetParent(hitArrowContainer.transform);
            hitArrows.Add(newArrow);
            OrientArrow(start, dest, newArrow);
        }
예제 #15
0
 public UnitState(IUnitController unitController, HexEntry pos, List <HexEntry> recentPath, int movesRemaining, int zoCMovesRemaining, int hp, int shots)
 {
     this._unitController   = unitController;
     this.pos               = pos;
     this.recentPath        = recentPath;
     this.movesRemaining    = movesRemaining;
     this.zoCMovesRemaining = zoCMovesRemaining;
     this.hp    = hp;
     this.shots = shots;
 }
예제 #16
0
        public void HexMouseEnter(HexEntry hex)
        {
            hoveredHex = hex;

            if (inputEnabled.hexHover)
            {
                hex.HexManager.FaintBackground();
                selectionManager.ShowPathToHex(hex);
            }
        }
예제 #17
0
 public AttackResult(IUnitController target, IUnitController source, int healthRemaining,
                     HexEntry sourceHex, HexEntry targetHex, AttackType attackType, List <Outcome> pushMoves = null)
 {
     this.target          = target;
     this.source          = source;
     this.healthRemaining = healthRemaining;
     this.sourceHex       = sourceHex;
     this.targetHex       = targetHex;
     this.attackType      = attackType;
     this.pushMoves       = pushMoves;
 }
예제 #18
0
        public Outcome(SelectionManager selectionManager, ScenarioLoader scenarioLoader, SZOutcome toCopy)
        {
            activeUnit    = selectionManager.GetUnitByID(toCopy.activeUnit);
            position      = scenarioLoader.GetHexByID(toCopy.position);
            spendingMoves = toCopy.spendingMoves;

            combat = new List <AttackResult>();
            foreach (SZAttackResult attackResult in toCopy.combat)
            {
                combat.Add(new AttackResult(selectionManager, scenarioLoader, attackResult));
            }
        }
예제 #19
0
        public static List <List <HexEntry> > FindAllPaths(HexEntry current, int movesRemaining, IUnitController controller)
        {
            // We will fill this with paths we find
            List <List <HexEntry> > allPaths = new List <List <HexEntry> >();

            // This will be used after every run of the outer loop to process the most recently found paths
            List <List <HexEntry> > pathsFound    = new List <List <HexEntry> >();
            List <List <HexEntry> > newPathsFound = new List <List <HexEntry> >();

            // Used to keep track of hexes we don't want our new paths to go back to.
            HashSet <HexEntry> closed = new HashSet <HexEntry>();

            // Initially, we already know how to get to the origin
            List <HexEntry> originPath = new List <HexEntry>();

            originPath.Add(current);

            // Add the origin path and update allPaths
            pathsFound.Add(originPath);
            allPaths = allPaths.Concat(pathsFound).ToList();

            // We've considered all paths that go through the origin: close it.
            closed.Add(current);

            // At each step, consider paths of length i (not including the origin--that would be i + 1)
            for (var i = 1; i <= movesRemaining; i++)
            {
                // This is a set of the hexes that surround the perimeter of closed.
                HashSet <HexEntry> borderHexes = FindBorderHexes(pathsFound);

                // Now we add them to closed.
                foreach (HexEntry hex in borderHexes)
                {
                    closed.Add(hex);
                }

                // After each call to AugmentPath, newPathsFound will have a new path in it.
                for (var j = 0; j < pathsFound.Count; j++)
                {
                    List <HexEntry> path = pathsFound[j];
                    AugmentPath(path, newPathsFound, closed, controller, movesRemaining);
                }

                // For the next round, make the new paths found the "old" paths found, and make a new newPathsFound
                allPaths = new List <List <HexEntry> >(allPaths.Concat(newPathsFound));

                pathsFound    = newPathsFound;
                newPathsFound = new List <List <HexEntry> >();
            }

            return(allPaths);
        }
예제 #20
0
        public void HexMouseExit(HexEntry hex)
        {
            if (hoveredHex == hex)
            {
                hoveredHex = null;
            }

            hex.HexManager.FullBackground();

            if (inputEnabled.hexHover)
            {
                selectionManager.HidePathToHex(hex);
            }
        }
예제 #21
0
 //Returns null if not neighbors
 public static Bearing?NeighborsToBearing(HexEntry start, HexEntry end)
 {
     foreach (Bearing b in Enum.GetValues(typeof(Bearing)))
     {
         if (start.Neighbors.ContainsKey(b))
         {
             if (start.Neighbors[b] == end)
             {
                 return(b);
             }
         }
     }
     return(null);
 }
예제 #22
0
        public Outcome(IUnitController activeUnit, HexEntry position, bool spendingMoves, List <AttackResult> combat = null)
        {
            this.activeUnit    = activeUnit;
            this.position      = position;
            this.spendingMoves = spendingMoves;

            if (combat == null)
            {
                this.combat = new List <AttackResult>();
            }
            else
            {
                this.combat = combat;
            }
        }
예제 #23
0
 public void SetPath(List <Outcome> pathInput, HexEntry currentHex)
 {
     for (int i = 0; i < pathInput.Count; i++)
     {
         GameObject newSegment = Instantiate(pathSegment, Vector3.zero, Quaternion.identity);
         newSegment.GetComponent <SpriteRenderer>().color = Config.Palette.pathFootstep;
         newSegment.transform.SetParent(pathSegmentContainer.transform);
         pathSegments.Add(newSegment);
         if (i == 0)
         {
             OrientSegment(currentHex, pathInput[i].position, newSegment);
         }
         else
         {
             OrientSegment(pathInput[i - 1].position, pathInput[i].position, newSegment);
         }
     }
 }
예제 #24
0
        private void MarkEdge(IUnitController unit, HexEntry hex, Bearing b)
        {
            Vector2 cornerSpot  = new Vector2();
            Vector2 rotationVec = new Vector2();

            switch (b)
            {
            case Bearing.E:
                cornerSpot  = FindCorner(hex, -30);
                rotationVec = Vector2.down;
                break;

            case Bearing.NNE:
                cornerSpot  = FindCorner(hex, -90);
                rotationVec = new Vector2(1.732f, -1);
                break;

            case Bearing.NNW:
                cornerSpot  = FindCorner(hex, -150);
                rotationVec = new Vector2(1.732f, 1);
                break;

            case Bearing.W:
                cornerSpot  = FindCorner(hex, -210);
                rotationVec = Vector2.up;
                break;

            case Bearing.SSW:
                cornerSpot  = FindCorner(hex, -270);
                rotationVec = new Vector2(-1.732f, 1);
                break;

            case Bearing.SSE:
                cornerSpot  = FindCorner(hex, -330);
                rotationVec = new Vector2(-1.732f, -1);
                break;
            }
            GameObject segment = Instantiate(rangeIndicator, new Vector3(cornerSpot.x, cornerSpot.y, 3), Quaternion.FromToRotation(Vector2.right, rotationVec));

            segment.transform.SetParent(rangeIndicatorContainer.transform);
            segment.GetComponent <SpriteRenderer>().color = Config.Palette.PlayerColor(unit.PlayerOwner);

            rangeIndicatorSegments[unit].Add(segment);
        }
예제 #25
0
        // Tell each HexEntry about the HexEntries that are immediately adjacent
        public void ConnectNeighbors()
        {
            foreach (KeyValuePair <Vector2, HexEntry> entry in HexGrid)
            {
                Vector2  loc      = entry.Key;
                HexEntry hexEntry = entry.Value;
                IDictionary <Bearing, HexEntry> neighbors = new Dictionary <Bearing, HexEntry>();

                foreach (Bearing b in Enum.GetValues(typeof(Bearing)))
                {
                    Vector2 neighborLoc = loc + HexVectorUtil.NeighborOffsetFromBearing(b);
                    if (HexGrid.ContainsKey(neighborLoc))
                    {
                        neighbors.Add(b, HexGrid[neighborLoc]);
                    }
                }

                hexEntry.SetNeighbors(neighbors);
            }
        }
예제 #26
0
        public override int CheckRetal(List <HexEntry> recentPath, IUnitController enemy)
        {
            if (recentPath.Count < 2 || enemy.PlayerOwner == unitController.PlayerOwner)
            {
                return(0);
            }

            HexEntry current = recentPath[recentPath.Count - 1];
            HexEntry last    = recentPath[recentPath.Count - 2];

            if (HexVectorUtil.AxialDistance(current.BoardPos, unitController.SimPosition.BoardPos) == 3)
            {
                if (HexVectorUtil.AxialDistance(last.BoardPos, unitController.SimPosition.BoardPos) > 3)
                {
                    return(unitController.DamageOutput);
                }
            }

            return(0);
        }
예제 #27
0
        public void ShowRangeIndicator(IUnitController unit, HexEntry location, int range)
        {
            if (location == null)
            {
                return;
            }

            if (!rangeIndicatorSegments.ContainsKey(unit))
            {
                rangeIndicatorSegments[unit] = new List <GameObject>();
            }

            List <HexEntry> hexesInRange = new List <HexEntry>();

            for (int dx = -range; dx <= range; dx++)
            {
                for (int dy = Mathf.Max(-range, -dx - range); dy <= Mathf.Min(range, -dx + range); dy++)
                {
                    Vector2 hexPosition = new Vector2((int)location.BoardPos.x + dx, (int)location.BoardPos.y - dx - dy);
                    if (scenarioLoader.HexGrid.ContainsKey(hexPosition))
                    {
                        hexesInRange.Add(scenarioLoader.HexGrid[hexPosition]);
                    }
                }
            }

            foreach (HexEntry h in hexesInRange)
            {
                foreach (Bearing b in Enum.GetValues(typeof(Bearing)))
                {
                    if (!h.Neighbors.ContainsKey(b))
                    {
                        MarkEdge(unit, h, b);
                    }
                    else if (!hexesInRange.Contains(h.Neighbors[b]))
                    {
                        MarkEdge(unit, h, b);
                    }
                }
            }
        }
예제 #28
0
        public void Interpret(List <Outcome> outcomes, HexEntry footstepStart = null)
        {
            if (footstepStart != null)
            {
                pathVisualizer.SetPath(outcomes, footstepStart);
            }

            foreach (Outcome outcome in outcomes)
            {
                foreach (AttackResult attackResult in outcome.combat)
                {
                    if (attackResult.attackType != AttackType.Push)
                    {
                        attackResult.targetHex.HexManager.BorderColor = Config.Palette.attack;
                    }
                    else
                    {
                        attackResult.targetHex.HexManager.BorderColor = Config.Palette.pushAttack;
                        attackResult.targetHex.HexManager.gameObject.transform.position = new Vector3(attackResult.targetHex.HexManager.gameObject.transform.position.x,
                                                                                                      attackResult.targetHex.HexManager.gameObject.transform.position.y, 0); // Make sure push attack borders go behind damaging attack borders
                    }
                    redHexes.Add(attackResult.targetHex);

                    pathVisualizer.CreateHitArrow(attackResult.sourceHex, attackResult.targetHex);

                    attackResult.source.SpriteManager.ShowExtraInfo(UnitSpriteManager.UnitInfoDisplaySource.CombatVisualization, true);
                    rangeIndicators.Add(attackResult.source);

                    if (attackResult.source.PlayerOwner == attackResult.target.PlayerOwner)
                    {
                        pathVisualizer.CreateFriendlyFireWarning(outcome.position);
                    }

                    if (attackResult.pushMoves != null)
                    {
                        Interpret(attackResult.pushMoves);
                    }
                }
            }
        }
예제 #29
0
        public void Initialize(IMover mover, OutcomeAnimator outcomeAnimator, UnitSpriteManager unitSpriteManager,
                               HexEntry startPosition)
        {
            this._mover             = mover;
            this._outcomeAnimator   = outcomeAnimator;
            this._unitSpriteManager = unitSpriteManager;

            RecentPath = new List <HexEntry>();
            Position   = startPosition;
            unitSpriteManager.VisiblePosition = Position;

            HP = UnitBaseStats.HP(UnitType);
            SpriteManager.MaxHealthDisplay     = HP; // Instantiate the health bar sizing for the unit, maxHP must be first
            SpriteManager.CurrentHealthDisplay = HP;

            TurnEndUpkeep();
            TurnStartUpkeep();

            UnitType    = _unitType; // Gives spritemanager unit-type data
            WeaponType  = _weaponType;
            PlayerOwner = _playerOwner;
        }
예제 #30
0
        public AttackResult(SelectionManager selectionManager, ScenarioLoader scenarioLoader, SZAttackResult toCopy)
        {
            target          = selectionManager.GetUnitByID(toCopy.target);
            source          = selectionManager.GetUnitByID(toCopy.source);
            healthRemaining = toCopy.healthRemaining;
            sourceHex       = scenarioLoader.GetHexByID(toCopy.sourceHex);
            targetHex       = scenarioLoader.GetHexByID(toCopy.targetHex);
            attackType      = toCopy.attackType;

            if (toCopy.pushMoves == null)
            {
                pushMoves = null;
            }
            else
            {
                pushMoves = new List <Outcome>();
                foreach (SZOutcome outcome in toCopy.pushMoves)
                {
                    pushMoves.Add(new Outcome(selectionManager, scenarioLoader, outcome));
                }
            }
        }