ff_u_plc_att

Attacks on placeables.

Melee attacks on placeables and doors always hit and never sneak attack; a critical hit also lowers the target’s hardness by 1. Ranged critical hits become normal hits.

Variables

ENABLE__FF_U_PLC_ATT: (string) (opt-in) Set to "Y" on module to enable the unit.

Source code

// @code

#include "ff_i_core"

const string ENABLE__FF_U_PLC_ATT = "ENABLE__FF_U_PLC_ATT";


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

//! @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"));
  int nObjectType = GetObjectType(oTarget);
  if (nObjectType != OBJECT_TYPE_PLACEABLE && nObjectType != OBJECT_TYPE_DOOR)
    return;

  /*
  TODO: Reduce combat feedback
  // 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 a melee attack on a placeable: auto-hit, no sneak attack, hardness lowered on a critical.
//! @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;
  int nObjectType = GetObjectType(oTarget);
  if (nObjectType != OBJECT_TYPE_PLACEABLE && nObjectType != OBJECT_TYPE_DOOR)
    return;

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


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

void main()
{
  string sEvent = NWNX_Events_GetCurrentEvent();
  if (sEvent == ON_REGISTER)
  {
    if (!GetModuleFlag(ENABLE__FF_U_PLC_ATT, FALSE))
      return;

    NWNX_Damage_SetAttackEventScript("on_attack");
    SubscribeToEvent(ON_DEFAULT_ATTACK, __FILE__);
    SubscribeToEvent(NWNX_ON_ATTACK_TARGET_CHANGE_BEFORE, __FILE__);
  }
  else if (sEvent == ON_DEFAULT_ATTACK)
    OnAttackPlaceable(OBJECT_SELF);
  else if (sEvent == NWNX_ON_ATTACK_TARGET_CHANGE_BEFORE)
    SetFeedback(OBJECT_SELF);
}