Exemplo n.º 1
0
 /// <summary>
 /// Rolls attacks for either attackers or defenders
 /// </summary>
 /// <param name="units">Unit stacks making attacks</param>
 /// <param name="results">List storing attack roll results</param>
 private void RollAttacks(List <UnitStack> units, List <AttackRollResultsCollection> results)
 {
     for (int i = 0; i < units.Count; i++)
     {
         if (!units[i].IsAffectedBy("Confusion"))
         {
             List <Attack> phaseAttacks = units[i].GetUnitType().GetAttacksForPhase(_currentPhase);
             if (phaseAttacks.Count > 0)
             {
                 AttackRollResultsCollection unitStackAttackRolls = new AttackRollResultsCollection();
                 for (int j = 0; j < units[i].GetTotalQty(); j++)
                 {
                     for (int k = 0; k < phaseAttacks.Count; k++)
                     {
                         for (int l = 0; l < phaseAttacks[k].GetNumberOfAttacks(); l++)
                         {
                             AttackRollResult result = CombatHelper.Instance.CreateAnAttackRollResult(units[i], phaseAttacks[k]);
                             unitStackAttackRolls.AddAttackRollResult(result);
                         }
                     }
                 }
                 results.Add(unitStackAttackRolls);
             }
         }
     }
     results.Sort((x, y) => x.GetAt(0).AttackSkill.CompareTo(y.GetAt(0).AttackSkill));
 }
Exemplo n.º 2
0
    /// <summary>
    /// Calculate mathematical expectation of the weapon damage value
    /// </summary>
    /// <param name="attackRollResult">Result of the attack roll</param>
    /// <param name="defender">Defending unit stack</param>
    /// <param name="totalDefense">Defender's total defense against the attack</param>
    /// <param name="armor">Defender's armor value against the attack</param>
    /// <param name="maxValue">Max value the damage can reach</param>
    /// <returns>Mathematical expectation of the weapon damage value</returns>
    public double EstimateWeaponDamage(AttackRollResult attackRollResult, UnitStack defender, int totalDefense, int armor, int maxValue)
    {
        double result = 0;

        if ((IsUsingCriticals() && attackRollResult.IsCritical) || (attackRollResult.AttackRoll + attackRollResult.AttackSkill > totalDefense))
        {
            result = 0.5 * (attackRollResult.Attack.GetMinDamage() + attackRollResult.Attack.GetMaxDamage());
            if (IsUsingCriticals() && attackRollResult.IsCritical)
            {
                result *= 2;
            }
            if (armor > 0)
            {
                result -= armor;
            }
            if (result < 0)
            {
                result = 0;
            }
            if (result > maxValue)
            {
                result = maxValue;
            }
        }
        return(result);
    }
Exemplo n.º 3
0
        private void RenderAttackRollResult(StringBuilder resHtml, AttackRollResult ar)
        {
            if (ar.Character != null)
            {
                AppendCharacterHeader(resHtml, ar.Character);
            }

            resHtml.AppendOpenTagWithClass("span", "weaponheader");
            if (ar.Attack.Weapon == null)
            {
                resHtml.AppendSmallIcon("sword");
            }
            else if (ar.Attack.Weapon.Ranged)
            {
                resHtml.AppendSmallIcon("bow");
            }
            else if (ar.Attack.Weapon.Natural)
            {
                resHtml.AppendSmallIcon("claw");
            }
            else
            {
                resHtml.AppendSmallIcon("sword");
            }

            resHtml.AppendHtml(ar.Name.Capitalize());
            resHtml.AppendLineBreak();
            resHtml.AppendCloseTag("span");

            foreach (SingleAttackRoll res in ar.Rolls)
            {
                RenderRollResult(resHtml, res.Result, false, ar.Attack.CritRange);

                resHtml.AppendHtml(" Dmg: ");
                RenderRollResult(resHtml, res.Damage);

                foreach (BonusDamage bd in res.BonusDamage)
                {
                    resHtml.Append(" + ");
                    RenderRollResult(resHtml, bd.Damage);
                    resHtml.Append(" " + bd.DamageType);
                }

                if (res.CritResult != null)
                {
                    resHtml.AppendHtml(" Crit: ");
                    RenderRollResult(resHtml, res.CritResult, res.CritResult.Rolls[0].Result == 1);

                    if (res.CritResult.Rolls[0].Result != 1)
                    {
                        resHtml.AppendHtml(" Dmg: ");
                    }
                    RenderRollResult(resHtml, res.CritDamage);
                }
            }
        }
    /// <summary>
    /// Remove an attack roll result from the collection
    /// </summary>
    /// <param name="result">Attack roll result to remove</param>
    /// <returns>Whether the operation was successful</returns>
    public bool RemoveAttackRollResult(AttackRollResult result)
    {
        bool success = _model.Remove(result);

        if (success && Updated != null)
        {
            Updated(this, EventArgs.Empty);
        }
        return(success);
    }
