Пример #1
0
        /// <summary>
        /// Gets the amount of damage, healing, and crit chance/damage a status effect does as a list of key value pairs.
        /// </summary>
        /// <param name="character">The character to use to calculate the amount of damage and healing a status effect does.</param>
        /// <param name="status">The status effect to get data for.</param>
        /// <returns>A list of key value pairs containing the name of the stat and the value of that stat.</returns>
        private List <KeyValuePair <string, int> > GetStatusDamageData(Character character, StatusEffect status)
        {
            var data = new List <KeyValuePair <string, int> >();

            var damage = GetElementalData(DamageCalculator.GetDamage(character, status), "Damage/Turn");
            // Calculate heals
            int  healAmount    = DamageCalculator.GetHealing(character, status);
            int  healPercent   = DamageCalculator.GetHealingPercentage(character, status);
            bool healsOrDamage = damage.Any() || healAmount != 0 || healPercent != 0;

            // Add damage first
            data.AddRange(damage);

            if (healsOrDamage)
            {
                data.Add(new KeyValuePair <string, int>("Crit Chance", status.DamageCritChance + character.CritChance));
                data.Add(new KeyValuePair <string, int>("Crit Damage", status.DamageCritMultiplier + character.CritMultiplier));
            }

            // Add any healing amounts
            if (healAmount != 0)
            {
                data.Add(new KeyValuePair <string, int>("Health Restored/Turn", healAmount));
            }
            if (healPercent != 0)
            {
                data.Add(new KeyValuePair <string, int>("% Health Restored/Turn", healPercent));
            }

            return(data);
        }
Пример #2
0
        /// <summary>
        /// Applies a StatusEffect onto a CombatEntity.
        /// </summary>
        /// <param name="recipient">The CombatEntity who is receiving the StatusEffect.</param>
        /// <param name="applicator">The CombatEntity applying the StatusEffect on the receiver.</param>
        /// <param name="statusEffect">The StatusEffect to apply onto the receiver.</param>
        /// <param name="isCrit">If true, will include critical damage in the calculations.</param>
        public void Apply(CombatEntity recipient,
                          CombatEntity applicator,
                          StatusEffect statusEffect,
                          bool isCrit)
        {
            var status = recipient.StatusEffects.FirstOrDefault(se => se.BaseStatus.Id == statusEffect.Id);

            if (status == null)
            {
                var appliedStatusEffect = CreateAppliedStatus(applicator, recipient, statusEffect);
                ApplyStatEffects(recipient, statusEffect);
                recipient.StatusEffects.Add(appliedStatusEffect);
            }
            else
            {
                // Apply another stack of StatusEffect
                if (statusEffect.StackSize > 1 && status.CurrentStacks < statusEffect.StackSize)
                {
                    status.CumulativeDamage += DamageCalculator.GetDamage(applicator, statusEffect, isCrit);
                    status.CumulativeHeal   += DamageCalculator.GetHeal(applicator, statusEffect, isCrit);
                    status.CurrentStacks++;
                    status.Duration = statusEffect.Duration;
                }
                // Can't apply another stack, refresh duration instead
                else
                {
                    status.Duration = statusEffect.Duration;
                }
            }
        }
Пример #3
0
 public virtual void Attack(Character target)
 {
     if (CanHit(this, target))
     {
         DamageCalculator dc = new DamageCalculator();
         int damage          = dc.GetDamage(this, target);
         target.TakeDamage(damage);
     }
 }
Пример #4
0
        /// <summary>
        /// Creates ActionData using data from a character and that character's action.
        /// </summary>
        /// <param name="character">The character to use data from.</param>
        /// <param name="action">The action to use data from.</param>
        /// <returns>A struct containing display data about the action.</returns>
        private ActionData CreateActionData(Character character, ActionBase action)
        {
            var data   = new ActionData();
            var damage = DamageCalculator.GetDamage(character, action);

            data.Damage         = action.Damage;
            data.ModifiedDamage = damage;
            data.Heal           = action.HealAmount;
            data.StatusEffects  = new List <string>(action.BuffsToApply.Select(status => status.Name));
            return(data);
        }
