Ejemplo n.º 1
0
    // try to heal the person, but if too far, screw it go for wait instead
    public override IEnumerator Execute()
    {
        map.FindSelectableTiles(currUnit.currentTile, currUnit.stats[StatString.MOVEMENT_RANGE].Value);

        yield return(new WaitForSecondsRealtime(0.75f));

        Tile      targetTile  = enemyUnit.medicTarget.currentTile;
        EnemyUnit medicTarget = enemyUnit.medicTarget;

        AStarSearch.GeneratePath(map, currUnit.currentTile, targetTile, false, true);

        // check if target tile is selectable, and also go as far from movement range as possible.
        int distanceFromTarget = 0, minHealingRange = (int)enemyUnit.teamHealingAbilities.Min(ability => ability.attackRange);

        while (!targetTile.selectable || distanceFromTarget < minHealingRange && targetTile != currUnit.currentTile)
        {
            distanceFromTarget++;
            targetTile = targetTile.parent;
        }

        // if cannot reach in 2 turns, screw it change to Wait()
        int maxHealingRange     = (int)enemyUnit.teamHealingAbilities.Max(ability => ability.attackRange);
        var maxPermissibleRange = (int)
                                  (currUnit.stats[StatString.MOVEMENT_RANGE].Value +
                                   enemyUnit.medicTarget.stats[StatString.MOVEMENT_RANGE].Value) * 2 + maxHealingRange;

        if (targetTile.distance > maxPermissibleRange)
        {
            turnScheduler.SetState(new EnemyAiWait(turnScheduler));
        }

        // A star movement towards the target
        currUnit.GetPathToTile(targetTile);

        yield return(new WaitUntil(() => currUnit.CurrState == UnitState.IDLING));

        // check if there are players in range
        bool canHeal = false;

        foreach (Ability ability in enemyUnit.teamHealingAbilities) // TODO should be able to sort in ascending order by healing amount
        {
            if (map.PlayerTargetInAttackRange(currUnit.currentTile, ability.attackRange, medicTarget))
            {
                currUnit.abilityTargetUnits = new List <Unit>()
                {
                    medicTarget
                };                                                             // TODO only supports single target healing at the moment
                currUnit.chosenAbility = ability;
                turnScheduler.SetState(new EnemyAbility(turnScheduler));
                canHeal = true;
            }
        }

        if (!canHeal)
        {
            turnScheduler.SetState(new EnemyAiWait(turnScheduler));
        }
    }
Ejemplo n.º 2
0
        public IEnumerator astar_finds_shortest_distance_from_5x1y_to_7x0y_equals_4()
        {
            // ACT
            AStarSearch.GeneratePath(_map, _map.tileList[5][1], _map.tileList[7][0]);

            // ASSERT
            Assert.AreEqual(4, _map.tileList[7][0].distance);

            yield return(null);
        }
Ejemplo n.º 3
0
    // selectable means that only selectable tiles will be added (for walking within the selectable tile zone)
    // playerTargeting means that the only occupied tile allowed will be the goal tile (to allow targeting of units)
    public static Tile GeneratePathToNearestTarget(Map map, Tile start, List <Tile> targets, bool selectable = false, bool playerTargeting = false)
    {
        // use distance to determine closest player
        int  minDistance = int.MaxValue;
        Tile targetTile  = targets[0];

        foreach (Tile target in targets)
        {
            AStarSearch.GeneratePath(map, start, target, selectable, playerTargeting);
            if (target.distance < minDistance)
            {
                minDistance = target.distance;
                targetTile  = target;
            }
        }

        AStarSearch.GeneratePath(map, start, targetTile, selectable, playerTargeting);
        return(targetTile);
    }
Ejemplo n.º 4
0
    public List <Vector2> GeneratePath(Vector2 start, Vector2 end)
    {
        uint             nodeConversion = OS.GetTicksMsec();
        TileMapGraphNode startNode      = tileMapGraph.GetGraphNodeFromWorldPos(start);
        TileMapGraphNode endNode        = tileMapGraph.GetGraphNodeFromWorldPos(end);

        GD.Print("Node Conversion: " + (OS.GetTicksMsec() - nodeConversion) + "ms");

        uint astarSearch = OS.GetTicksMsec();
        List <TileMapGraphNode> graphNodePath = AStarSearch.GeneratePath(tileMapGraph, startNode, endNode);

        GD.Print("AStarSearch.GeneratePath: " + (OS.GetTicksMsec() - astarSearch) + "ms");

        List <Vector2> path = new List <Vector2>();

        if (graphNodePath != null)
        {
            for (int i = 0; i < graphNodePath.Count; i++)
            {
                path.Add(graphNodePath[i].GetWorldPosition());
            }
        }
        return(path);
    }
