Combat GetAttackResult

From Dragon Age Toolset Wiki
Jump to: navigation, search

Called by:

Calls:

Parameters:

Variables:

Returns:

Used for:

/**
* @brief Determine whether or not an attack hits a target.
*
* Note that this function only calculates to hit and crits, death blows are
* determined in Combat_PerformAttack
*
* @param oAttacker  The attacker
* @param oTarget    The target that is being attacked
* @param nAbility   If != 0, it won't trigger backstabs and deathblows.
*
* @returns  COMBAT_RESULT_HIT, COMBAT_RESULT_CRITICALHIT or COMBAT_RESULT_MISS
*
* @author Georg Zoeller
*
**/
int Combat_GetAttackResult(object oAttacker, object oTarget, object oWeapon, float fBonus = 0.0f, int nAbility = 0);
int Combat_GetAttackResult(object oAttacker, object oTarget, object oWeapon, float fBonus = 0.0f, int nAbility = 0)
{
 
    // -------------------------------------------------------------------------
    // Debug
    // -------------------------------------------------------------------------
    int nForcedCombatResult = GetForcedCombatResult(oAttacker);
    if (nForcedCombatResult != -1)
    {
        #ifdef DEBUG
        Log_Trace_Combat("combat_h.GetAttackResult"," Skipped rules, FORCED RESULT IS:" + ToString(nForcedCombatResult), oTarget);
        #endif
        return nForcedCombatResult;
    }
 
 
 
    // -------------------------------------------------------------------------
    // Placeables are always hit
    // -------------------------------------------------------------------------
    if (GetObjectType(oTarget) == OBJECT_TYPE_PLACEABLE)
    {
        #ifdef DEBUG
        Log_Trace_Combat("combat_h.GetAttackResult"," Placeable, automatic result : COMBAT_RESULT_HIT", oTarget);
        #endif
        return COMBAT_RESULT_HIT;
    }
 
 
    // -------------------------------------------------------------------------
    // Displacement
    // -------------------------------------------------------------------------
    float fDisplace = GetCreatureProperty(oTarget, PROPERTY_ATTRIBUTE_DISPLACEMENT);
    float fRand = RandomFloat()*100.0;
    if ( fRand < fDisplace)
    {
        #ifdef DEBUG
        Log_Trace_Combat("combat_h.GetAttackResult"," Displacement effect kciked in, automatic result : COMBAT_RESULT_MISS", oTarget);
        #endif
 
        // ---------------------------------------------------------------------
        // if the target has the evasion talent, attribute this miss to the talent
        // (random 50% because the anim is interrupting)
        // ---------------------------------------------------------------------
        if (HasAbility(oTarget, ABILITY_TALENT_EVASION) && fRand < (20.0f - (RandomFloat()*10.0f)  ))
        {
                command currentCmd = GetCurrentCommand(oTarget);
                int nCmdType = GetCommandType(currentCmd);
                // Evasion only plays during attack and wait commands.
                if ( nCmdType == COMMAND_TYPE_WAIT || nCmdType == COMMAND_TYPE_ATTACK || nCmdType == COMMAND_TYPE_INVALID )
                {
                    ApplyEffectOnObject(EFFECT_DURATION_TYPE_INSTANT, Effect(1055), oTarget, 0.0f, oTarget, ABILITY_TALENT_EVASION);
                }
        }
 
        return COMBAT_RESULT_MISS;
    }
 
 
 
    int  nAttackType = Combat_GetAttackType(oAttacker, oWeapon);
    int nRet;
 
    // -------------------------------------------------------------------------
    // Get the attackers attack rating (includes effects and equipment stats)
    // -------------------------------------------------------------------------
    float   fAttackRating;
 
    if (GetBaseItemType(oWeapon) == BASE_ITEM_TYPE_STAFF)
    {
        // ---------------------------------------------------------------------
        // Staves always hit
        // ---------------------------------------------------------------------
        return COMBAT_RESULT_HIT;
    }
 
 
 
    fAttackRating =  GetCreatureAttackRating (oAttacker);
 
    // -------------------------------------------------------------------------
    // Add item stat (usually 0) along with scripted boni and attack bias.
    // -------------------------------------------------------------------------
 
    fAttackRating += GetItemStat(oWeapon,   ITEM_STAT_ATTACK) + fBonus + ATTACK_HIT_BIAS;
 
    // -------------------------------------------------------------------------
    // Easier difficulties grant the player a bonus.
    // -------------------------------------------------------------------------
    fAttackRating += Diff_GetRulesAttackBonus(oAttacker);
 
 
    // -------------------------------------------------------------------------
    // This section deals with figuring out which critical hit modifier (melee, ranged, etc)
    // to use for this attack.
    // -------------------------------------------------------------------------
    float   fCriticalHitModifier;
    int     nCriticalHitModifier  = (nAttackType == ATTACK_TYPE_RANGED) ?  CRITICAL_MODIFIER_RANGED : CRITICAL_MODIFIER_MELEE;
 
 
 
    fCriticalHitModifier = GetCreatureCriticalHitModifier(oAttacker, nCriticalHitModifier);
    fCriticalHitModifier += GetItemStat(oWeapon, ITEM_STAT_CRIT_CHANCE_MODIFIER);
 
    // -------------------------------------------------------------------------
    //  Bravery grants +3.5 critical hit per enemy past the first 2
    // -------------------------------------------------------------------------
    if (HasAbility(oAttacker, ABILITY_TALENT_BRAVERY))
    {
 
        int nEnemies = Max(0,GetArraySize(GetCreaturesInMeleeRing(oAttacker,0.0, 359.99f,TRUE,0))-2);
        fCriticalHitModifier +=  nEnemies * 3.5f;
    }
 
 
    // -------------------------------------------------------------------------
    // Calculate Flanking Bonus
    // -------------------------------------------------------------------------
    float fFlanking = Combat_GetFlankingBonus(oAttacker, oTarget);
    if (fFlanking > 0.0 )
    {
        // ---------------------------------------------------------------------
        // Also increase chance for critical hits by 1/5th of the flanking bonus
        // ---------------------------------------------------------------------
        fCriticalHitModifier *=  (1.0 + (fFlanking/5.0));
    }
 
    // -------------------------------------------------------------------------
    // Range plays a role too.
    // -------------------------------------------------------------------------
    float fDistance =  GetDistanceBetween(oAttacker, oTarget);
    float fNoPenaltyDistance = MaxF(POINT_BLANK_RANGE,GetItemStat(oWeapon, ITEM_STAT_OPTIMUM_RANGE));
    fDistance = MaxF(fDistance-fNoPenaltyDistance,0.0f);
 
    float fAttackRoll = 50.0;
    float fPenalties = fDistance; // every meter distance past the free range is -1!
    float fAttack = fAttackRating  + fAttackRoll + fFlanking - fPenalties;
 
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    // BEGIN SECTION CRITICAL HITS
    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
        int bThreatenCritical = (RandomFloat()*100.0f   < fCriticalHitModifier  );
 
        if (!bThreatenCritical)
        {
            // ---------------------------------------------------------------------
            // Attacking out of stealth always threatens crit.
            // ---------------------------------------------------------------------
            if (IsModalAbilityActive(oAttacker, ABILITY_SKILL_STEALTH_1))
            {
                bThreatenCritical = TRUE;
            }
 
            // -----------------------------------------------------------------
            // Death hex effect ... all hits are auto crit
            // -----------------------------------------------------------------
            if (GetHasEffects(oTarget, EFFECT_TYPE_DEATH_HEX))
            {
                bThreatenCritical = TRUE;
            }
 
            // -----------------------------------------------------------------
            // Autocrit effect
            // -----------------------------------------------------------------
            if (GetHasEffects(oAttacker, EFFECT_TYPE_AUTOCRIT))
            {
                bThreatenCritical = TRUE;
            }
        }
        else
        {
            // ---------------------------------------------------------------------
            // Double strike does not allow crits
            // ---------------------------------------------------------------------
            if (IsModalAbilityActive(oAttacker, ABILITY_TALENT_DUAL_WEAPON_DOUBLE_STRIKE))
            {
                bThreatenCritical = FALSE;
            }
 
            // rapid shot doesn't allow critical strikes
            if (IsModalAbilityActive(oAttacker, ABILITY_TALENT_RAPIDSHOT))
            {
                bThreatenCritical = FALSE;
            }
        }
 
 
        // ---------------------------------------------------------------------
        // Targets that have critical hit immunity can not be crit...
        // ---------------------------------------------------------------------
        if (bThreatenCritical)
        {
            Log_Trace_Combat("combat_h.GetAttackResult"," Critical hit averted, target has critical hit immunity", oTarget);
            bThreatenCritical = !HasAbility(oTarget, ABILITY_TRAIT_CRITICAL_HIT_IMMUNITY);
        }
 
 
 
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    // END SECTION CRITICAL HITS
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 
 
 
 
    // -------------------------------------------------------------------------
    // This section deals with calculating the defense values of the attack target
    // -------------------------------------------------------------------------
    float fDefenseRating =  GetCreatureDefense(oTarget) + ((nAttackType == ATTACK_TYPE_RANGED) ? GetCreatureProperty(oTarget,PROPERTY_ATTRIBUTE_MISSILE_SHIELD):0.0f);
 
    // -------------------------------------------------------------------------
    // Easier difficulties grant the player a bonus.
    // -------------------------------------------------------------------------
    fDefenseRating += Diff_GetRulesDefenseBonus(oTarget);
 
    // -------------------------------------------------------------------------
    // A hit is successful if the attack rating exceeds the defense rating
    // -------------------------------------------------------------------------
    if (RandomFloat()*100.0f <  fAttack - fDefenseRating)
    {
 
        // ---------------------------------------------------------------------
        // If we threatened a critical, we crit here, otherwise just report normal hit
        // ---------------------------------------------------------------------
        nRet =  ( (bThreatenCritical) ? COMBAT_RESULT_CRITICALHIT : COMBAT_RESULT_HIT  );
 
        if ( nAttackType == ATTACK_TYPE_MELEE)
        {
            // -----------------------------------------------------------------
            // If we are backstabbing, we change the result here if it
            // was a crit. Abilities never bs (anim priority)
            // -----------------------------------------------------------------
            if ( nAbility == 0 && Combat_CheckBackstab(oAttacker, oTarget, oWeapon,fFlanking))
            {
                    nRet =  COMBAT_RESULT_BACKSTAB;
            }
        }
 
    }
    // -------------------------------------------------------------------------
    // Miss...
    // -------------------------------------------------------------------------
    else
    {
        nRet = COMBAT_RESULT_MISS;
 
    }
 
    // -------------------------------------------------------------------------
    // Misdirection Hex
    // -------------------------------------------------------------------------
    if (GetHasEffects(oAttacker, EFFECT_TYPE_MISDIRECTION_HEX))
    {
        #ifdef DEBUG
        Log_Trace_Combat("combat_h.GetAttackResult"," Attacker under misdirection hex: Hits are misses, crits are hits.", oTarget);
        #endif
 
        if (nRet == COMBAT_RESULT_HIT || nRet == COMBAT_RESULT_BACKSTAB)
        {
            nRet = COMBAT_RESULT_MISS;
        }
        else if (nRet == COMBAT_RESULT_CRITICALHIT)
        {
            nRet = COMBAT_RESULT_HIT;
        }
    }
 
 
  //  return COMBAT_RESULT_BACKSTAB;
 
    #ifdef DEBUG
    Log_Trace_Combat("combat_h.GetAttackResult"," ToHit Calculation: " +
                     " fAttack:"  + ToString(fAttack) +
                     " = (fAttackRating: " + ToString(fAttackRating) +
                     " fAttackRoll:" + ToString(fAttackRoll) +
                     " (range penalty:" + ToString(fPenalties) + ")"+
                     " fFlanking: " + ToString(fFlanking) +
                     " fBonus(script): " + ToString(fBonus) +
                     ")" , oAttacker, oTarget, LOG_CHANNEL_COMBAT_TOHIT );
 
    Log_Trace_Combat("combat_h.GetAttackResult"," ToHit Calculation (2):  " +
                     " fDefenseRating: " + ToString(fDefenseRating) +
                     " fCriticalHitModifier: " + ToString(fCriticalHitModifier) +
                     " bThreatenCritical: " + ToString(bThreatenCritical), oAttacker, oTarget, LOG_CHANNEL_COMBAT_TOHIT );
 
    #endif
 
    return nRet;
 
}