Пример #5
0
        /// <summary>
        /// Creates an instance of an AppliedStatusEffect from the given StatusEffect.
        /// </summary>
        /// <param name="applicator">The CombatEntity applying this instance of the AppliedStatusEffect.</param>
        /// <param name="statusEffect">The base to use for the AppliedStatusEffect.</param>
        /// <returns>Returns an instance of an AppliedStatusEffect that keeps track of a StatusEffect on a CombatEntity.</returns>
        private AppliedStatusEffect CreateAppliedStatus(CombatEntity applicator, CombatEntity recipient, StatusEffect statusEffect)
        {
            var appliedStatus = new AppliedStatusEffect();

            appliedStatus.BaseStatus       = statusEffect;
            appliedStatus.CumulativeDamage = DamageCalculator.GetDamage(applicator, statusEffect);
            appliedStatus.CumulativeHeal   = DamageCalculator.GetHeal(applicator, statusEffect);
            appliedStatus.CumulativeHeal  += DamageCalculator.GetPercentageHeal(recipient.Resources.MaxHealth, statusEffect.PercentHealPerTurn);

            appliedStatus.CurrentStacks = 1;
            appliedStatus.Duration      = statusEffect.Duration;

            return(appliedStatus);
        }
Пример #6
0
        /// <summary>
        /// Creates an instance of a DelayedAbility.
        /// </summary>
        /// <param name="attacker">The CombatEntity starting the effect of a DelayedAbility.</param>
        /// <param name="ability">The Ability to convert into a DelayedAbility.</param>
        /// <param name="action">The object containing details about the action being performed.</param>
        /// <param name="targetFormation">The Formation that the CombatEntity is targeting with its action.</param>
        /// <returns></returns>
        public DelayedAbilityResult CreateDelayedAbility(CombatEntity attacker,
                                                         Ability ability,
                                                         BattleAction action,
                                                         Formation targetFormation)
        {
            var result = new DelayedAbilityResult();

            // Target out of bounds
            if (!ability.IsPointBlank && !ability.IsPositionStatic &&
                (action.TargetPosition > 9 || action.TargetPosition < 1))
            {
                result.FailureReason = BattleErrorWriter.WriteTargetPositionOutOfBounds();
                return(result);
            }

            if (!HasEnoughResources(attacker, ability, out string failureReason))
            {
                result.FailureReason = failureReason;
                return(result);
            }

            int targetPosition = action.TargetPosition;

            if (ability.IsPointBlank)
            {
                targetPosition = GetTargetPosition(attacker, targetFormation);
            }

            ConsumeResources(attacker, ability);
            bool isCrit = IsCritical(attacker, ability);

            result.DelayedAbility = new DelayedAbility
            {
                Actor           = attacker,
                BaseAbility     = ability,
                StoredDamage    = DamageCalculator.GetDamage(attacker, ability, isCrit),
                StoredHealing   = DamageCalculator.GetHeal(attacker, ability, isCrit),
                TargetFormation = targetFormation,
                TargetPosition  = action.TargetPosition,
                IsCrit          = isCrit,
                TurnsLeft       = ability.DelayedTurns
            };

            return(result);
        }