Exemplo n.º 5
0
        public void RollAttack(Attack atk, Character ch)
        {
            AttackRollResult res = new AttackRollResult(atk);

            res.Character = ch;

            _Results.Insert(0, res);

            TrimList();

            RenderResults();
        }
Exemplo n.º 6
0
 /// <summary>
 /// Class constructor
 /// </summary>
 /// <param name="attack">Attack roll result</param>
 /// <param name="target">Target unit defending against the attack</param>
 /// <param name="defensePositiveDieRoll">Value of the defense's positive roll</param>
 /// <param name="defenseNegativeDieRoll">Value of the defense's negative roll</param>
 /// <param name="defenseSkill">Value of the skill used in defense</param>
 /// <param name="shield">Shield value used in the defense roll</param>
 public AttackResolutionEvent(AttackRollResult attack,
                              UnitStack target,
                              int defensePositiveDieRoll,
                              int defenseNegativeDieRoll,
                              int defenseSkill,
                              int shield)
 {
     _attack = attack;
     _target = target;
     _defensePositiveDieRoll = defensePositiveDieRoll;
     _defenseNegativeDieRoll = defenseNegativeDieRoll;
     _defenseSkill           = defenseSkill;
     _shield = shield;
 }
 /// <summary>
 /// Add another attack roll result to the collection
 /// </summary>
 /// <param name="result">Attack roll result to add</param>
 /// <returns>Whether the operation was successful</returns>
 public bool AddAttackRollResult(AttackRollResult result)
 {
     if (_model.Count > 0 && result.UnitStack != _unitStack)
     {
         return(false);
     }
     _model.Add(result);
     _unitStack = result.UnitStack;
     if (Updated != null)
     {
         Updated(this, EventArgs.Empty);
     }
     return(true);
 }
Exemplo n.º 8
0
    /// <summary>
    /// Calculate damage inflicted by an attack
    /// </summary>
    /// <param name="attackRollResult">Attack roll result</param>
    /// <param name="defender">Defending unit stack</param>
    /// <param name="totalDefense">Defender's total defense value</param>
    /// <param name="armor">Defender's armor value</param>
    /// <returns>Damage inflicted by the attack to the defender</returns>
    public int CalculateDamage(AttackRollResult attackRollResult, UnitStack defender, int totalDefense, int armor)
    {
        int result = 0;

        if ((IsUsingCriticals() && attackRollResult.IsCritical) || (attackRollResult.AttackRoll + attackRollResult.AttackSkill > totalDefense))
        {
            result = attackRollResult.FullDamage;
            if (armor > 0)
            {
                result = Mathf.Max(0, result - armor);
            }
        }
        return(result);
    }
Exemplo n.º 9
0
    /// <summary>
    /// Generate an attack roll result
    /// </summary>
    /// <param name="attacker">Unit stack representing the attacker</param>
    /// <param name="attack">Attack object for roll result generation</param>
    /// <param name="plusDie">Value of the positive die's roll (pass zero to actually roll)</param>
    /// <param name="minusDie">Value of the negative die's roll (pass zero to actually roll)</param>
    /// <returns>Whether the die roll represents a critical success</returns>
    public AttackRollResult CreateAnAttackRollResult(UnitStack attacker, Attack attack, int plusDie = 0, int minusDie = 0)
    {
        bool isCritical      = false;
        int  positiveDieRoll = plusDie == 0 ? Dice.RollDie(_diceSides) : plusDie;
        int  negativeDieRoll = minusDie == 0 ? Dice.RollDie(_diceSides) : minusDie;
        int  dieRoll         = positiveDieRoll - negativeDieRoll;
        int  bonusDamage     = 0;

        if (IsCritical(dieRoll))
        {
            isCritical  = true;
            bonusDamage = attack.RollDamage();
        }
        AttackRollResult result = new AttackRollResult(attacker, attack, positiveDieRoll, negativeDieRoll, attack.GetSkill(), attack.RollDamage(), bonusDamage, isCritical);

        return(result);
    }
