Files
PRC8/nwn/nwnprc/trunk/scripts/prc_combatfocus.nss
Jaysyn904 783f0ddac4 2026/02/22 Update
Added scripting for Combat Form feats: Combat Focus, Combat Awareness, Combat Stability, Combat Defense, Combat Vitality & Combat Strike.
Updated fighter bonus feat lists for Champion of Torm, Dragon Devotee, Eldritch Knight, Fighter, Hospitaler, Psychic Warrior, Serene Guardian, Spellsword and Warblade.
2026-02-22 13:06:52 -05:00

205 lines
8.2 KiB
Plaintext

//:://////////////////////////////////////////////////////////////////
//:: ;-. ,-. ,-. ,-.
//:: | ) | ) / ( )
//:: |-' |-< | ;-:
//:: | | \ \ ( )
//:: ' ' ' `-' `-'
//:://////////////////////////////////////////////////////////////////
//::
/*
Combat Focus
Type of Feat: Combat Form, Fighter Bonus Feat.
The way of the warrior requires more than simple, brute strength.
Some warriors bring their minds to such keen focus during the heat
of battle that they can attain superhuman levels of endurance,
perception, and mental toughness. Through intense mental exercise
and training, you learn to enter a state of perfect martial
clarity.
Prerequisite: WIS 13
Specifics: In battle, you push aside the chaos of the fight and
attain a focused state that grants you a keen, clear picture of
the battle. Fear and pain ebb away as you focus solely on
defeating your enemy. The first time you make a successful
attack during an encounter, you gain your combat focus. In this
state, your mind and body become one, allowing you to overcome
mundane physical limits. You can maintain your combat focus for
10 rounds after entering it, +1 additional round per combat form
feat you possess aside from this one. You can only gain your
combat focus once per encounter. While you are maintaining your
combat focus, you gain a +2 bonus on Will saves. If you have
three or more combat form feats, this bonus increases to +4.
Use: Automatic.
Special: A fighter can select Combat Focus as one of his fighter
bonus feats.
*/
//::
//:://////////////////////////////////////////////////////////////////
//:: Script: prc_combatfocus.nss
//:: Author: Jaysyn
//:: Created: 2026-02-22 12:29:37
//:://////////////////////////////////////////////////////////////////
#include "prc_alterations"
#include "prc_inc_function"
#include "prc_inc_cmbtform"
#include "inc_eventhook"
void CombatFocus_AddOnHitToItem(object oItem)
{
if (!GetIsObjectValid(oItem)) return;
itemproperty ipOnHit = ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1);
ipOnHit = TagItemProperty(ipOnHit, "Tag_PRC_OnHitKeeper");
IPSafeAddItemProperty(oItem, ipOnHit, 999999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
AddEventScript(oItem, EVENT_ITEM_ONHIT, "prc_combatfocus", TRUE, FALSE);
}
// Helper to remove OnHit property from a weapon/ammo
void CombatFocus_RemoveOnHitFromItem(object oItem)
{
if (!GetIsObjectValid(oItem)) return;
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "prc_combatfocus", TRUE, FALSE);
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
}
void main()
{
int nEvent = GetRunningEvent();
object oPC;
// Resolve the correct PC based on the event
switch (nEvent)
{
case EVENT_ONPLAYERREST_FINISHED: oPC = GetLastBeingRested(); break;
case EVENT_ONPLAYEREQUIPITEM: oPC = GetItemLastEquippedBy(); break;
case EVENT_ONPLAYERUNEQUIPITEM: oPC = GetItemLastUnequippedBy(); break;
case EVENT_ONHEARTBEAT: oPC = OBJECT_SELF; break;
case EVENT_ITEM_ONHIT: oPC = OBJECT_SELF; break;
default: oPC = OBJECT_SELF; break;
}
if (nEvent == FALSE) // EvalPRCFeats entry
{
if (!GetHasFeat(FEAT_COMBAT_FOCUS, oPC)) return;
// Register EVENT_ITEM_ONHIT once if not already registered
if (!GetLocalInt(oPC, "CmbtFocus_OnHit_Registered"))
{
AddEventScript(oPC, EVENT_ITEM_ONHIT, "prc_combatfocus", TRUE, FALSE);
SetLocalInt(oPC, "CmbtFocus_OnHit_Registered", TRUE);
}
// Register equip/unequip events once
if (!GetLocalInt(oPC, "CmbtFocus_EquipHooks_Registered"))
{
AddEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "prc_combatfocus", TRUE, FALSE);
AddEventScript(oPC, EVENT_ONPLAYERUNEQUIPITEM, "prc_combatfocus", TRUE, FALSE);
SetLocalInt(oPC, "CmbtFocus_EquipHooks_Registered", TRUE);
}
// Register rest-finished event once
if (!GetLocalInt(oPC, "CmbtFocus_RestHook_Registered"))
{
AddEventScript(oPC, EVENT_ONPLAYERREST_FINISHED, "prc_combatfocus", TRUE, FALSE);
SetLocalInt(oPC, "CmbtFocus_RestHook_Registered", TRUE);
}
// Apply Will bonus if focus is active
if (GetLocalInt(oPC, COMBAT_FOCUS_VAR))
{
ApplyCombatFocusWillBonus(oPC);
// Always ensure heartbeat is registered while focus is active
if (!GetLocalInt(oPC, "CmbtFocus_HB_Registered"))
{
AddEventScript(oPC, EVENT_ONHEARTBEAT, "prc_combatfocus", TRUE, FALSE);
SetLocalInt(oPC, "CmbtFocus_HB_Registered", TRUE);
}
}
else
{
// Remove Will bonus if focus is not active
if (GetLocalInt(oPC, "CombatFocus_Will"))
RemoveCombatFocusWillBonus(oPC);
}
}
else if (nEvent == EVENT_ONPLAYEREQUIPITEM)
{
object oItem = GetItemLastEquipped();
if (!GetIsObjectValid(oItem)) return;
// Skip VoP and Forsaker to avoid conflicts
if (GetHasFeat(FEAT_VOWOFPOVERTY, oPC) || GetLevelByClass(CLASS_TYPE_FORSAKER, oPC)) return;
if (IPGetIsMeleeWeapon(oItem))
{
CombatFocus_AddOnHitToItem(oItem);
// Also add to ammo if ranged
object oAmmo = GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC);
if (GetIsObjectValid(oAmmo)) CombatFocus_AddOnHitToItem(oAmmo);
oAmmo = GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC);
if (GetIsObjectValid(oAmmo)) CombatFocus_AddOnHitToItem(oAmmo);
oAmmo = GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC);
if (GetIsObjectValid(oAmmo)) CombatFocus_AddOnHitToItem(oAmmo);
}
}
else if (nEvent == EVENT_ONPLAYERUNEQUIPITEM)
{
object oItem = GetItemLastUnequipped();
if (!GetIsObjectValid(oItem)) return;
if (IPGetIsMeleeWeapon(oItem))
{
CombatFocus_RemoveOnHitFromItem(oItem);
// Also remove from ammo if ranged
CombatFocus_RemoveOnHitFromItem(GetItemInSlot(INVENTORY_SLOT_BOLTS, oPC));
CombatFocus_RemoveOnHitFromItem(GetItemInSlot(INVENTORY_SLOT_BULLETS, oPC));
CombatFocus_RemoveOnHitFromItem(GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC));
}
}
else if (nEvent == EVENT_ONHEARTBEAT)
{
if (!GetIsPC(oPC) || !GetLocalInt(oPC, COMBAT_FOCUS_VAR)) return;
// Combat Awareness HP display only
if (GetHasFeat(FEAT_COMBAT_AWARENESS, oPC))
ShowAdjacentHP(oPC);
}
else if (nEvent == EVENT_ONPLAYERREST_FINISHED)
{
RemoveCombatFocusWillBonus(oPC);
DeleteLocalInt(oPC, COMBAT_FOCUS_VAR);
DeleteLocalInt(oPC, "CombatFocus_RoundsRemaining");
DeleteLocalInt(oPC, COMBAT_FOCUS_ENC);
if (GetLocalInt(oPC, "CmbtFocus_HB_Registered"))
{
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_combatfocus", TRUE, FALSE);
DeleteLocalInt(oPC, "CmbtFocus_HB_Registered");
}
object oRightHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
if (GetIsObjectValid(oRightHand) && IPGetIsMeleeWeapon(oRightHand))
{
DelayCommand(1.0f, CombatFocus_AddOnHitToItem(oRightHand));
}
}
else if (nEvent == EVENT_ITEM_ONHIT)
{
if (!GetIsPC(oPC)) return;
if (!GetHasFeat(FEAT_COMBAT_FOCUS, oPC)) return;
if (GetLocalInt(oPC, COMBAT_FOCUS_VAR)) return;
if (GetLocalInt(oPC, COMBAT_FOCUS_ENC)) return;
CombatFocus_OnAttackHit(oPC);
// Start round decay heartbeat
if (!GetLocalInt(oPC, "CmbtFocus_HB_Registered"))
{
AddEventScript(oPC, EVENT_ONHEARTBEAT, "prc_combatfocus", TRUE, FALSE);
SetLocalInt(oPC, "CmbtFocus_HB_Registered", TRUE);
DelayCommand(6.0, CombatFocus_DecayRounds(oPC));
}
// Start combat-end detection
DelayCommand(6.0, CombatFocus_CombatEndHeartbeat(oPC));
}
}