Пример #7
0
        private static void ReportHit(
            string hitDescription,
            int attack,
            int defense,
            int health,
            float attackMultiplier = 1f,
            int numAttacks         = 1
            )
        {
            var attackWithMultiplier = (int)Math.Round(attack * attackMultiplier);
            var damageDone           = DamageCalculator.GetDamage(attackWithMultiplier, defense);
            var totalDamageDone      = damageDone * numAttacks;
            var damageDonePercentage = 1 - (health - totalDamageDone) / (float)health;
            var hitsToKill           = CalculateHitsToKill(totalDamageDone, health);

            if (numAttacks == 1)
            {
                System.Console.WriteLine($"{damageDone} dmg, {hitDescription} ({damageDonePercentage:P}, htk: {hitsToKill})");
            }
            else if (numAttacks > 1)
            {
                System.Console.WriteLine($"{totalDamageDone} dmg ({numAttacks}x {damageDone}), {hitDescription} ({damageDonePercentage:P}, htk: {hitsToKill})");
            }
        }
        public void UpdateForecast(Vector2Int attackingUnitNewPosition, Unit playerUnit, Unit enemyUnit)
        {
            if (playerUnit.PlayerNumber != enemyUnit.PlayerNumber)
            {
                targetUnitInformationBackground.color = ENEMY_COLOR;

                var defenderPosition        = gameViewModel.GetPositionOfUnit(enemyUnit);
                var defenderAttackPositions = gameViewModel.GetSurroundingLocationsAtPoint(defenderPosition, enemyUnit.MainWeapon.Range);

                playerNameLabel.text         = playerUnit.Name;
                playerAttackMethodLabel.text = playerUnit.MainWeapon.Name;
                playerCurrentHpLabel.text    = $"CUR {playerUnit.HealthPoints}";
                playerMaxHpLabel.text        = $"MAX {playerUnit.MaxHealthPoints}";

                playerHitLabel.text       = $"HIT {DamageCalculator.GetHitChance(playerUnit, enemyUnit)}";
                playerOffensiveLabel.text = $"OFF {DamageCalculator.GetOffensive(playerUnit)}";

                enemyDefensiveLabel.text = $"DEF {DamageCalculator.GetDefensive(playerUnit, enemyUnit)}";

                enemyPotentialHpLabel.gameObject.SetActive(true);

                int enemyHealthLost = 0;
                if (playerUnit.Speed.Value > enemyUnit.Speed.Value)
                {
                    enemyHealthLost = 2 * DamageCalculator.GetDamage(playerUnit, enemyUnit);
                    playerFollowupPossibleLabel.gameObject.SetActive(true);
                }
                else
                {
                    enemyHealthLost = DamageCalculator.GetDamage(playerUnit, enemyUnit);
                    playerFollowupPossibleLabel.gameObject.SetActive(false);
                }

                if (enemyUnit.HealthPoints - enemyHealthLost <= 0)
                {
                    enemyPotentialHpLabel.text = $"0";
                }
                else
                {
                    enemyPotentialHpLabel.text = $"{enemyUnit.HealthPoints - enemyHealthLost}";
                }

                playerDefensiveLabel.text = $"DEF {DamageCalculator.GetDefensive(enemyUnit, playerUnit)}";
                playerCritLabel.text      = $"CRT {DamageCalculator.GetCritRate(playerUnit, enemyUnit)}";

                enemyNameLabel.text      = enemyUnit.Name;
                enemyCurrentHpLabel.text = $"CUR {enemyUnit.HealthPoints}";
                enemyMaxHpLabel.text     = $"MAX {enemyUnit.MaxHealthPoints}";

                playerPotentialHpLabel.gameObject.SetActive(true);
                if (defenderAttackPositions.Contains(attackingUnitNewPosition))
                {
                    var playerHealthLost = 0;
                    if (enemyUnit.Speed.Value > playerUnit.Speed.Value)
                    {
                        playerHealthLost = 2 * DamageCalculator.GetDamage(enemyUnit, playerUnit);
                        enemyFollowupPossibleLabel.gameObject.SetActive(true);
                    }
                    else
                    {
                        playerHealthLost = DamageCalculator.GetDamage(enemyUnit, playerUnit);
                        enemyFollowupPossibleLabel.gameObject.SetActive(false);
                    }

                    if (playerUnit.HealthPoints - playerHealthLost <= 0)
                    {
                        playerPotentialHpLabel.text = "0";
                    }
                    else
                    {
                        playerPotentialHpLabel.text = $"{playerUnit.HealthPoints - playerHealthLost}";
                    }

                    enemyAttackMethodLabel.text = playerUnit.MainWeapon.Name;

                    enemyHitLabel.text       = $"HIT {DamageCalculator.GetHitChance(enemyUnit, playerUnit)}";
                    enemyOffensiveLabel.text = $"OFF {DamageCalculator.GetOffensive(enemyUnit)}";

                    enemyDefensiveLabel.text = $"DEF {DamageCalculator.GetDefensive(playerUnit, enemyUnit)}";
                    enemyCritLabel.text      = $"CRT {DamageCalculator.GetCritRate(enemyUnit, playerUnit)}";
                }
                else
                {
                    playerPotentialHpLabel.text = $"{playerUnit.HealthPoints}";
                    enemyAttackMethodLabel.text = "";
                    enemyHitLabel.text          = $"HIT 0";
                    enemyOffensiveLabel.text    = $"OFF 0";
                    enemyCritLabel.text         = $"CRT 0";
                }
            }
        }
        public void UpdateForecastWithSkill(Vector2Int attackingUnitNewPosition, Unit playerUnit, Unit targetUnit, SingleTargetSkill skill)
        {
            if (playerUnit.PlayerNumber != targetUnit.PlayerNumber)
            {
                targetUnitInformationBackground.color = ENEMY_COLOR;

                var defenderPosition        = gameViewModel.GetPositionOfUnit(targetUnit);
                var defenderAttackPositions = gameViewModel.GetSurroundingLocationsAtPoint(defenderPosition, targetUnit.MainWeapon.Range);
                var damageSkill             = skill as SingleDamageSkill;

                playerNameLabel.text         = playerUnit.Name;
                playerAttackMethodLabel.text = $"{damageSkill.SkillName}";
                playerCurrentHpLabel.text    = $"CUR {playerUnit.HealthPoints}";
                playerMaxHpLabel.text        = $"MAX {playerUnit.MaxHealthPoints}";

                playerHitLabel.text       = $"HIT {damageSkill.GetHitChance(playerUnit, targetUnit)}";
                playerOffensiveLabel.text = $"OFF {damageSkill.GetOffensive(playerUnit)}";
                playerDefensiveLabel.text = $"DEF {DamageCalculator.GetDefensive(targetUnit, playerUnit)}";
                playerCritLabel.text      = $"CRT {damageSkill.GetCritRate(playerUnit, targetUnit)}";

                playerPotentialHpLabel.gameObject.SetActive(true);
                if (defenderAttackPositions.Contains(attackingUnitNewPosition))
                {
                    var playerHealthLost = 0;
                    if (targetUnit.Speed.Value > playerUnit.Speed.Value)
                    {
                        playerHealthLost = damageSkill.SkillCost + (2 * DamageCalculator.GetDamage(targetUnit, playerUnit));
                        enemyFollowupPossibleLabel.gameObject.SetActive(true);
                    }
                    else
                    {
                        playerHealthLost = damageSkill.SkillCost + DamageCalculator.GetDamage(targetUnit, playerUnit);
                        enemyFollowupPossibleLabel.gameObject.SetActive(false);
                    }

                    if (playerUnit.HealthPoints - playerHealthLost <= 0)
                    {
                        playerPotentialHpLabel.text = "0";
                    }
                    else
                    {
                        playerPotentialHpLabel.text = $"{playerUnit.HealthPoints - playerHealthLost}";
                    }

                    enemyAttackMethodLabel.text = targetUnit.MainWeapon.Name;

                    enemyHitLabel.text       = $"HIT {DamageCalculator.GetHitChance(targetUnit, playerUnit)}";
                    enemyOffensiveLabel.text = $"OFF {DamageCalculator.GetOffensive(targetUnit)}";
                    enemyCritLabel.text      = $"CRT {DamageCalculator.GetCritRate(targetUnit, playerUnit)}";
                }
                else
                {
                    playerPotentialHpLabel.text = $"{playerUnit.HealthPoints - skill.SkillCost}";
                    enemyAttackMethodLabel.text = "";
                    enemyHitLabel.text          = $"HIT 0";
                    enemyOffensiveLabel.text    = $"OFF 0";
                    enemyCritLabel.text         = $"CRT 0";
                }

                enemyNameLabel.text      = targetUnit.Name;
                enemyCurrentHpLabel.text = $"CUR {targetUnit.HealthPoints}";

                enemyPotentialHpLabel.gameObject.SetActive(true);
                var enemyHealthLost = damageSkill.GetDamage(playerUnit, targetUnit);
                if (targetUnit.HealthPoints - enemyHealthLost <= 0)
                {
                    enemyPotentialHpLabel.text = $"0";
                }
                else
                {
                    enemyPotentialHpLabel.text = $"{targetUnit.HealthPoints - damageSkill.GetDamage(playerUnit, targetUnit)}";
                }

                enemyMaxHpLabel.text     = $"MAX {targetUnit.MaxHealthPoints}";
                enemyDefensiveLabel.text = $"DEF {damageSkill.GetDefensive(playerUnit, targetUnit)}";
            }
            else
            {
                targetUnitInformationBackground.color = ALLY_COLOR;

                var defenderPosition        = gameViewModel.GetPositionOfUnit(targetUnit);
                var defenderAttackPositions = gameViewModel.GetSurroundingLocationsAtPoint(defenderPosition, targetUnit.MainWeapon.Range);
                var supportSkill            = skill as SingleSupportSkill;

                playerNameLabel.text         = playerUnit.Name;
                playerAttackMethodLabel.text = $"{supportSkill.SkillName}";
                playerCurrentHpLabel.text    = $"CUR {playerUnit.HealthPoints}";
                playerMaxHpLabel.text        = $"MAX {playerUnit.MaxHealthPoints}";

                playerHitLabel.text       = $"HIT ";
                playerOffensiveLabel.text = $"OFF ";
                playerDefensiveLabel.text = $"DEF ";
                playerCritLabel.text      = $"CRT ";

                playerPotentialHpLabel.gameObject.SetActive(true);
                playerPotentialHpLabel.text = $"{playerUnit.HealthPoints - skill.SkillCost}";

                enemyNameLabel.text         = targetUnit.Name;
                enemyAttackMethodLabel.text = "";
                enemyCurrentHpLabel.text    = $"CUR {targetUnit.HealthPoints}";

                enemyPotentialHpLabel.gameObject.SetActive(true);

                var targetUnitHealthGain = supportSkill.GetHealAmount(playerUnit, targetUnit);
                if (targetUnitHealthGain == 0)
                {
                    enemyPotentialHpLabel.gameObject.SetActive(false);
                    enemyPotentialHpLabel.text = $"0";
                }
                else
                {
                    enemyPotentialHpLabel.gameObject.SetActive(true);
                    if (targetUnit.HealthPoints + targetUnitHealthGain > targetUnit.MaxHealthPoints.Value)
                    {
                        enemyPotentialHpLabel.text = $"{targetUnit.MaxHealthPoints.Value}";
                    }
                    else
                    {
                        enemyPotentialHpLabel.text = $"{targetUnit.HealthPoints + targetUnitHealthGain}";
                    }
                }

                enemyMaxHpLabel.text     = $"MAX {targetUnit.MaxHealthPoints}";
                enemyDefensiveLabel.text = $"DEF ";
            }
        }
