Combat PerformAttack

From Dragon Age Toolset Wiki
Jump to: navigation, search

Called by:

Calls:

Parameters:

Variables:

Returns:

Used for:

/**
*  @brief Handles processing an Attack Command
*
*  @param oAttacker       The command owner, usually OBJECT_SELF
*  @param oTarget         The Target of the command
*
*  @returns CombatAttackResultStruct with damage and attackresult populated
*
*  "Don't touch this if you want to live"
*
*  @author Georg Zoeller
**/
struct CombatAttackResultStruct Combat_PerformAttack(object oAttacker, object oTarget, object oWeapon , float fDamageOverride = 0.0f, int nAbility = 0);
struct CombatAttackResultStruct Combat_PerformAttack(object oAttacker, object oTarget, object oWeapon ,  float fDamageOverride = 0.0f, int nAbility = 0)
{
    struct  CombatAttackResultStruct stRet;
    float   fDamage = 0.0f;
    int     nAttackType = Combat_GetAttackType(oAttacker, oWeapon);
 
    stRet.fAttackDuration =    ATTACK_LOOP_DURATION_INVALID ;
 
    // -------------------------------------------------------------------------
    // Attack check happens here...
    // -------------------------------------------------------------------------
 
    stRet.nAttackResult = Combat_GetAttackResult(oAttacker, oTarget, oWeapon );
 
