Combat h.nss

From Dragon Age Toolset Wiki
Jump to: navigation, search

Overview

Combat_h.nss handles the core rule resolution logic for non-talent combat interaction.


Notable Functions

Combat_GetAttackResult

This function is determines whether or not a weapon/unarmed attack hits and determines substates such as critical hit or backstab.

/**
* @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 / Dodge
    // -------------------------------------------------------------------------
    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)
        {            
            bThreatenCritical = !HasAbility(oTarget, ABILITY_TRAIT_CRITICAL_HIT_IMMUNITY);
 
            if (!bThreatenCritical)
              Log_Trace_Combat("combat_h.GetAttackResult"," Critical hit averted, target has critical hit immunity", oTarget);
        }
 
    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    // 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;
        }
    }
 
    #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;
}

Combat_CheckBackstab

// -----------------------------------------------------------------------------
// Check if backstab conditions are true
// -----------------------------------------------------------------------------
int Combat_CheckBackstab(object oAttacker, object oTarget, object oWeapon, float fFlankingBonus)
{
    // -------------------------------------------------------------------------
    // If we we are a rogue
    // -------------------------------------------------------------------------
    if (!HasAbility(oAttacker, ABILITY_TALENT_HIDDEN_ROGUE))
    {
        return FALSE;
    }
 
    // -------------------------------------------------------------------------
    // Only humanoid rigs have the backstab anim 
    // -------------------------------------------------------------------------
    if (!IsHumanoid(oAttacker))
    {
        return FALSE;
    }
 
    // -------------------------------------------------------------------------
    // And target is not immune
    // -------------------------------------------------------------------------
    if (HasAbility(oTarget, ABILITY_TRAIT_CRITICAL_HIT_IMMUNITY))
    {
        return FALSE;
    }
 
    // -------------------------------------------------------------------------
    // And attacker does not use double strike mode
    // -------------------------------------------------------------------------
    if (IsModalAbilityActive(oAttacker, ABILITY_TALENT_DUAL_WEAPON_DOUBLE_STRIKE))
    {
        return FALSE;
    }
 
    // -------------------------------------------------------------------------
    // We can only backstab if we are flanking.
    // -------------------------------------------------------------------------
    if (fFlankingBonus>0.0)
    {
        return TRUE;
    }
    else
    {
         /* Coup de grace*/
        if (HasAbility(oAttacker, ABILITY_TALENT_BACKSTAB))
        {
            if (GetHasEffects(oTarget, EFFECT_TYPE_STUN))
            {
                return TRUE;
            }
            else if (GetHasEffects(oTarget, EFFECT_TYPE_PARALYZE))
            {
                return TRUE;
            }
        }
 
    }
 
    return FALSE;
}


Combat_GetFlankingBonus

This function is invoked during to hit determination and returns the magnitude of the benefit the attacker may receive due to his facing and location relative to the target.

// -----------------------------------------------------------------------------
// Determine the magnitude of positional bonuses for the attacker
// -----------------------------------------------------------------------------
float Combat_GetFlankingBonus(object oAttacker, object oTarget)
{
 
    if (HasAbility(oTarget, ABILITY_TALENT_SHIELD_TACTICS))
    {
        if (IsUsingShield(oTarget))
            return 0.0;
    }
 
    if (GetHasEffects(oTarget, EFFECT_TYPE_FLANK_IMMUNITY))
    {
        return 0.0;
    }
 
    float fAngle = GetAngleBetweenObjects(oTarget, oAttacker);
    float fFactor = 0.0;
 
    // -------------------------------------------------------------------------
    // The maximum bonus from position is +15
    // -------------------------------------------------------------------------
    float fMaxModifier = 15.0;
 
    // The attackers ability to flank is stored in a creature property
    float fFlankingAngle = GetCreatureProperty(oAttacker, PROPERTY_ATTRIBUTE_FLANKING_ANGLE);
 
    if (fFlankingAngle <= 10.0 ) /*old savegames have this at 10*/
    {
        fFlankingAngle = 60.0; // old savegames need this to avoid divby0 later
    }
    else if (fFlankingAngle>180.0)
    {
        fFlankingAngle = 180.0;
    }
 
 
    // -------------------------------------------------------------------------
    // Combat movement expands the maximum bonus to +20
    // -------------------------------------------------------------------------
    if (HasAbility(oAttacker, ABILITY_TALENT_COMBAT_MOVEMENT))
    {
        fMaxModifier = 20.0;
    }
 
    if ( fMaxModifier <= 0.0 )
    {
        return 0.0;
    }
 
    if ( (fAngle>= (180.0 - fFlankingAngle) && fAngle<=(180.0 + fFlankingAngle )))
    {
        // Shield block negates flanking on the left.
 
        int bShieldBlock =  HasAbility(oTarget,ABILITY_TALENT_SHIELD_BLOCK);
        int bUsingShield = IsUsingShield(oTarget);
 
        if (!bShieldBlock || fAngle < 180.0 || (bShieldBlock && !bUsingShield) )
        {
            fFactor = (fFlankingAngle -  fabs( 180.0 - fAngle))/fFlankingAngle;
        }
 
    }
 
    // Only rogues get the full positional benefits on the battlefield,
    // everyone else gets half
    float fClassModifier = GetCreatureCoreClass(oAttacker) == CLASS_ROGUE?1.0f:0.5f;
 
 
    return fFactor * fMaxModifier * fClassModifier;
}