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.
257 lines
8.6 KiB
Plaintext
257 lines
8.6 KiB
Plaintext
//:://////////////////////////////////////////////
|
|
//:: ;-. ,-. ,-. ,-.
|
|
//:: | ) | ) / ( )
|
|
//:: |-' |-< | ;-:
|
|
//:: | | \ \ ( )
|
|
//:: ' ' ' `-' `-'
|
|
//:://////////////////////////////////////////////
|
|
//::
|
|
/*
|
|
Library for Combat Form related functions.
|
|
|
|
*/
|
|
//::
|
|
//:://////////////////////////////////////////////
|
|
//:: Script: prc_inc_cmbtform.nss
|
|
//:: Author: Jaysyn
|
|
//:: Created: 2026-02-22 12:34:01
|
|
//:://////////////////////////////////////////////
|
|
#include "prc_feat_const"
|
|
#include "inc_item_props"
|
|
#include "inc_eventhook"
|
|
|
|
const string COMBAT_FOCUS_VAR = "CombatFocus_Active";
|
|
const string COMBAT_FOCUS_END = "CombatFocus_EndTime";
|
|
const string COMBAT_FOCUS_ENC = "CombatFocus_Encounter";
|
|
const string CA_BLIND_VAR = "CombatAwareness_Blindsight";
|
|
|
|
void CombatFocus_EndAfterGrace(object oPC);
|
|
|
|
//:: Count Combat Form feats (excluding Combat Focus itself)
|
|
int CountCombatFormFeats(object oPC)
|
|
{
|
|
int nCount = GetHasFeat(FEAT_COMBAT_AWARENESS, oPC)
|
|
+ GetHasFeat(FEAT_COMBAT_DEFENSE, oPC)
|
|
+ GetHasFeat(FEAT_COMBAT_STABILITY, oPC)
|
|
+ GetHasFeat(FEAT_COMBAT_STRIKE, oPC)
|
|
+ GetHasFeat(FEAT_COMBAT_VIGOR, oPC);
|
|
return nCount;
|
|
}
|
|
|
|
//:; Will save bonus helpers
|
|
void ApplyCombatFocusWillBonus(object oPC)
|
|
{
|
|
object oSkin = GetPCSkin(oPC);
|
|
int nForms = CountCombatFormFeats(oPC);
|
|
int nBonus = (nForms >= 3) ? 4 : 2;
|
|
SetCompositeBonus(oSkin, "CombatFocus_Will", nBonus,
|
|
ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC,
|
|
IP_CONST_SAVEBASETYPE_WILL);
|
|
}
|
|
|
|
void RemoveCombatFocusWillBonus(object oPC)
|
|
{
|
|
object oSkin = GetPCSkin(oPC);
|
|
SetCompositeBonus(oSkin, "CombatFocus_Will", 0,
|
|
ITEM_PROPERTY_SAVING_THROW_BONUS_SPECIFIC,
|
|
IP_CONST_SAVEBASETYPE_WILL);
|
|
}
|
|
|
|
//:: Reset encounter flag
|
|
void CombatFocus_ResetEncounter(object oPC)
|
|
{
|
|
DeleteLocalInt(oPC, COMBAT_FOCUS_ENC);
|
|
}
|
|
|
|
void CombatFocus_CombatEndHeartbeat(object oPC)
|
|
{
|
|
if (GetIsInCombat(oPC))
|
|
{
|
|
DelayCommand(6.0, CombatFocus_CombatEndHeartbeat(oPC));
|
|
}
|
|
else
|
|
{
|
|
if (GetLocalInt(oPC, COMBAT_FOCUS_VAR))
|
|
{
|
|
// Schedule grace period end
|
|
DelayCommand(6.0, CombatFocus_EndAfterGrace(oPC));
|
|
}
|
|
else
|
|
{
|
|
// Clear encounter flag if focus not active
|
|
DeleteLocalInt(oPC, COMBAT_FOCUS_ENC);
|
|
}
|
|
}
|
|
}
|
|
|
|
//:; Combat Stability bonus helpers
|
|
void ApplyCombatStabilityBonus(object oPC)
|
|
{
|
|
int nForms = CountCombatFormFeats(oPC);
|
|
int nBonus = (nForms >= 3) ? 8 : 4;
|
|
SetLocalInt(oPC, "CombatStability_Bonus", nBonus);
|
|
}
|
|
|
|
void RemoveCombatStabilityBonus(object oPC)
|
|
{
|
|
DeleteLocalInt(oPC, "CombatStability_Bonus");
|
|
}
|
|
|
|
//:: Fast healing helpers for Combat Vigor
|
|
void ApplyCombatVigorFastHeal(object oPC)
|
|
{
|
|
int nForms = CountCombatFormFeats(oPC);
|
|
int nHeal = (nForms >= 3) ? 4 : 2;
|
|
effect eRegen = EffectRegenerate(nHeal, 6.0f);
|
|
eRegen = TagEffect(eRegen, "CombatVor_FastHeal");
|
|
eRegen = SupernaturalEffect(eRegen);
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eRegen, oPC);
|
|
SetLocalInt(oPC, "CombatVigor_Active", TRUE);
|
|
}
|
|
|
|
void RemoveCombatVigorFastHeal(object oPC)
|
|
{
|
|
effect eCheck = GetFirstEffect(oPC);
|
|
while (GetIsEffectValid(eCheck))
|
|
{
|
|
if (GetEffectTag(eCheck) == "CombatVor_FastHeal")
|
|
RemoveEffect(oPC, eCheck);
|
|
eCheck = GetNextEffect(oPC);
|
|
}
|
|
DeleteLocalInt(oPC, "CombatVigor_Active");
|
|
}
|
|
|
|
//;: Dodge AC helpers for Combat Defense
|
|
void ApplyCombatDefenseAC(object oPC)
|
|
{
|
|
object oSkin = GetPCSkin(oPC);
|
|
int nForms = CountCombatFormFeats(oPC);
|
|
int nBonus = (nForms >= 3) ? 2 : 1;
|
|
SetCompositeBonus(oSkin, "CombatDefense_AC", nBonus, ITEM_PROPERTY_AC_BONUS);
|
|
SetLocalInt(oPC, "CombatDefense_Active", TRUE);
|
|
}
|
|
|
|
void RemoveCombatDefenseAC(object oPC)
|
|
{
|
|
object oSkin = GetPCSkin(oPC);
|
|
SetCompositeBonus(oSkin, "CombatDefense_AC", 0, ITEM_PROPERTY_AC_BONUS);
|
|
DeleteLocalInt(oPC, "CombatDefense_Active");
|
|
}
|
|
|
|
//:: Blindsight helpers for Combat Awareness
|
|
void ApplyCombatAwarenessBlindsight(object oPC)
|
|
{
|
|
effect eBlind = EffectBonusFeat(FEAT_BLINDSIGHT_5_FEET);
|
|
eBlind = TagEffect(eBlind, "CombatAwareness_Blindsight");
|
|
eBlind = SupernaturalEffect(eBlind);
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBlind, oPC);
|
|
SetLocalInt(oPC, CA_BLIND_VAR, TRUE);
|
|
}
|
|
|
|
void RemoveCombatAwarenessBlindsight(object oPC)
|
|
{
|
|
effect eCheck = GetFirstEffect(oPC);
|
|
while (GetIsEffectValid(eCheck))
|
|
{
|
|
if (GetEffectTag(eCheck) == "CombatAwareness_Blindsight")
|
|
RemoveEffect(oPC, eCheck);
|
|
eCheck = GetNextEffect(oPC);
|
|
}
|
|
DeleteLocalInt(oPC, CA_BLIND_VAR);
|
|
}
|
|
|
|
//:: Show HP of adjacent creatures while focus is active
|
|
void ShowAdjacentHP(object oPC)
|
|
{
|
|
if (!GetLocalInt(oPC, COMBAT_FOCUS_VAR)) return;
|
|
location lPC = GetLocation(oPC);
|
|
object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 5.0f, lPC, FALSE, OBJECT_TYPE_CREATURE);
|
|
while (GetIsObjectValid(oTarget))
|
|
{
|
|
if (oTarget != oPC)
|
|
{
|
|
int nHP = GetCurrentHitPoints(oTarget);
|
|
string sName = GetName(oTarget);
|
|
FloatingTextStringOnCreature(sName + ": " + IntToString(nHP) + " HP", oPC);
|
|
}
|
|
oTarget = GetNextObjectInShape(SHAPE_SPHERE, 5.0f, lPC, FALSE, OBJECT_TYPE_CREATURE);
|
|
}
|
|
}
|
|
|
|
void CombatFocus_EndAfterGrace(object oPC)
|
|
{
|
|
// If combat resumed, do nothing
|
|
if (GetIsInCombat(oPC)) return;
|
|
|
|
// Remove effects and clear state
|
|
RemoveCombatFocusWillBonus(oPC);
|
|
RemoveCombatAwarenessBlindsight(oPC);
|
|
RemoveCombatDefenseAC(oPC);
|
|
RemoveCombatVigorFastHeal(oPC);
|
|
RemoveCombatStabilityBonus(oPC);
|
|
DeleteLocalInt(oPC, COMBAT_FOCUS_VAR);
|
|
DeleteLocalInt(oPC, COMBAT_FOCUS_END);
|
|
DeleteLocalInt(oPC, COMBAT_FOCUS_ENC);
|
|
// Unregister heartbeat if it was registered
|
|
if (GetLocalInt(oPC, "CmbtFocus_HB_Registered"))
|
|
{
|
|
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_combatfocus", TRUE, FALSE);
|
|
DeleteLocalInt(oPC, "CmbtFocus_HB_Registered");
|
|
}
|
|
FloatingTextStringOnCreature("Your Combat Focus fades", oPC);
|
|
}
|
|
|
|
void CombatFocus_DecayRounds(object oPC)
|
|
{
|
|
if (!GetLocalInt(oPC, COMBAT_FOCUS_VAR)) return;
|
|
|
|
int nRounds = GetLocalInt(oPC, "CombatFocus_RoundsRemaining") - 1;
|
|
if (nRounds <= 0)
|
|
{
|
|
// Expired
|
|
RemoveCombatFocusWillBonus(oPC);
|
|
RemoveCombatAwarenessBlindsight(oPC);
|
|
RemoveCombatDefenseAC(oPC);
|
|
RemoveCombatVigorFastHeal(oPC);
|
|
RemoveCombatStabilityBonus(oPC);
|
|
DeleteLocalInt(oPC, COMBAT_FOCUS_VAR);
|
|
DeleteLocalInt(oPC, "CombatFocus_RoundsRemaining");
|
|
// Unregister heartbeat
|
|
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "prc_combatfocus", TRUE, FALSE);
|
|
DeleteLocalInt(oPC, "CmbtFocus_HB_Registered");
|
|
}
|
|
else
|
|
{
|
|
SetLocalInt(oPC, "CombatFocus_RoundsRemaining", nRounds);
|
|
// Schedule next decrement in 6 seconds
|
|
DelayCommand(6.0, CombatFocus_DecayRounds(oPC));
|
|
}
|
|
}
|
|
|
|
//:: External call: trigger on first successful attack of an encounter
|
|
void CombatFocus_OnAttackHit(object oPC)
|
|
{
|
|
if (!GetHasFeat(FEAT_COMBAT_FOCUS, oPC)) return;
|
|
if (GetLocalInt(oPC, COMBAT_FOCUS_VAR)) return;
|
|
if (GetLocalInt(oPC, COMBAT_FOCUS_ENC)) return;
|
|
|
|
int nDurationRounds = 10 + CountCombatFormFeats(oPC);
|
|
SetLocalInt(oPC, COMBAT_FOCUS_VAR, TRUE);
|
|
SetLocalInt(oPC, COMBAT_FOCUS_ENC, TRUE);
|
|
// Store remaining rounds instead of end timestamp
|
|
SetLocalInt(oPC, "CombatFocus_RoundsRemaining", nDurationRounds);
|
|
|
|
ApplyCombatFocusWillBonus(oPC);
|
|
|
|
// Apply Combat Form feat bonuses if possessed.
|
|
if (GetHasFeat(FEAT_COMBAT_VIGOR, oPC) && !GetLocalInt(oPC, "CombatVigor_Active"))
|
|
ApplyCombatVigorFastHeal(oPC);
|
|
if (GetHasFeat(FEAT_COMBAT_AWARENESS, oPC) && !GetLocalInt(oPC, CA_BLIND_VAR))
|
|
ApplyCombatAwarenessBlindsight(oPC);
|
|
if (GetHasFeat(FEAT_COMBAT_DEFENSE, oPC) && !GetLocalInt(oPC, "CombatDefense_Active"))
|
|
ApplyCombatDefenseAC(oPC);
|
|
if (GetHasFeat(FEAT_COMBAT_STABILITY, oPC) && !GetLocalInt(oPC, "CombatStability_Bonus"))
|
|
ApplyCombatStabilityBonus(oPC);
|
|
|
|
FloatingTextStringOnCreature("Combat Focus gained", oPC, FALSE);
|
|
} |