    // -------------------------------------------------------------------------
    // If attack result was not a miss, go on to calculate damage
    // -------------------------------------------------------------------------
    if (stRet.nAttackResult != COMBAT_RESULT_MISS)
    {
        int bCriticalHit = (stRet.nAttackResult == COMBAT_RESULT_CRITICALHIT);
 
        // -------------------------------------------------------------------------
        // If attack result was not a miss, check if we need to handle a deathblow
        // -------------------------------------------------------------------------
        fDamage = ( (fDamageOverride == 0.0f) ?
                          Combat_Damage_GetAttackDamage(oAttacker, oTarget, oWeapon, stRet.nAttackResult) : fDamageOverride);
 
 
        float fTargetHealth = GetCurrentHealth( oTarget );
 
 
        // -----------------------------------------------------------------
        //  Ranged weapons attacks are not synched and therefore we never
        //  need to worry about reporting deathblows to the engine.
        // ---------------------------------------------------------------------
 
        // ---------------------------------------------------------------------
        // When not using a ranged weapon, there are synchronize death blows to handle
        // ---------------------------------------------------------------------
        if ( nAttackType != ATTACK_TYPE_RANGED && nAbility == 0 && stRet.nAttackResult != COMBAT_RESULT_MISS )
        {
 
            // -----------------------------------------------------------------
            // Deathblows against doors look cool, but really...
            // -----------------------------------------------------------------
            if (  GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
            {
 
                // ---------------------------------------------------------
                // Special conditions.
                //
                // There are a few cases in the single player campaign
                // where we want the spectacular deathblow to occur if possible.
                //
                // The following logic defines these conditions
                // ---------------------------------------------------------
                int nAppearance =   GetAppearanceType(oTarget) ;
                int nRank       = GetCreatureRank(oTarget);
 
                int bSpecial =   FALSE;
 
                // ---------------------------------------------------------
                // ... all boss ogres (there's 1 in the official campaign) by request
                //     from Dr. Muzyka.
                // ... all elite bosses
                // ---------------------------------------------------------
                if ( (nAppearance == APR_TYPE_OGRE || (nRank == CREATURE_RANK_BOSS||nRank == CREATURE_RANK_ELITE_BOSS)  ) ||
                      nRank == CREATURE_RANK_ELITE_BOSS)
                {
                        // -------------------------------------------------
                        // ... but only if they are at the health threshold
                        //     required for deathblows to trigger
                        // -------------------------------------------------
                        if (IsHumanoid(oAttacker))
                        {
                            bSpecial = _GetRelativeResourceLevel(oTarget, PROPERTY_DEPLETABLE_HEALTH) < SPECIAL_BOSS_DEATHBLOW_THRESHOLD;
                        }
                }
 
                // ---------------------------------------------------------
                // Deathblows occur when
                //  ... target isn't immortal (duh) AND
                //      ... the damage of the hit exceeds the creature's health OR
                //      ... aforementioned 'special' conditions are met.
                // ---------------------------------------------------------
                if ( (!IsImmortal( oTarget ) && (fDamage >= fTargetHealth || bSpecial)))
                {
 
                    // -----------------------------------------------------
                    // ... only from party members AND
                    //    ... if we determine that a deathblow doesn't interrupt gameplay OR
                    //    ... aforementioned 'special' conditions are met
                    // -----------------------------------------------------
                    if (IsPartyMember(oAttacker) && (CheckForDeathblow(oAttacker, oTarget) || bSpecial))
                    {
                        // -------------------------------------------------
                        // Verify some more conditions...
                        // -------------------------------------------------
                        int bDeathBlow = Combat_GetValidDeathblow(oAttacker, oTarget);
                        if (bDeathBlow)
                        {
                            stRet.nAttackResult = COMBAT_RESULT_DEATHBLOW ;
 
                            // ---------------------------------------------
                            // Special treatment for ogre
                            // Reason: The ogre, unlike all other special bosses
                            //         has a second, non spectacular deathblow.
                            //         if we specify 0, there's a 50% chance that
                            //         one is played, which we don't want in this
                            //         case, so we're passing the id of the
                            //         spectacular one instead.
                            // ---------------------------------------------
                            if (bSpecial && nAppearance == APR_TYPE_OGRE)
                            {
                                stRet.nDeathblowType = 5; // 5 - ogre slowmo deathblow
                            }
                            else
                            {
                                stRet.nDeathblowType = 0;  // 0 - auto select in engine;
                            }
 
                        }
                        else
                        {
                            // Failure to meet conditions: convert to hit.
                            if (stRet.nAttackResult != COMBAT_RESULT_BACKSTAB)
                            {
                                stRet.nAttackResult = COMBAT_RESULT_HIT;
                            }
                        }
                    }
                    else
                    {
                        // Failure to meet conditions: convert to hit.
                        if (stRet.nAttackResult != COMBAT_RESULT_BACKSTAB)
                        {
                            stRet.nAttackResult = COMBAT_RESULT_HIT;
                        }
                    }
 
 
 
                 } /* ishumanoid*/
 
 
            } /* obj_type creature*/
        }
 
 
    }
    int nDamageType = DAMAGE_TYPE_PHYSICAL;
 
    if ( nAttackType == ATTACK_TYPE_RANGED)
    {
        // ---------------------------------------------------------------------
        // Certain projectiles modifyt the damage type done by a ranged weapon
        // This is defined in PRJ_BASE.
        // ---------------------------------------------------------------------
        int nProjectileIndex = GetLocalInt(oWeapon,PROJECTILE_OVERRIDE);
        if (nProjectileIndex)
        {
            int nDamageTypeOverride = GetM2DAInt(TABLE_PROJECTILES,"DamageType",nProjectileIndex);
            if (nDamageTypeOverride>0)
            {
                nDamageType = nDamageTypeOverride;
            }
        }
 
        // ---------------------------------------------------------------------
        // When using a ranged weapon, we need to report the duration of the
        // aim loop to the engine
        // ---------------------------------------------------------------------
        stRet.fAttackDuration = GetCreatureRangedDrawSpeed(oAttacker, oWeapon);
    }
    else
    {
        float fSpeed = CalculateAttackTiming(oAttacker, oWeapon);
        if (fSpeed>0.0f)
        {
            stRet.fAttackDuration = fSpeed;
        }
    }
 
    // -------------------------------------------------------------------------
    // The Impact effect is not a real effect - it is not ever applied. Instead
    // it is used to marshal information about the attack back to the impact
    // event.
    // -------------------------------------------------------------------------
    stRet.eImpactEffect = EffectImpact(fDamage, oWeapon, 0, 0, nDamageType);
 
    #ifdef DEBUG
    Log_Trace_Combat("combat_h.Combat_PerformAttack"," Attack Result: " + Log_GetAttackResultNameById(stRet.nAttackResult),  oAttacker, oTarget, LOG_CHANNEL_COMBAT_TOHIT );
    #endif
 
    return stRet;
}