Пример #10
0
        private static void Main()
        {
            var builder  = new MonsterSpeciesBuilder();
            var director = new MonsterSpeciesDirector(builder);

            var slimeSpecies = director.CreateSlime();
            var wolfSpecies  = director.CreateWolf();

            // System.Console.WriteLine();
            // foreach (var level in new[] {/*1, 5, 10, 20, 30, */40})
            // {
            //     var attacker = new Monster(slimeSpecies, level);
            //     var defender = new Monster(slimeSpecies, level);
            //
            //     DamageReporter.ReportDamage(attacker, defender);
            //
            //     System.Console.WriteLine();
            // }
            // return;

            var playerTeam = new List <Monster>
            {
                new(wolfSpecies, 40),
                // new(slimeSpecies, 40),
            };

            var enemyTeam = new List <Monster>
            {
                new(slimeSpecies, 40),
                // new(slimeSpecies, 40),
            };

            var localPlayerController = new LocalPlayerController();
            var simpleAIController    = new LocalPlayerController();
            // var simpleAIController = new SimpleAIController();

            var combatSystem = new CombatSystem(playerTeam, localPlayerController);

            combatSystem.InitiateCombat(enemyTeam, simpleAIController);

            while (combatSystem.IsInProgress)
            {
                // check for all monster deaths on one side

                var activeMonster = combatSystem.GetNextActiveMonster();

                if (activeMonster == null)
                {
                    combatSystem.Tick();
                    continue;
                }

                combatSystem.CurrentTurn++;

                System.Console.WriteLine($"Turn {combatSystem.CurrentTurn}");

                var           controller = activeMonster.Controller;
                var           skill      = controller.GetSkill(activeMonster);
                CombatMonster target;

                if (skill.Target == Target.Enemy)
                {
                    var eligibleTargets = combatSystem.AllMonsters
                                          .Where(m => m.Controller != controller)
                                          .Where(m => m.IsAlive)
                                          .ToList();

                    target = controller.GetTarget(eligibleTargets);

                    foreach (var component in skill.Components)
                    {
                        if (component is DamageComponent damageComponent)
                        {
                            float damage = DamageCalculator.GetDamage(
                                activeMonster.CurrentStats[Stat.Attack],
                                target.CurrentStats[Stat.Defense]
                                );

                            damage *= damageComponent.DamageMultiplier;

                            var realDamage = (int)Math.Round(damage);

                            var targetMaxHealth  = target.Monster.Stats[Stat.Health];
                            var damagePercentage = (1 - (float)(targetMaxHealth - realDamage) / targetMaxHealth) * 100;

                            target.CurrentStats[Stat.Health] -= realDamage;

                            var sb = new StringBuilder();

                            sb.Append(activeMonster.Monster.Species.Name);
                            sb.Append($" (Lv {activeMonster.Monster.Level})");
                            sb.Append(" uses ");
                            sb.Append(skill.Name);
                            sb.Append(" on ");
                            sb.Append(target.Monster.Species.Name);
                            sb.Append($" (Lv {target.Monster.Level})");
                            sb.Append(" for ");
                            sb.Append(realDamage);
                            sb.Append($" ({damagePercentage:F}%) damage");

                            System.Console.WriteLine(sb);
                        }
                        else if (component is StatusEffectComponent)
                        {
                            // todo
                        }
                        else
                        {
                            // todo
                        }
                    }
                }
                else if (skill.Target == Target.Friendly)
                {
                    var eligibleTargets = combatSystem.AllMonsters
                                          .Where(m => m.Controller == controller)
                                          .Where(m => m.IsAlive)
                                          .ToList();

                    target = controller.GetTarget(eligibleTargets);

                    // todo
                }
                else
                {
                    // todo
                }

                System.Console.WriteLine();
                activeMonster.AttackBar = 0;
            }
        }
    }
}