Difference between revisions of "Combat h.nss"
From Dragon Age Toolset Wiki
Dom Queron (Talk | contribs) (Added Combat_GetFlankingBonus) |
(Added code for combat_getattackresult) |
||
Line 5: | Line 5: | ||
=== Notable Functions === | === 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. | ||
+ | |||
+ | <dascript> | ||
+ | /** | ||
+ | * @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) | ||
+ | { | ||
+ | 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; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | #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; | ||
+ | } | ||
+ | </dascript> | ||
Revision as of 04:27, 28 October 2009
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) { 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; } } #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; }