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.
251 lines
11 KiB
Plaintext
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);
|
|
}
|
|
}
|
|
} |