Exemplo n.º 10
0
    /// <summary>
    /// Estimate results of a unit stack attacking another during a specified turn phase
    /// </summary>
    /// <param name="attacker">Attacking unit stack</param>
    /// <param name="defender">Defending unit stack</param>
    /// <param name="phase">Turn phase</param>
    /// <returns>Mathematical expectation of the attack damage value</returns>
    public double EstimateStackAttacksDamage(UnitStack attacker, UnitStack defender, Combat.TurnPhase phase)
    {
        double result = GetEstimatedDamage(attacker, defender, phase);

        if (result >= 0)
        {
            return(result);
        }
        result = 0;
        int           maxHP   = defender.GetUnitType().GetHitPoints();
        List <Attack> attacks = attacker.GetUnitType().GetAttacksForPhase(phase);

        for (int i = 0; i < attacks.Count; i++)
        {
            int numberOfAttacks = attacks[i].GetNumberOfAttacks();
            for (int attackerPlusDie = 0; attackerPlusDie < _diceSides; attackerPlusDie++)
            {
                for (int attackerMinusDie = 0; attackerMinusDie < _diceSides; attackerMinusDie++)
                {
                    AttackRollResult attackRollResult = CreateAnAttackRollResult(attacker, attacks[i],
                                                                                 attackerPlusDie + 1, attackerMinusDie + 1);

                    int defensiveSkill = CalculateDefensiveSkill(attackRollResult.Attack, defender);
                    int shield         = CalculateShieldValue(attackRollResult.Attack, defender);
                    int armor          = CalculateArmorValue(attackRollResult.Attack, defender, attackRollResult.IsCritical);

                    for (int defenderPlusDie = 0; defenderPlusDie < _diceSides; defenderPlusDie++)
                    {
                        for (int defenderMinusDie = 0; defenderMinusDie < _diceSides; defenderMinusDie++)
                        {
                            // both defenderPlusDie and defenderMinusDie would add +1 to them,
                            // so the +1s would cancel each other
                            int defenseRoll  = defenderPlusDie - defenderMinusDie;
                            int totalDefense = defenseRoll + defensiveSkill + shield;
                            result += numberOfAttacks * EstimateWeaponDamage(attackRollResult, defender, totalDefense, armor, maxHP);
                        }
                    }
                }
            }
        }
        result *= _damageScale;
        SetEstimatedDamage(attacker, defender, phase, result);
        return(result);
    }
Exemplo n.º 11
0
    /// <summary>
    /// Calculate mathematical expectation of the attack damage value
    /// </summary>
    /// <param name="attackRollResult">Attack roll result object representing the attack</param>
    /// <param name="defender">Defending unit stack</param>
    /// <returns>Mathematical expectation of the attack damage value</returns>
    public int EstimateAttackDamage(AttackRollResult attackRoll, UnitStack defender)
    {
        int    result      = 0;
        double totalDamage = 0;
        int    maxHP       = defender.GetUnitType().GetHitPoints();

        for (int attackerPlusDie = 0; attackerPlusDie < _diceSides; attackerPlusDie++)
        {
            for (int attackerMinusDie = 0; attackerMinusDie < _diceSides; attackerMinusDie++)
            {
                AttackRollResult attackRollResult = CreateAnAttackRollResult(attackRoll.UnitStack, attackRoll.Attack,
                                                                             attackerPlusDie + 1, attackerMinusDie + 1);

                int defensiveSkill = CalculateDefensiveSkill(attackRollResult.Attack, defender);
                int shield         = CalculateShieldValue(attackRollResult.Attack, defender);
                int armor          = CalculateArmorValue(attackRollResult.Attack, defender, attackRollResult.IsCritical);

                for (int defenderPlusDie = 0; defenderPlusDie < _diceSides; defenderPlusDie++)
                {
                    for (int defenderMinusDie = 0; defenderMinusDie < _diceSides; defenderMinusDie++)
                    {
                        // both defenderPlusDie and defenderMinusDie would add +1 to them,
                        // so the +1s would cancel each other
                        int defenseRoll  = defenderPlusDie - defenderMinusDie;
                        int totalDefense = defenseRoll + defensiveSkill + shield;
                        totalDamage += EstimateWeaponDamage(attackRollResult, defender, totalDefense, armor, maxHP);
                    }
                }
            }
        }

        totalDamage *= _damageScale;
        result       = Convert.ToInt32(Math.Floor(totalDamage));
        totalDamage -= result;

        System.Random rando = new System.Random();
        if (totalDamage > rando.NextDouble())
        {
            result++;
        }
        return(result);
    }