Ejemplo n.º 5
0
    public override IEnumerator Execute()
    {
        // if can self heal
        if (enemyUnit.CanSelfHeal())
        {
            // retreat, heal
            // from all player positions, find selectable tiles using dijkstra - add a proximity score to each one of them.
            // Then after that, enemy unit finds selectable tiles and chooses a tile at random of the least proximity score..
            yield return(new WaitForSecondsRealtime(0.5f));

            Retreat();

            yield return(new WaitUntil(() => currUnit.CurrState == UnitState.IDLING));

            map.RemoveSelectableTiles(currUnit.currentTile);

            // heal

            // randomly assign ability (in future maybe prioritise highest heal instead)
            Ability healAbility = enemyUnit.selfHealingAbilities[new System.Random().Next(0, enemyUnit.selfHealingAbilities.Count)];

            enemyUnit.chosenAbility = healAbility;
            currUnit.CurrState      = UnitState.TARGETING;
            map.FindAttackableTiles(currUnit.currentTile, healAbility.attackRange, healAbility.targetingStyle);
            yield return(new WaitForSecondsRealtime(1));

            map.RemoveAttackableTiles();

            // TODO in future need to migrate this to abiltyTargeting to maximise the number of targets
            List <Unit> targetUnits = new List <Unit>()
            {
                currUnit
            };

            turnScheduler.StartCoroutine(turnScheduler.AbilityAnimation(currUnit));
            yield return(new WaitUntil(() => currUnit.anim.GetBool("isAbility") == false));

            yield return(turnScheduler.StartCoroutine(turnScheduler.currUnit.chosenAbility.Execute(turnScheduler.currUnit, targetUnits)));

            foreach (Unit unit in targetUnits)
            {
                if (unit.isDead())
                {
                    yield return(turnScheduler.StartCoroutine(turnScheduler.RemoveUnit(unit)));
                }
                else
                {
                    unit.UpdateUI();
                }
            }

            targetUnits.Clear();
            turnScheduler.currUnit.chosenAbility = null;

            yield return(new WaitForSeconds(1f));

            turnScheduler.SetState(new EnemyEndTurn(turnScheduler));

            yield break;
        }

        else
        {
            List <EnemyUnit> healers = enemies.FindAll(x => x.CanTeamHeal());
            // find closest unit who can team heal
            if (healers.Count > 0)
            {
                List <PlayerUnit> players = turnScheduler.players;

                // Rank healers and find one to run towards
                // Ranked according to distance
                // Ranked by maximising the minimum (distance from player - player range)


                // initialize min/max ally/enemies distance,
                AStarSearch.GeneratePath(map, enemyUnit.currentTile, healers[0].currentTile, false, true);
                List <int>   allyDistances      = new List <int>();
                List <float> minPlayerDistances = new List <float>();


                // loop through all the healers and players and rank heuristic
                for (int i = 0; i < healers.Count; i++)
                {
                    EnemyUnit healer = healers[i];
                    AStarSearch.GeneratePath(map, enemyUnit.currentTile, healer.currentTile, false, true);
                    allyDistances.Add(healer.currentTile.distance);

                    // finding (distance from player - player range) for all players
                    // moveTo is to tile the player will end up at
                    Tile moveTo  = healer.currentTile;
                    bool changed = false;
                    while (moveTo.distance > enemyUnit.stats[StatString.MOVEMENT_RANGE].Value)
                    {
                        moveTo  = moveTo.parent;
                        changed = true;
                    }
                    if (changed)
                    {
                        Assert.AreNotEqual(moveTo, healer.currentTile);
                    }

                    // calculating the minimum player distance and add the inverse since we want to be far away TODO DIKJSTRA ONCE AND FOR ALL
                    int minPlayerDistance = int.MaxValue;
                    foreach (Unit player in players)
                    {
                        AStarSearch.GeneratePath(map, player.currentTile, moveTo, false, true);
                        var playerEffectiveAttackRange = player.stats[StatString.ATTACK_RANGE].Value +
                                                         player.stats[StatString.MOVEMENT_RANGE].Value;
                        int currPlayerDistance = (int)(moveTo.distance - playerEffectiveAttackRange);
                        currPlayerDistance = currPlayerDistance < 1 ? 1 : currPlayerDistance;
                        minPlayerDistance  = Math.Min(minPlayerDistance, currPlayerDistance);
                    }
                    minPlayerDistances.Add(1.0f / minPlayerDistance);
                }


                // normalize both lists then find the min heustistic (60% ally 40% player)
                List <float> allyDistancesNorm      = NormalizeList(allyDistances);
                List <float> minPlayerDistancesNorm = NormalizeList(minPlayerDistances);

                float minRecoveryHeuristic = float.MaxValue;
                int   targetHealerIndex    = 0;

                for (int i = 0; i < allyDistancesNorm.Count; i++)
                {
                    float currentHeuristic = 0.6f * allyDistancesNorm[i] + 0.4f * minPlayerDistancesNorm[i];
                    if (currentHeuristic < minRecoveryHeuristic)
                    {
                        targetHealerIndex    = i;
                        minRecoveryHeuristic = currentHeuristic;
                    }
                }


                // go to closest healer
                Tile targetTile = healers[targetHealerIndex].currentTile;

                AStarSearch.GeneratePath(map, enemyUnit.currentTile, targetTile, false, true);

                map.FindSelectableTiles(currUnit.currentTile, enemyUnit.stats[StatString.MOVEMENT_RANGE].Value);

                yield return(new WaitForSecondsRealtime(0.5f));

                /*
                 * while (targetTile.distance > enemyUnit.stats[StatString.MOVEMENT_RANGE].Value)
                 * {
                 *  targetTile = targetTile.parent;
                 * }
                 */
                while (!targetTile.selectable)
                {
                    targetTile = targetTile.parent;
                }
                currUnit.GetPathToTile(targetTile);

                yield return(new WaitUntil(() => currUnit.CurrState == UnitState.IDLING));

                turnScheduler.SetState(new EnemyEndTurn(turnScheduler));
            }

            // no healing avenues. Run? Or change to aggressive mode
            else
            {
                yield return(new WaitForSecondsRealtime(0.5f));

                Retreat();

                yield return(new WaitUntil(() => currUnit.CurrState == UnitState.IDLING));

                map.RemoveSelectableTiles(currUnit.currentTile);

                turnScheduler.SetState(new EnemyEndTurn(turnScheduler));
            }
        }
    }