//:://///////////////////////////////////////////// //:: 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); }