Exemplo n.º 12
0
    /// <summary>
    /// Is the unit stack a valid target for the attack?
    /// </summary>
    /// <param name="target">Unit stack which is a potential target for the attack</param>
    /// <param name="attackRollResult">Attack roll result representing the attack</param>
    /// <returns>Whether the unit stack is a valid target for the attack</returns>
    private bool IsValidAttackTarget(UnitStack target, AttackRollResult attackRollResult)
    {
        if (_attackers.Contains(target) && _attackers.Contains(attackRollResult.UnitStack))
        {
            FileLogger.Trace("COMBAT", "Both " + attackRollResult.UnitStack.GetUnitType().GetName() + " and " + target.GetUnitType().GetName() + " belong to the attacker");
            return(false);
        }

        if (_defenders.Contains(target) && _defenders.Contains(attackRollResult.UnitStack))
        {
            FileLogger.Trace("COMBAT", "Both " + attackRollResult.UnitStack.GetUnitType().GetName() + " and " + target.GetUnitType().GetName() + " belong to the defender");
            return(false);
        }

        if (target.GetTotalQty() == 0)
        {
            FileLogger.Trace("COMBAT", "No defenders left in this stack");
            return(false);
        }
        return(true);
    }
Exemplo n.º 13
0
        public void RollAttackSet(AttackSet atkSet, Character ch)
        {
            AttackSetResult res = new AttackSetResult();

            res.Character = ch;

            foreach (Attack at in atkSet.WeaponAttacks)
            {
                AttackRollResult ares = new AttackRollResult(at);
                res.Results.Add(ares);
            }
            foreach (Attack at in atkSet.NaturalAttacks)
            {
                AttackRollResult ares = new AttackRollResult(at);
                res.Results.Add(ares);
            }


            _Results.Insert(0, res);

            TrimList();

            RenderResults();
        }
Exemplo n.º 14
0
 public AttackResult(AttackRollResult result, List <int> rngs, int attackDamage)
 {
     Result          = result;
     RandNumbersUsed = rngs;
     BaseDamage      = attackDamage;
 }
 /// <summary>
 /// Check if the collection contains an attack roll result
 /// </summary>
 /// <param name="attackRoll">Attack roll result to check</param>
 /// <returns>Whether the collection contains this attack roll result</returns>
 public bool Contains(AttackRollResult attackRoll)
 {
     return(_model.Contains(attackRoll));
 }
Exemplo n.º 16
0
    // TODO: handle the situation when one side is eliminated completely (end the combat)
    public bool ResolveAttack(UnitGroup targetGroup, int index, AttackRollResult attack)
    {
        Unit target;
        if (index > 0)
        {
            target = targetGroup.GetUnit(index);

            if (target == null)
            {
                Debug.Log("Invalid attack resolution target!");
                return false;
            }

            if (target.Quantity <= 0)
            {
                Debug.Log("Zero Quantity!");
                return false;
            }
        }
        else
        {
            target = targetGroup.GetRandomUnit();
        }

        if (attackers.Contains(targetGroup) && attackers.Contains(attack.UnitGroup))
        {
            Debug.Log("Invalid attack resolution target!");
            return false;
        }

        if (defenders.Contains(targetGroup) && defenders.Contains(attack.UnitGroup))
        {
            Debug.Log("Invalid attack resolution target!");
            return false;
        }

        int defenseRoll = Dice.RollDie(10) + Dice.RollDie(10) + target.UnitType.Defense;
        if ((useCriticals && attack.IsCritical) || (attack.Attack > defenseRoll))
        {
            Debug.Log(attack.Attack.ToString() + " attack vs " + defenseRoll + " defense: hit!");
            int damage = attack.FullDamage;
            if (!attack.UnitGroup.UnitType.Attack.IsAP)
            {
                damage -= target.UnitType.Armor;
            }
            Debug.Log(attack.FullDamage.ToString() + " damage vs " + target.UnitType.Armor + " armor, result = " + damage);

            if (damage > 0)
            {
                target.Quantity -= 1;

                int newHP = target.CurrentHealth - damage;
                Debug.Log("HP: " + target.CurrentHealth + " - " + damage + " = " + newHP);

                if (newHP > 0)
                {
                    Unit reinforced = targetGroup.GetUnitWithHealth(newHP);
                    if (reinforced == null)
                    {
                        Debug.Log("Attempted to reinforce a null unit");
                        return false;
                    }
                    reinforced.Quantity += 1;
                }
            }
        }
        else
        {
            Debug.Log(attack.Attack.ToString() + " attack vs " + defenseRoll + " defense: miss.");
        }

        if (attackerRollResults.Contains(attack))
        {
            attackerRollResults.Remove(attack);
        }
        else
        {
            defenderRollResults.Remove(attack);
        }
        /*
        if (attackerRollResults.Count == 0 && defenderRollResults.Count == 0)
        {
            if (CurrentPhase == attackPhase.CHARGE)
            {
                CurrentPhase = attackPhase.MELEE;
            }
            else
            {
                CurrentPhase = attackPhase.CHARGE;
            }
        }
        */
        return true;
    }
