ff_u_att_plc

Handle an attack event.

Variables

ENABLE__FF_U_ATT_PLC: (string) (opt-in) Set to "Y" to enable unit.

cf: ff_u_att_feedbk, ff_u_att_plc, ff_u_feedback

Source code

// @code

#include "ff_i_core"

const string ENABLE__FF_U_ATT_PLC = "ENABLE__FF_U_ATT_PLC";


// -----------------------------------------------------------------------------

//! @brief Adjust combat feedback visibility based on whether oPC is attacking a placeable.
//! @param oPC A player character.
void SetFeedback(object oPC);
void SetFeedback(object oPC)
{
  if (!GetIsPC(oPC))
    return;

  object oTarget = StringToObject(NWNX_Events_GetEventData("NEW_TARGET_OBJECT_ID"));
  if (GetObjectType(oTarget) != OBJECT_TYPE_PLACEABLE)
    return;

  // 61939,,,"You cannot damage your target with your currently equipped weapon!"
  if (GetPlotFlag(oTarget))
  {
    NWNX_Feedback_SetFeedbackMessageHidden(NWNX_FEEDBACK_COMBAT_WEAPON_NOT_EFFECTIVE, FALSE, oPC);
    NWNX_Player_SetTlkOverride(oPC, 61939, "The object is indestructible.");
  }
  else
    NWNX_Feedback_SetFeedbackMessageHidden(NWNX_FEEDBACK_COMBAT_WEAPON_NOT_EFFECTIVE, TRUE, oPC);

  NWNX_Feedback_SetCombatLogMessageHidden(NWNX_FEEDBACK_COMBATLOG_COMPLEX_ATTACK, TRUE, oPC);
  if (!GetIsDebug())
  {
    NWNX_Feedback_SetCombatLogMessageHidden(NWNX_FEEDBACK_COMBATLOG_COMPLEX_DAMAGE, TRUE, oPC);
    NWNX_Feedback_SetCombatLogMessageHidden(NWNX_FEEDBACK_COMBATLOG_SIMPLE_DAMAGE, TRUE, oPC);
  }

  NWNX_Feedback_SetFeedbackMessageHidden(NWNX_FEEDBACK_COMBAT_DAMAGE_RESISTANCE, TRUE, oPC);
  NWNX_Feedback_SetFeedbackMessageHidden(NWNX_FEEDBACK_COMBAT_DAMAGE_RESISTANCE_REMAINING, TRUE, oPC);
}


// -----------------------------------------------------------------------------

//! @brief Downgrade critical hits to normal hits for ranged attacks on placeables.
//! @param oAttacker The attacking creature.
void OnRangedAttack(object oAttacker);
void OnRangedAttack(object oAttacker)
{
  struct NWNX_Damage_AttackEventData stAttack = NWNX_Damage_GetAttackEventData();

  if (stAttack.iAttackResult == 3) // Critical
    stAttack.iAttackResult = 1; // normal hit

  NWNX_Damage_SetAttackEventData(stAttack);
}


// -----------------------------------------------------------------------------

//! @brief Handle melee attack on a placeable: auto-hit, disable sneak attack, reduce hardness on crit.
//! @param oAttacker The attacking creature.
void OnPhysicalAttack(object oAttacker);
void OnPhysicalAttack(object oAttacker)
{
  struct NWNX_Damage_AttackEventData stAttack = NWNX_Damage_GetAttackEventData();
  object oTarget = stAttack.oTarget;

  stAttack.iSneakAttack = 0; // never
  if (stAttack.iAttackResult == 3) // Critical
  {
    // On a critical hit, reduce the hardness
    int nHardness = GetHardness(oTarget);
    nHardness = max(0, nHardness - 1);
    SetHardness(nHardness, oTarget);
  }
  else
    stAttack.iAttackResult = 7; // automatic hit

  NWNX_Damage_SetAttackEventData(stAttack);
}


// -----------------------------------------------------------------------------

//! @brief Dispatch ranged or melee placeable attack handling.
//! @param oAttacker The attacking creature.
void OnAttackPlaceable(object oAttacker)
{
  struct NWNX_Damage_AttackEventData stAttack = NWNX_Damage_GetAttackEventData();

  object oTarget = stAttack.oTarget;
  if (GetObjectType(oTarget) != OBJECT_TYPE_PLACEABLE)
    return;

  if (stAttack.bRangedAttack)
    OnRangedAttack(oAttacker);
  else
    OnPhysicalAttack(oAttacker);
}


// -----------------------------------------------------------------------------

void main()
{
  object oModule = GetModule();
  if (!GetModuleFlag(ENABLE__FF_U_ATT_PLC, FALSE))
    return;

  string sEvent = GetCurrentEvent();
  if (sEvent == ON_REGISTER)
  {
    SubscribeToEvent(ON_DEFAULT_ATTACK, __FILE__);
    SubscribeToEvent(ON_BEFORE_ATTACK_TARGET_CHANGE, __FILE__);
  }
  else if (sEvent == ON_DEFAULT_ATTACK)
    OnAttackPlaceable(OBJECT_SELF);
  else if (sEvent == ON_AFTER_ATTACK_TARGET_CHANGE)
    SetFeedback(OBJECT_SELF);
}