예제 #1
0
 public Action(List <Tile> path, ActionType actionType = ActionType.Nothing, Targetable target = null, float score = 0f)
 {
     this.path       = path;
     this.actionType = actionType;
     Score           = score;
     if (actionType == ActionType.Rest && target != null)
     {
         throw new ArgumentException("A rest action cannot have a target unit");
     }
     this.target = target;
 }
예제 #2
0
 public void LinkTargetable(Targetable targetable)
 {
     if (targetable.GetType() == typeof(Unit))
     {
         LinkUnit((Unit)targetable);
     }
     else if (targetable.GetType() == typeof(Door))
     {
         LinkDoor((Door)targetable);
     }
     UpdateClickHint();
 }
예제 #3
0
        /// <summary>
        /// Finds the safest place a unit could go to rest
        /// </summary>
        /// <param name="potentialActions">The potential actions of this unit</param>
        /// <param name="playableUnit">The unit currently controlled by the AI</param>
        /// <returns>The path to the safest tile to go</returns>
        private List <Tile> FindFleePath(List <Action> potentialActions, Unit playableUnit, GridController grid)
        {
            int[,] optionMap = new int[grid.NbColumns, grid.NbLines];
            for (int i = 0; i < optionMap.GetLength(0); i++)
            {
                for (int j = 0; j < optionMap.GetLength(1); j++)
                {
                    optionMap[i, j] = 0;
                }
            }

            Targetable enemy = null;

            for (int i = 0; i < optionMap.GetLength(0); i++)
            {
                for (int j = 0; j < optionMap.GetLength(1); j++)
                {
                    for (int k = 0; k < potentialActions.Count; k++)
                    {
                        enemy = potentialActions[k].Target;
                        if (enemy is Unit enemyUnit)
                        {
                            //Each tile has a score based on the added number of turns it would take each player's unit to get there
                            optionMap[i, j] += (int)Math.Ceiling((enemyUnit.MovementCosts[i, j] - 1) / (double)enemyUnit.Stats.MoveSpeed);
                        }
                    }
                }
            }

            int        highestScore = 0;
            Vector2Int position     = new Vector2Int(-1, -1);

            for (int i = 0; i < optionMap.GetLength(0); i++)
            {
                for (int j = 0; j < optionMap.GetLength(1); j++)
                {
                    if (optionMap[i, j] >= highestScore && playableUnit.MovementCosts[i, j] <= playableUnit.Stats.MoveSpeed)
                    {
                        highestScore = optionMap[i, j];
                        position.x   = i;
                        position.y   = j;
                    }
                }
            }

            return(FindPathTo(playableUnit, position, grid));
        }
예제 #4
0
        /// <summary>
        /// Calculates how the distance to a target unit influences the score of an action
        /// </summary>
        /// <param name="playableUnit">The unit currently controlled by the AI</param>
        /// <param name="targetPath">The path to a target unit</param>
        /// <returns>The score modifier caused by the target's distance</returns>
        public float DistanceChoiceMod(Unit playableUnit, Targetable target, List <Tile> targetPath)
        {
            float scoreMod = 0f;

            if (playableUnit.TargetIsInRange(target))
            {
                scoreMod = aiControllerValues.AdjacentTargetChoiceMod;
            }
            else if (targetPath.Count <= 1)
            {
                scoreMod += aiControllerValues.InaccessibleTargetChoiceMod;
            }
            else
            {
                double nbToursDouble = PathFinder.CalculatePathCost(targetPath, playableUnit.MovementCosts) / playableUnit.Stats.MoveSpeed;
                int    nbTours       = (int)Math.Ceiling(nbToursDouble);
                scoreMod = aiControllerValues.TurnMultiplierForDistanceChoiceMod * nbTours + aiControllerValues.TurnAdderForDistanceChoiceMod;
            }
            return(scoreMod);
        }
예제 #5
0
        public IEnumerator Attack(Targetable target, bool isCountering, float duration)
        {
            if (associatedUnit.IsAttacking)
            {
                yield break;
            }
            associatedUnit.IsAttacking = true;
            associatedUnit.UnitAnimator?.PlayAttackAnimation();

            var counter  = 0f;
            var startPos = associatedUnit.Transform.position;
            //2f to go only halfway
            var targetPos = (target.CurrentTile.WorldPosition + startPos) / 2f;

            LookAt(targetPos);
            duration /= 2;

            while (counter < duration)
            {
                counter += Time.deltaTime;
                associatedUnit.Transform.position = Vector3.Lerp(startPos, targetPos, counter / duration);
                yield return(null);
            }

            var hitRate      = associatedUnit.Stats.HitRate - target.CurrentTile.DefenseRate;
            var damage       = 0;
            var critModifier = 1;

            if (Random.value <= hitRate)
            {
                damage = associatedUnit.Stats.AttackStrength;
                if (target is Unit unit)
                {
                    associatedUnit.OnDodge.Publish(unit);
                    associatedUnit.UnitAnimator?.PlayBlockAnimation();
                }
            }
            else if (target is Unit unit)
            {
                associatedUnit.OnHurt.Publish(unit);
                associatedUnit.UnitAnimator?.PlayHurtAnimation();
            }

            if (target is Unit enemyUnit)
            {
                if (damage > 0 && !isCountering && !enemyUnit.IsImmuneToCrits &&
                    (associatedUnit.CanCritOnEverybody || enemyUnit.WeaponType == associatedUnit.WeaponAdvantage))
                {
                    critModifier = Random.value <= associatedUnit.Stats.CritRate ? 2 : 1;
                    damage      *= critModifier;
                    if (critModifier > 1 && associatedUnit.CameraShake != null)
                    {
                        associatedUnit.CameraShake.TriggerShake();
                    }
                }
            }

            target.CurrentHealthPoints -= damage;

            if (target is Unit)
            {
                uiController.ChangeCharacterDamageTaken(damage, !associatedUnit.IsEnemy, critModifier);
            }

            associatedUnit.UnitAnimator?.StopBlockAnimation();
            associatedUnit.UnitAnimator?.StopHurtAnimation();

            counter = 0;

            while (counter < duration)
            {
                counter += Time.deltaTime;
                associatedUnit.Transform.position = Vector3.Lerp(targetPos, startPos, counter / duration);
                yield return(null);
            }

            associatedUnit.Transform.position = startPos;
            associatedUnit.IsAttacking        = false;
            associatedUnit.UnitAnimator?.StopAttackAnimation();

            //A unit cannot make a critical hit on a counter && cannot counter on a counter
            if (!target.NoHealthLeft && !isCountering && target is Unit targetUnit)
            {
                yield return(targetUnit.UnitMover.Attack(associatedUnit, true, gameSettings.AttackDuration));
            }

            if (!isCountering)
            {
                associatedUnit.HasActed = true;
            }
        }
예제 #6
0
 public void AddTarget(Targetable target)
 {
     targetsToDestroy.Add(target);
 }