Combat h.nss
From Dragon Age Toolset Wiki
Contents
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; }