Updated Release Archive. Fixed Mage-killer prereqs. Removed old LETO & ConvoCC related files. Added organized spell scroll store. Fixed Gloura spellbook. Various TLK fixes. Reorganized Repo. Removed invalid user folders. Added DocGen back in.
398 lines
18 KiB
Plaintext
398 lines
18 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: Ability Damage application
|
|
//:: prc_inc_damage
|
|
//:://////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Internal constants */
|
|
//////////////////////////////////////////////////
|
|
|
|
const string VIRTUAL_ABILITY_SCORE = "PRC_Virtual_Ability_Score_";
|
|
const string UbHealable_ABILITY_DAMAGE = "PRC_UbHealableAbilityDamage_";
|
|
const string ABILITY_DAMAGE_SPECIALS = "PRC_Ability_Damage_Special_Effects_Flags";
|
|
const string ABILITY_DAMAGE_MONITOR = "PRC_Ability_Monitor";
|
|
const int ABILITY_DAMAGE_EFFECT_PARALYZE = 1;
|
|
const int ABILITY_DAMAGE_EFFECT_KNOCKDOWN = 2;
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function prototypes */
|
|
//////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Gets the amount of ubHealable ability damage suffered by the creature to given ability
|
|
*
|
|
* @param oTarget The creature whose ubHealable ability damage to examine
|
|
* @param nAbility One of the ABILITY_* constants
|
|
*/
|
|
int GetUnHealableAbilityDamage(object oTarget, int nAbility);
|
|
|
|
/**
|
|
* Removes the specified amount of normally unHealable ability damage from the target
|
|
*
|
|
* @param oTarget The creature to restore
|
|
* @param nAbility Ability to restore, one of the ABILITY_* constants
|
|
* @param nAmount Amount to restore the ability by, should be > 0 for the function
|
|
* to have any effect
|
|
*/
|
|
void RecoverUnHealableAbilityDamage(object oTarget, int nAbility, int nAmount);
|
|
|
|
/**
|
|
* Applies the ability damage to the given target. Handles the virtual loss of
|
|
* ability scores below 3 and the effects of reaching 0 and making the damage
|
|
* ubHealable by standard means if requested.
|
|
*
|
|
*
|
|
* @param oTarget The creature about to take ability damage
|
|
* @param nAbility One of the ABILITY_* constants
|
|
* @param nAmount How much to reduce the ability score by
|
|
* @param nDurationType One of the DURATION_TYPE_* contants
|
|
* @param bHealable Whether the damage is healable by normal means or not.
|
|
* Implemented by applying the damage as an iprop on the hide
|
|
*
|
|
* The following are passed to SPApplyEffectToObject:
|
|
* @param fDuration If temporary, the duration. If this is -1.0, the damage
|
|
* will be applied so that it wears off at the rate of 1 point
|
|
* per ingame day.
|
|
* @param bDispellable Is the effect dispellable? If FALSE, the system will delay
|
|
* the application of the effect a short moment (10ms) to break
|
|
* spellID association. This will make effects from the same
|
|
* source stack with themselves.
|
|
* @param oSource Object causing the ability damage
|
|
*/
|
|
void ApplyAbilityDamage(object oTarget, int nAbility, int nAmount, int nDurationType, int bHealable = TRUE,
|
|
float fDuration = 0.0f, int bDispellable = FALSE, object oSource = OBJECT_SELF);
|
|
|
|
// Similar funcionality to ApplyAbilityDamage() but used only for alcohol effects
|
|
// If you add new Alcohol effects or modify ApplyAbilityDamage() function, you
|
|
// should update this function as well.
|
|
void ApplyAlcoholEffect(object oTarget, int nAmount, float fDuration);
|
|
|
|
/**
|
|
* Sets the values of ability decrease on target's hide to be the same as the value
|
|
* tracked on the target object itself. This is called with delay from ScrubPCSkin()
|
|
* in order to synchronise the tracked value of ubHealable damage with that actually
|
|
* present on the hide.
|
|
* Please call this if you do similar operations on the hide.
|
|
*
|
|
* @param oTarget The creature whose hide and tracked value to synchronise.
|
|
*/
|
|
void ReApplyUnhealableAbilityDamage(object oTarget);
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Includes */
|
|
//////////////////////////////////////////////////
|
|
|
|
//#include "prc_inc_racial"
|
|
#include "prc_effect_inc"
|
|
#include "inc_item_props"
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function defintions */
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
void ApplyAbilityDamage(object oTarget, int nAbility, int nAmount, int nDurationType, int bHealable = TRUE,
|
|
float fDuration = 0.0f, int bDispellable = FALSE, object oSource = OBJECT_SELF)
|
|
{
|
|
// Immunity check
|
|
if(GetIsImmune(oTarget, IMMUNITY_TYPE_ABILITY_DECREASE, oSource))
|
|
return;
|
|
|
|
if (GetLocalInt(oTarget, "IncarnumDefenseCE") && nAbility == ABILITY_STRENGTH)
|
|
return;
|
|
|
|
if (GetIsMeldBound(oTarget, MELD_VITALITY_BELT) == CHAKRA_WAIST && nAbility == ABILITY_CONSTITUTION)
|
|
return;
|
|
|
|
if (GetHasSpellEffect(VESTIGE_DAHLVERNAR, oTarget) && nAbility == ABILITY_WISDOM && GetLocalInt(oTarget, "ExploitVestige") != VESTIGE_DAHLVERNAR_MAD_SOUL)
|
|
return;
|
|
|
|
// Strongheart Vest reduces by Essentia amount + 1. If it's bound, reduces ability drain as well
|
|
if (GetHasSpellEffect(MELD_STRONGHEART_VEST, oTarget) && bHealable)
|
|
{
|
|
int nEssentia = GetEssentiaInvested(oTarget, MELD_STRONGHEART_VEST);
|
|
nAmount = nAmount - (nEssentia + 1);
|
|
// If there's no damage, jump out.
|
|
if (0 >= nAmount) return;
|
|
}
|
|
else if (GetIsMeldBound(oTarget, MELD_STRONGHEART_VEST) == CHAKRA_WAIST && !bHealable)
|
|
{
|
|
int nEssentia = GetEssentiaInvested(oTarget, MELD_STRONGHEART_VEST);
|
|
nAmount = nAmount - (nEssentia + 1);
|
|
// If there's no damage, jump out.
|
|
if (0 >= nAmount) return;
|
|
}
|
|
|
|
// Get the value of the stat before anything is done
|
|
int nStartingValue = GetAbilityScore(oTarget, nAbility);
|
|
|
|
// First, apply the whole damage as an effect
|
|
//SendMessageToPC(GetFirstPC(), "Applying " + IntToString(nAmount) + " damage to stat " + IntToString(nAbility));
|
|
if(bHealable)
|
|
{
|
|
// Is the damage temporary and specified to heal at the PnP rate
|
|
if(nDurationType == DURATION_TYPE_TEMPORARY && fDuration == -1.0f)
|
|
{
|
|
int i;
|
|
for(i = 1; i <= nAmount; i++)
|
|
DelayCommand(0.01f, ApplyEffectToObject(nDurationType, bDispellable ? TagEffect(EffectAbilityDecrease(nAbility, 1), IntToString(nAbility)+IntToString(1)) : TagEffect(SupernaturalEffect(EffectAbilityDecrease(nAbility, 1)), IntToString(nAbility)+IntToString(1)), oTarget, HoursToSeconds(24) * i));
|
|
}
|
|
else if(!bDispellable)
|
|
{
|
|
DelayCommand(0.01f, ApplyEffectToObject(nDurationType, TagEffect(SupernaturalEffect(EffectAbilityDecrease(nAbility, nAmount)), IntToString(nAbility)+IntToString(nAmount)), oTarget, fDuration));
|
|
}
|
|
else
|
|
{
|
|
ApplyEffectToObject(nDurationType, TagEffect(EffectAbilityDecrease(nAbility, nAmount), IntToString(nAbility)+IntToString(nAmount)), oTarget, fDuration);
|
|
}
|
|
}
|
|
// Non-healable damage
|
|
else
|
|
{
|
|
int nIPType;
|
|
int nTotalAmount;
|
|
string sVarName = "PRC_UbHealableAbilityDamage_";
|
|
switch(nAbility)
|
|
{
|
|
case ABILITY_STRENGTH: nIPType = IP_CONST_ABILITY_STR; sVarName += "STR"; break;
|
|
case ABILITY_DEXTERITY: nIPType = IP_CONST_ABILITY_DEX; sVarName += "DEX"; break;
|
|
case ABILITY_CONSTITUTION: nIPType = IP_CONST_ABILITY_CON; sVarName += "CON"; break;
|
|
case ABILITY_INTELLIGENCE: nIPType = IP_CONST_ABILITY_INT; sVarName += "INT"; break;
|
|
case ABILITY_WISDOM: nIPType = IP_CONST_ABILITY_WIS; sVarName += "WIS"; break;
|
|
case ABILITY_CHARISMA: nIPType = IP_CONST_ABILITY_CHA; sVarName += "CHA"; break;
|
|
|
|
default:
|
|
WriteTimestampedLogEntry("Unknown nAbility passed to ApplyAbilityDamage: " + IntToString(nAbility));
|
|
return;
|
|
}
|
|
|
|
// Sum the damage being added with damage that was present previously
|
|
nTotalAmount = GetLocalInt(oTarget, sVarName) + nAmount;
|
|
|
|
// Apply the damage
|
|
SetCompositeBonus(GetPCSkin(oTarget), sVarName, nTotalAmount, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, nIPType);
|
|
|
|
// Also store the amount of damage on the PC itself so it can be restored at a later date.
|
|
SetLocalInt(oTarget, sVarName, nTotalAmount);
|
|
|
|
// Schedule recovering if the damage is temporary
|
|
if(nDurationType == DURATION_TYPE_TEMPORARY)
|
|
{
|
|
// If the damage is specified to heal at the PnP rate, schedule one point to heal per day
|
|
if(fDuration == -1.0f)
|
|
{
|
|
int i;
|
|
for(i = 1; i <= nAmount; i++)
|
|
DelayCommand(HoursToSeconds(24) * i, RecoverUnHealableAbilityDamage(oTarget, nAbility, 1));
|
|
}
|
|
// Schedule everything to heal at once
|
|
else
|
|
DelayCommand(fDuration, RecoverUnHealableAbilityDamage(oTarget, nAbility, nAmount));
|
|
}
|
|
}
|
|
|
|
// The system is off by default
|
|
if(!GetPRCSwitch(PRC_PNP_ABILITY_DAMAGE_EFFECTS))
|
|
return;
|
|
|
|
// If the target is at the minimum supported by NWN, check if they have had their ability score reduced below already
|
|
if(nStartingValue == 3)
|
|
nStartingValue = GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(nAbility)) ?
|
|
GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(nAbility)) - 1 :
|
|
nStartingValue;
|
|
|
|
// See if any of the damage goes into the virtual area of score < 3
|
|
if(nStartingValue - nAmount < 3)
|
|
{
|
|
int nVirtual = nStartingValue - nAmount;
|
|
if(nVirtual < 0) nVirtual = 0;
|
|
|
|
// Mark the virtual value
|
|
SetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(nAbility), nVirtual + 1);
|
|
|
|
// Cause effects for being at 0
|
|
if(nVirtual == 0)
|
|
{
|
|
// Apply the effects
|
|
switch(nAbility)
|
|
{
|
|
// Lying down
|
|
case ABILITY_STRENGTH:
|
|
case ABILITY_INTELLIGENCE:
|
|
case ABILITY_WISDOM:
|
|
case ABILITY_CHARISMA:
|
|
// Do not apply duplicate effects
|
|
/*if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_PARALYZE))
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oTarget);*/
|
|
if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_KNOCKDOWN))
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 9999.0f);
|
|
SetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS, GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) | ABILITY_DAMAGE_EFFECT_KNOCKDOWN);
|
|
}
|
|
//break;
|
|
|
|
// Paralysis
|
|
case ABILITY_DEXTERITY:
|
|
// Do not apply duplicate effects
|
|
if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_PARALYZE))
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oTarget);
|
|
SetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS, GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) | ABILITY_DAMAGE_EFFECT_PARALYZE);
|
|
}
|
|
break;
|
|
|
|
// Death
|
|
case ABILITY_CONSTITUTION:
|
|
// Non-constitution score critters avoid this one
|
|
if(!(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD ||
|
|
MyPRCGetRacialType(oTarget) == RACIAL_TYPE_CONSTRUCT
|
|
) )
|
|
{
|
|
DeathlessFrenzyCheck(oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDeath()), oTarget);
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WriteTimestampedLogEntry("Unknown nAbility passed to ApplyAbilityDamage: " + IntToString(nAbility));
|
|
return;
|
|
}
|
|
|
|
// Start the monitor HB if it is not active yet
|
|
if(GetThreadState(ABILITY_DAMAGE_MONITOR, oTarget) == THREAD_STATE_DEAD)
|
|
SpawnNewThread(ABILITY_DAMAGE_MONITOR, "prc_abil_monitor", 1.0f, oTarget);
|
|
|
|
// Note the ability score for monitoring
|
|
SetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR, GetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR) | (1 << nAbility));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyAlcoholEffect(object oTarget, int nAmount, float fDuration)
|
|
{
|
|
// Immunity check
|
|
if(GetIsImmune(oTarget, IMMUNITY_TYPE_ABILITY_DECREASE))
|
|
return;
|
|
|
|
// Get the value of the stat before anything is done
|
|
int nStartingValue = GetAbilityScore(oTarget, ABILITY_INTELLIGENCE);
|
|
|
|
// First, apply the whole damage as an effect
|
|
DelayCommand(0.01f, AssignCommand(GetPCSkin(oTarget), ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SupernaturalEffect(EffectAbilityDecrease(ABILITY_INTELLIGENCE, nAmount)), oTarget, fDuration)));
|
|
|
|
// The system is off by default
|
|
if(!GetPRCSwitch(PRC_PNP_ABILITY_DAMAGE_EFFECTS))
|
|
return;
|
|
|
|
// If the target is at the minimum supported by NWN, check if they have had their ability score reduced below already
|
|
if(nStartingValue == 3)
|
|
nStartingValue = GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE)) ?
|
|
GetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE)) - 1 :
|
|
nStartingValue;
|
|
|
|
// See if any of the damage goes into the virtual area of score < 3
|
|
if(nStartingValue - nAmount < 3)
|
|
{
|
|
int nVirtual = nStartingValue - nAmount;
|
|
if(nVirtual < 0) nVirtual = 0;
|
|
|
|
// Mark the virtual value
|
|
SetLocalInt(oTarget, VIRTUAL_ABILITY_SCORE + IntToString(ABILITY_INTELLIGENCE), nVirtual + 1);
|
|
|
|
// Cause effects for being at 0
|
|
if(!nVirtual)
|
|
{
|
|
// Do not apply duplicate effects
|
|
/*if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_PARALYZE))
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneParalyze(), oTarget);*/
|
|
if(!(GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) & ABILITY_DAMAGE_EFFECT_KNOCKDOWN))
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 9999.0f);
|
|
SetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS, GetLocalInt(oTarget, ABILITY_DAMAGE_SPECIALS) | ABILITY_DAMAGE_EFFECT_KNOCKDOWN);
|
|
}
|
|
|
|
// Start the monitor HB if it is not active yet
|
|
if(GetThreadState(ABILITY_DAMAGE_MONITOR, oTarget) == THREAD_STATE_DEAD)
|
|
SpawnNewThread(ABILITY_DAMAGE_MONITOR, "prc_abil_monitor", 1.0f, oTarget);
|
|
|
|
// Note the ability score for monitoring
|
|
SetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR, GetLocalInt(oTarget, ABILITY_DAMAGE_MONITOR) | (1 << ABILITY_INTELLIGENCE));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReApplyUnhealableAbilityDamage(object oTarget)
|
|
{
|
|
object oSkin = GetPCSkin(oTarget);
|
|
SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_STR",
|
|
GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_STR"),
|
|
ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_STR);
|
|
SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_DEX",
|
|
GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_DEX"),
|
|
ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_DEX);
|
|
SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_CON",
|
|
GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_CON"),
|
|
ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_CON);
|
|
SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_INT",
|
|
GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_INT"),
|
|
ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_INT);
|
|
SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_WIS",
|
|
GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_WIS"),
|
|
ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_WIS);
|
|
SetCompositeBonus(oSkin, "PRC_UbHealableAbilityDamage_CHA",
|
|
GetLocalInt(oTarget, "PRC_UbHealableAbilityDamage_CHA"),
|
|
ITEM_PROPERTY_DECREASED_ABILITY_SCORE, IP_CONST_ABILITY_CHA);
|
|
}
|
|
|
|
int GetUnHealableAbilityDamage(object oTarget, int nAbility)
|
|
{
|
|
int nIPType;
|
|
string sVarName = "PRC_UbHealableAbilityDamage_";
|
|
switch(nAbility)
|
|
{
|
|
case ABILITY_STRENGTH: sVarName += "STR"; break;
|
|
case ABILITY_DEXTERITY: sVarName += "DEX"; break;
|
|
case ABILITY_CONSTITUTION: sVarName += "CON"; break;
|
|
case ABILITY_INTELLIGENCE: sVarName += "INT"; break;
|
|
case ABILITY_WISDOM: sVarName += "WIS"; break;
|
|
case ABILITY_CHARISMA: sVarName += "CHA"; break;
|
|
|
|
default:
|
|
WriteTimestampedLogEntry("Unknown nAbility passed to GetUnHealableAbilityDamage: " + IntToString(nAbility));
|
|
return FALSE;
|
|
}
|
|
|
|
return GetLocalInt(oTarget, sVarName);
|
|
}
|
|
|
|
|
|
void RecoverUnHealableAbilityDamage(object oTarget, int nAbility, int nAmount)
|
|
{
|
|
// Sanity check, one should not be able to cause more damage via this function, ApplyAbilityDamage() is for that.
|
|
if(nAmount < 0) return;
|
|
|
|
int nIPType, nNewVal;
|
|
string sVarName = "PRC_UbHealableAbilityDamage_";
|
|
switch(nAbility)
|
|
{
|
|
case ABILITY_STRENGTH: nIPType = IP_CONST_ABILITY_STR; sVarName += "STR"; break;
|
|
case ABILITY_DEXTERITY: nIPType = IP_CONST_ABILITY_DEX; sVarName += "DEX"; break;
|
|
case ABILITY_CONSTITUTION: nIPType = IP_CONST_ABILITY_CON; sVarName += "CON"; break;
|
|
case ABILITY_INTELLIGENCE: nIPType = IP_CONST_ABILITY_INT; sVarName += "INT"; break;
|
|
case ABILITY_WISDOM: nIPType = IP_CONST_ABILITY_WIS; sVarName += "WIS"; break;
|
|
case ABILITY_CHARISMA: nIPType = IP_CONST_ABILITY_CHA; sVarName += "CHA"; break;
|
|
|
|
default:
|
|
WriteTimestampedLogEntry("Unknown nAbility passed to ApplyAbilityDamage: " + IntToString(nAbility));
|
|
return;
|
|
}
|
|
|
|
nNewVal = GetLocalInt(oTarget, sVarName) - nAmount;
|
|
if(nNewVal < 0) nNewVal = 0;
|
|
|
|
SetCompositeBonus(GetPCSkin(oTarget), sVarName, nNewVal, ITEM_PROPERTY_DECREASED_ABILITY_SCORE, nIPType);
|
|
SetLocalInt(oTarget, sVarName, nNewVal);
|
|
}
|