PRC8/nwn/nwnprc/trunk/newspellbook/tob_crusader.nss
Jaysyn904 6ec137a24e Updated AMS marker feats
Updated AMS marker feats.  Removed arcane & divine marker feats.  Updated Dread Necromancer for epic progression. Updated weapon baseitem models.  Updated new weapons for crafting & npc equip.
 Updated prefix.  Updated release archive.
2024-02-11 14:01:05 -05:00

251 lines
11 KiB
Plaintext

#include "tob_inc_tobfunc"
#include "prc_inc_combmove"
void DelayedDamageHB(object oPC, int nCount);
int LevelToDelayedDamage(int nLevel)
{
int nDelayedDamage = -1;
if (nLevel == 20) nDelayedDamage = 30;
else if (nLevel >= 16) nDelayedDamage = 25;
else if (nLevel >= 12) nDelayedDamage = 20;
else if (nLevel >= 8) nDelayedDamage = 15;
else if (nLevel >= 4) nDelayedDamage = 10;
else if (nLevel >= 1) nDelayedDamage = 5;
return nDelayedDamage;
}
void IndomitableSoul(object oPC)
{
int nSave = GetAbilityModifier(ABILITY_CHARISMA, oPC);
// Can't be negative.
if (0 > nSave) nSave = 0;
// Charisma to Will saves
effect eFort = ExtraordinaryEffect(EffectSavingThrowIncrease(SAVING_THROW_WILL, nSave));
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eFort, oPC);
if (DEBUG) DoDebug("Indomitable Soul applied.");
}
void DelayedDamageHB(object oPC, int nCount)
{
int nCurHP = GetCurrentHitPoints(oPC);
int nPreHP = GetLocalInt(oPC, "CrusaderHP");
int nClass = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC);
object oHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
if (nPreHP > nCurHP)
{
// Check current delayed damage pool and max delayed
int nDelayedPool = GetLocalInt(oPC, "DelayedDamage");
int nMaxDelayed = LevelToDelayedDamage(nClass);
//if (DEBUG) DoDebug("Delayed Pool equals: "+IntToString(nDelayedPool));
//if (DEBUG) DoDebug("Maximum Delayed equals: "+IntToString(nMaxDelayed));
// If there is space left
if (nMaxDelayed > nDelayedPool)
{
// Amount remaining
int nRemainingPool = nMaxDelayed - nDelayedPool;
//if (DEBUG) DoDebug("Remaining Pool equals: "+IntToString(nRemainingPool));
// Damage dealt
int nDamageTaken = nPreHP - nCurHP;
int nHeal = 0;
//if (DEBUG) DoDebug("Damage Taken equals: "+IntToString(nDamageTaken));
// Prevents player from regaining more HP than damage taken
if(nDamageTaken >= nRemainingPool)
nHeal = nRemainingPool;
else
nHeal = nDamageTaken;
//if (DEBUG) DoDebug("Heal Amount equals: "+IntToString(nHeal));
// Heal them the Delayed Damage
//effect eHeal = EffectHeal(nHeal);
if (!GetLocalInt(oPC, "CrusaderBreak")) //ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oPC);
if (GetMaxHitPoints(oPC) >= nCurHP + nHeal)
SetCurrentHitPoints(oPC, nCurHP + nHeal); // Done this way so it doesn't show up on the char screen
// Mark the Local Int with current damage plus whatever else was healed
SetLocalInt(oPC, "DelayedDamage", nDelayedPool + nHeal);
}
}
if (nCount == 24)
{
// Get the amount of damage prevented
int nDelayedPool = GetLocalInt(oPC, "DelayedDamage");
//if (DEBUG) DoDebug("Your delayed damage pool: " + IntToString(nDelayedPool));
if (nDelayedPool > 0)
{
// Furious counterstrike is delayed damage / 5
int nBonus = nDelayedPool / 5;
// Minimum of one if you have delayed damage
if (nBonus == 0 && nDelayedPool > 0) nBonus = 1;
//if (DEBUG) DoDebug("Your furious counterstrike: " + IntToString(nBonus));
// Calculate damage type and apply
int nDamageType = GetWeaponDamageType(oHand);
effect eLink = EffectLinkEffects(EffectAttackIncrease(nBonus), EffectDamageIncrease(GetIntToDamage(nBonus), nDamageType));
eLink = EffectLinkEffects(eLink, EffectVisualEffect(PSI_DUR_BURST));
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, ExtraordinaryEffect(eLink), oPC, 6.0);
// Visuals
//effect eVis = EffectVisualEffect(VFX_DUR_MARK_OF_THE_HUNTER);
//ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC);
// Now apply the delayed damage
int nTest = GetCurrentHitPoints(oPC);
if (nTest - nDelayedPool > 0) SetCurrentHitPoints(oPC, nTest - nDelayedPool);
else // If this triggers, the PC is dead
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDamage(nDelayedPool, DAMAGE_TYPE_POSITIVE, DAMAGE_POWER_ENERGY)), oPC);
ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDeath(TRUE)), oPC);
}
int nTest2 = GetCurrentHitPoints(oPC);
if ((nTest - nDelayedPool) != nTest2) SetLocalInt(oPC, "CrusaderBreak", TRUE);
else DeleteLocalInt(oPC, "CrusaderBreak");
}
// Clean up local int for this round
DeleteLocalInt(oPC, "DelayedDamage");
//if (DEBUG) DoDebug("Deleted delayed damage: " + IntToString(GetLocalInt(oPC, "DelayedDamage")));
nCount = 0; // Reset the loop
}
SetLocalInt(oPC, "CrusaderHP", GetCurrentHitPoints(oPC));
DelayCommand(0.25, DelayedDamageHB(oPC, nCount+1));
}
void main()
{
int nEvent = GetRunningEvent();
//if(DEBUG) DoDebug("tob_crusader running, event: " + IntToString(nEvent));
// Get the PC. This is event-dependent
object oPC;
switch(nEvent)
{
case EVENT_ITEM_ONHIT: oPC = OBJECT_SELF; break;
case EVENT_ONPLAYEREQUIPITEM: oPC = GetItemLastEquippedBy(); break;
case EVENT_ONPLAYERUNEQUIPITEM: oPC = GetItemLastUnequippedBy(); break;
case EVENT_ONHEARTBEAT: oPC = OBJECT_SELF; break;
default:
oPC = OBJECT_SELF;
}
object oItem;
object oArmour = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC);
object oHand = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
int nClass = GetLevelByClass(CLASS_TYPE_CRUSADER, oPC);
// We aren't being called from any event, instead from EvalPRCFeats
if(nEvent == FALSE)
{
// Saving throw bonus, charisma, does not stack with paladin
//if (nClass >= 2 && GetLevelByClass(CLASS_TYPE_PALADIN, oPC) == 0)IndomitableSoul(oPC);
// Hook in the events, needed from level 1 for Steely Resolve
//if(DEBUG) DoDebug("tob_crusader: Adding eventhooks");
//AddEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "tob_crusader", TRUE, FALSE);
//AddEventScript(oPC, EVENT_ONPLAYERUNEQUIPITEM, "tob_crusader", TRUE, FALSE);
AddEventScript(oPC, EVENT_ONHEARTBEAT, "tob_crusader", TRUE, FALSE);
//AddEventScript(oPC, EVENT_VIRTUAL_ONDAMAGED, "tob_crusader", TRUE, FALSE);
if (!GetLocalInt(oPC, "DelayedDamageHB"))
{
DelayedDamageHB(oPC, 1);
SetLocalInt(oPC, "DelayedDamageHB", TRUE);
}
}
// Damage reduction from Steely Resolve
/*else if(nEvent == EVENT_VIRTUAL_ONDAMAGED)
{
FloatingTextStringOnCreature("Ouch ouch ouch!", oPC);
}
// Damage reduction from Steely Resolve
else if(nEvent == EVENT_ITEM_ONHIT)
{
oItem = GetSpellCastItem();
object oTarget = PRCGetSpellTargetObject();
if(DEBUG) DoDebug("tob_crusader: OnHit:\n"
+ "oPC = " + DebugObject2Str(oPC) + "\n"
+ "oItem = " + DebugObject2Str(oItem) + "\n"
+ "oTarget = " + DebugObject2Str(oTarget) + "\n"
);
// Only applies to armours
if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
{
// Check current delayed damage pool and max delayed
int nDelayedPool = GetLocalInt(oPC, "DelayedDamage");
int nMaxDelayed = LevelToDelayedDamage(nClass);
if (DEBUG) DoDebug("Delayed Pool equals: "+IntToString(nDelayedPool));
if (DEBUG) DoDebug("Maximum Delayed equals: "+IntToString(nMaxDelayed));
// If there is space left
if (nMaxDelayed > nDelayedPool)
{
// Amount remaining
int nRemainingPool = nMaxDelayed - nDelayedPool;
if (DEBUG) DoDebug("Remaining Pool equals: "+IntToString(nRemainingPool));
// Damage dealt
int nDamageTaken = GetTotalDamageDealt();
int nHeal = 0;
if (DEBUG) DoDebug("Damage Taken equals: "+IntToString(nDamageTaken));
// Prevents player from regaining more HP than damage taken
if(nDamageTaken >= nRemainingPool)
nHeal = nRemainingPool;
else
nHeal = nDamageTaken;
if (DEBUG) DoDebug("Heal Amount equals: "+IntToString(nHeal));
// Heal them the Delayed Damage
effect eHeal = EffectHeal(nHeal);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oPC);
// Mark the Local Int with current damage plus whatever else was healed
SetLocalInt(oPC, "DelayedDamage", nDelayedPool + nHeal);
}
}// end if - Item is an armour
}// end if - Running OnHit event
// We are called from the OnPlayerEquipItem eventhook. Add OnHitCast: Unique Power to oPC's armour
else if(nEvent == EVENT_ONPLAYEREQUIPITEM)
{
oPC = GetItemLastEquippedBy();
oItem = GetItemLastEquipped();
if(DEBUG) DoDebug("tob_crusader - OnEquip");
// Only applies to armours
if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
{
// Add eventhook to the item
AddEventScript(oItem, EVENT_ITEM_ONHIT, "tob_crusader", TRUE, FALSE);
// Add the OnHitCastSpell: Unique needed to trigger the event
IPSafeAddItemProperty(oItem, ItemPropertyOnHitCastSpell(IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 1), 99999.0, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
}
}
// We are called from the OnPlayerUnEquipItem eventhook. Remove OnHitCast: Unique Power from oPC's armour
else if(nEvent == EVENT_ONPLAYERUNEQUIPITEM)
{
oPC = GetItemLastUnequippedBy();
oItem = GetItemLastUnequipped();
if(DEBUG) DoDebug("tob_crusader - OnUnEquip");
// Only applies to armours
if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
{
// Add eventhook to the item
RemoveEventScript(oItem, EVENT_ITEM_ONHIT, "tob_crusader", TRUE, FALSE);
// Remove the temporary OnHitCastSpell: Unique
RemoveSpecificProperty(oItem, ITEM_PROPERTY_ONHITCASTSPELL, IP_CONST_ONHIT_CASTSPELL_ONHIT_UNIQUEPOWER, 0, 1, "", 1, DURATION_TYPE_TEMPORARY);
}
}*/
// This is used to determine the bonus from Furious Counterstrike
else if(nEvent == EVENT_ONHEARTBEAT)
{
//if they are deleveled completely out of Crusader, no need to keep running these
if(!GetLevelByClass(CLASS_TYPE_CRUSADER, oPC))
{
RemoveEventScript(oPC, EVENT_ONPLAYEREQUIPITEM, "tob_crusader", TRUE, FALSE);
RemoveEventScript(oPC, EVENT_ONPLAYERUNEQUIPITEM, "tob_crusader", TRUE, FALSE);
RemoveEventScript(oPC, EVENT_ONHEARTBEAT, "tob_crusader", TRUE, FALSE);
}
}
}