Exemplo n.º 17
0
    /// <summary>
    /// Resolve an attack against the target unit stack
    /// </summary>
    /// <param name="target">Target unit stack</param>
    /// <param name="attackRollResult">Attack roll result representing the attack</param>
    /// <param name="useEstimates">Whether estimated results will be used or honest rolls will be made</param>
    /// <returns>Whether the attack was successfully resolved</returns>
    private bool ResolveAnAttackAgainstTarget(UnitStack target, AttackRollResult attackRollResult, bool useEstimates)
    {
        if (!IsValidAttackTarget(target, attackRollResult))
        {
            return(false);
        }

        int positiveDieRoll = CombatHelper.Instance.RollDie();
        int negativeDieRoll = CombatHelper.Instance.RollDie();
        int defenseRoll     = positiveDieRoll - negativeDieRoll;

        int defensiveSkill = CombatHelper.Instance.CalculateDefensiveSkill(attackRollResult.Attack, target);
        int shield         = CombatHelper.Instance.CalculateShieldValue(attackRollResult.Attack, target);

        AttackResolutionEvent data = new AttackResolutionEvent(attackRollResult, target, positiveDieRoll, negativeDieRoll, defensiveSkill, shield);
        int totalDefense           = defenseRoll + defensiveSkill + shield;

        int armor = CombatHelper.Instance.CalculateArmorValue(attackRollResult.Attack, target, attackRollResult.IsCritical);

        data.SetArmor(armor);

        int damage = 0;

        if (useEstimates)
        {
            damage = CombatHelper.Instance.EstimateAttackDamage(attackRollResult, target);
        }
        else
        {
            damage = CombatHelper.Instance.CalculateDamage(attackRollResult, target, totalDefense, armor);
        }
        data.SetDamage(damage);
        target.TakeDamage(damage);

        int totalAttack = attackRollResult.AttackRoll + attackRollResult.AttackSkill;

        if (useEstimates)
        {
            FileLogger.Trace("COMBAT", "Simulation: estimated damage = " + damage);
        }
        else
        {
            FileLogger.Trace("COMBAT", "Attack: " + totalAttack + ", defense: " + totalDefense + ", weapon damage: " + attackRollResult.FullDamage + ", armor: " + armor + ", resulting damage: " + damage);
        }

        bool deleted = false;

        for (int i = 0; i < _attackerRollResults.Count; i++)
        {
            if (_attackerRollResults[i].Contains(attackRollResult))
            {
                bool removed = _attackerRollResults[i].RemoveAttackRollResult(attackRollResult);
                if (!removed)
                {
                    FileLogger.Trace("COMBAT", "Failed to remove an attack roll result from a collection!");
                }
                if (_attackerRollResults[i].Count == 0)
                {
                    _attackerRollResults.RemoveAt(i);
                }
                deleted = true;
                break;
            }
        }

        if (!deleted)
        {
            for (int i = 0; i < _defenderRollResults.Count; i++)
            {
                if (_defenderRollResults[i].Contains(attackRollResult))
                {
                    bool removed = _defenderRollResults[i].RemoveAttackRollResult(attackRollResult);
                    if (!removed)
                    {
                        FileLogger.Trace("COMBAT", "Failed to remove an attack roll result from a collection!");
                    }
                    if (_defenderRollResults[i].Count == 0)
                    {
                        _defenderRollResults.RemoveAt(i);
                    }
                    break;
                }
            }
        }

        if (AttackResolved != null)
        {
            AttackResolved(this, data);
        }

        return(true);
    }
Exemplo n.º 18
0
 private AttackRollResult GenerateAttackRollResult(UnitGroup attacker)
 {
     bool isCritical = false;
     int dieRoll = Dice.RollDie(10) + Dice.RollDie(10);
     int bonusDamage = 0;
     if (useCriticals && dieRoll >= 18)
     {
         isCritical = true;
         bonusDamage = attacker.UnitType.Attack.RollDamage();
     }
     AttackRollResult result = new AttackRollResult(attacker, dieRoll + attacker.UnitType.Attack.Skill, attacker.UnitType.Attack.RollDamage(), bonusDamage, isCritical);
     return result;
 }