PRC8/nwn/nwnprc/trunk/include/prc_inc_breath.nss
Jaysyn904 e641b42f84 Exalted update
Updated Vow of Poverty. Added Sanctify Ki Strike, Holy Strike, Fist of Heavens, Vow of Abstinence, Vow of Chastity & Gift of Faith.  (@fenac).  Turned off the Taunt & Parry skills.  Re-disabled AC & save bonuses from Tumble & Spellcraft.   Updated min() & max() to PRCmin() & PRCmax() to not conflict with similarly named NUI adjacent functions.  Set Point Blank Shot to 30' per PnP.  Added icon for Chosen of Evil.  Started work on Hidden Talent.  Created Psionics function cheatsheet.  Updated release archive.
2025-01-29 22:46:38 -05:00

989 lines
41 KiB
Plaintext

//::///////////////////////////////////////////////
//:: Breath Weapon Include
//:: prc_inc_breath
//::///////////////////////////////////////////////
/** @file
Centralizes handling for most breath weapons for
implementing Metabreath and the like.
@author Fox
@date Created - 2008.1.19
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////
//////////////////////////////////////////////////
/* Structures */
//////////////////////////////////////////////////
/**
* A structure that contains common data used during power manifestation.
*/
struct breath{
/* Generic stuff */
/// The creature breathing
object oDragon;
/* Basic info */
/// The shape of the breath
int bLine;
/// The size of the breath in feet
float fRange;
/// The element of the breath
int nDamageType;
/// Type of dice
int nDiceType;
/// Number of dice
int nDiceNumber;
/// The stat relavant for DC calculating
int nDCStat;
/// Any other DC mods, like class levels for DragDis
int nOtherDCMod;
/// Save type - needed in case of Fort Save
int nSaveUsed;
/// Rounds until next use
int nRoundsUntilRecharge;
/* Metabreath */
/// Number of rounds Clinging Breaths last
int nClinging;
/// Number of rounds Lingering Breaths last
int nLingering;
/// Whether Enlarge Breath was used
int bEnlarge;
/// How much the DC is increased by Heighten Breath - max of Con
int nHeighten;
/// Whether Maximize Breath was used
int bMaximize;
/// Whether Spreading Breath was used
int bSpread;
/// Whether Tempest Breath was used
int bTempest;
/* Breath Channeling */
/// Whether Entangling Exhalation was used
int bEntangling;
/// Whether Exhaled Barrier was used
int bBarrier;
/// Whether Exhaled Immunity was used
int bExhaleImmune;
/* Special Cases */
/// Special case referance number - used for things like Pyroclastic
int nOverrideSpecial;
};
//////////////////////////////////////////////////
/* Function prototypes */
//////////////////////////////////////////////////
/**
* Creates the breath struct, taking metabreath and breath channeling into account.
*
* @param oDragon The creature breathing the breath weapon
* @param bLine True if breath is a line breath, false if a cone breath
* @param fRange The range of the breath weapon in feet.
* @param nDamageType The element of the breath weapon, if it has one.
* @param nDiceType The type of damage dice for the breath weapon.
* @param nDiceNumber The number of damage dice for the breath weapon.
* @param nDCStat The constant for the stat the breath weapon uses for DC
* calculation.
* @param nOtherDCMod Any other applicable modifications to the DC.
* e.g. Dragon Disciple levels.
* @param nOverrideSpecial Indicates any special case breath weapons, like the Pyroclastic's
* split damage or Shadow's negative level breath. Defaults to 0.
* @param nBaseRecharge The die size of the recharge "lock." Defaults to d4.
* @param nSaveUsed The constant to determine the save used. Defaults to Reflex.
* Diamond Dragon uses Fort saves for cold breath for example.
*
* @return Returns a struct that describes the breath being used, including
* applicable metabreath and breath channeling feats.
*/
struct breath CreateBreath(object oDragon, int bLine, float fRange, int nDamageType, int nDiceType, int nDiceNumber, int nDCStat, int nOtherDCMod, int nOverrideSpecial = 0, int nBaseRecharge = 4, int nSaveUsed = SAVING_THROW_REFLEX);
/**
* Applies the breath effect on the targeted location. Brought into the include since
* there is so much common code.
*
* @param BreathUsed A struct describing the details of the breath weapon
* @param lTargetArea The targeted location for the breath.
* @param bLinger Determines if this breath was applied by Lingering Breath metabreath.
* Defaults to FALSE.
* @param vSource The source of a Lingering breath. This does not matter except when
* bLinger is set to TRUE.
*
*/
void ApplyBreath(struct breath BreathUsed, location lTargetArea, int bLinger = FALSE, float vSourceX = 0.0, float vSourceY = 0.0, float vSourceZ = 0.0);
//////////////////////////////////////////////////
/* Includes */
//////////////////////////////////////////////////
#include "prc_alterations"
//////////////////////////////////////////////////
/* Internal functions */
//////////////////////////////////////////////////
// Exhaled Immunity is different enough to break off on it's own, as it only
// applies to one creature, and nothing else happens.
void ExhaleImmunity(object oDragon, int nDamageType, location lTargetArea)
{
//since there's no "GetObjectAtLocation," grab a tiny AoE to find the creature
object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 1.0, lTargetArea, TRUE,
OBJECT_TYPE_CREATURE, GetPosition(oDragon));
//make sure damage type is valid, default to fire for non-energy-damage-based breaths
int nImmuneType = DAMAGE_TYPE_FIRE;
if (nDamageType == DAMAGE_TYPE_ACID) nImmuneType = DAMAGE_TYPE_ACID;
else if (nDamageType == DAMAGE_TYPE_COLD) nImmuneType = DAMAGE_TYPE_COLD;
else if (nDamageType == DAMAGE_TYPE_SONIC) nImmuneType = DAMAGE_TYPE_SONIC;
else if (nDamageType == DAMAGE_TYPE_ELECTRICAL) nImmuneType = DAMAGE_TYPE_ELECTRICAL;
if((oTarget == OBJECT_INVALID) || (oTarget == oDragon))
{
SendMessageToPC(oDragon, "You must target another creature for Exhaled Immunity to work.");
return;
}
else
{
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(oDragon, GetSpellId(), FALSE));
//Determine effect delay
float fDelay = GetDistanceBetween(oDragon, oTarget)/20;
float fDuration = RoundsToSeconds(d4());
//set effects
effect eImmune = EffectDamageImmunityIncrease(nDamageType, 100);
effect eVis = EffectVisualEffect(VFX_IMP_ELEMENTAL_PROTECTION);
effect eVis2 = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE);
effect eLink = EffectLinkEffects(eImmune, eVis2);
//apply effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eImmune, oTarget, fDuration));
}
}
//fucntion to check for Adept breath
int IsAdeptBreath()
{
int nBreath = GetSpellId();
if(nBreath == BREATH_FIRE_CONE
|| nBreath == BREATH_FIRE_LINE
|| nBreath == BREATH_FROST_CONE
|| nBreath == BREATH_ELECTRIC_LINE
|| nBreath == BREATH_SICKENING_CONE
|| nBreath == BREATH_ACID_CONE
|| nBreath == BREATH_ACID_LINE
|| nBreath == BREATH_SLOW_CONE
|| nBreath == BREATH_WEAKENING_CONE
|| nBreath == BREATH_SLEEP_CONE
|| nBreath == BREATH_THUNDER_CONE
|| nBreath == BREATH_BAHAMUT_LINE
|| nBreath == BREATH_FORCE_LINE
|| nBreath == BREATH_PARALYZE_CONE
|| nBreath == BREATH_FIVEFOLD_TIAMAT)
return TRUE;
return FALSE;
}
int IsBreathProtected(object oDragon, object oTarget)
{
int nBPIndex = 0;
while(array_get_object(oTarget, "BreathProtected", nBPIndex) != OBJECT_INVALID)
{
if(array_get_object(oTarget, "BreathProtected", nBPIndex) == oDragon)
{
return TRUE;
}
else
nBPIndex++;
}
return FALSE;
}
//////////////////////////////////////////////////
/* Function definitions */
//////////////////////////////////////////////////
struct breath CreateBreath(object oDragon, int bLine, float fRange, int nDamageType, int nDiceType, int nDiceNumber, int nDCStat, int nOtherDCMod, int nOverrideSpecial = 0, int nBaseRecharge = 4, int nSaveUsed = SAVING_THROW_REFLEX)
{
struct breath BreathUsed;
//gather base data
BreathUsed.oDragon = oDragon;
BreathUsed.bLine = bLine;
BreathUsed.fRange = fRange;
BreathUsed.nDamageType = nDamageType;
BreathUsed.nDiceType = nDiceType;
BreathUsed.nDiceNumber = nDiceNumber;
BreathUsed.nDCStat = nDCStat;
BreathUsed.nOtherDCMod = nOtherDCMod;
int nFinalRecharge;
switch(nBaseRecharge)
{
case 4:
nFinalRecharge = d4(); break;
case 1:
nFinalRecharge = 1; break;
default:
nFinalRecharge = 0; break;
}
BreathUsed.nRoundsUntilRecharge = nFinalRecharge;
BreathUsed.nSaveUsed = nSaveUsed;
BreathUsed.nOverrideSpecial = nOverrideSpecial;
//Energy Aura bonus checking
switch(nDamageType)
{
case DAMAGE_TYPE_ACID:
if(GetLocalInt(oDragon,"AcidEnergyAura"))
BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"AcidEnergyAura");
break;
case DAMAGE_TYPE_COLD:
if(GetLocalInt(oDragon,"ColdEnergyAura"))
BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"ColdEnergyAura");
break;
case DAMAGE_TYPE_ELECTRICAL:
if(GetLocalInt(oDragon,"ElecEnergyAura"))
BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"ElecEnergyAura");
break;
case DAMAGE_TYPE_FIRE:
if(GetLocalInt(oDragon,"FireEnergyAura"))
BreathUsed.nOtherDCMod += GetLocalInt(oDragon,"FireEnergyAura");
}
/* Initialize metabreath/channeling tracking */
BreathUsed.nClinging = 0;
BreathUsed.nLingering = 0;
BreathUsed.bEnlarge = FALSE;
BreathUsed.nHeighten = 0;
BreathUsed.bMaximize = FALSE;
BreathUsed.bSpread = FALSE;
BreathUsed.bTempest = FALSE;
BreathUsed.bEntangling = FALSE;
BreathUsed.bBarrier = FALSE;
BreathUsed.bExhaleImmune = FALSE;
/* breath channeling */
// Whether Entangling Exhalation was used
if(GetLocalInt(oDragon, "EntangleBreath"))
BreathUsed.bEntangling = TRUE;
// Whether Exhaled Barrier was used
if(GetLocalInt(oDragon, "ExhaleBarrier"))
BreathUsed.bBarrier = TRUE;
// Whether Exhaled Immunity was used
if(GetLocalInt(oDragon, "ExhaleImmunity"))
BreathUsed.bExhaleImmune = TRUE;
//breath effect checks
//Cloud only works with 1 other effect applied
//this one handles combination with damage effects and enduring
if(GetLocalInt(oDragon, "AdeptCloudBreath")
&& !GetLocalInt(oDragon, "AdeptShapedBreath")
&& (GetSpellId() == BREATH_FIRE_CONE
|| GetSpellId() == BREATH_FROST_CONE
|| GetSpellId() == BREATH_SICKENING_CONE
|| GetSpellId() == BREATH_ACID_CONE
|| GetSpellId() == BREATH_SLOW_CONE
|| GetSpellId() == BREATH_WEAKENING_CONE
|| GetSpellId() == BREATH_SLEEP_CONE
|| GetSpellId() == BREATH_THUNDER_CONE
|| GetSpellId() == BREATH_PARALYZE_CONE))
{
BreathUsed.bSpread = TRUE;
}
//this one is Cloud + Shaped
if(GetLocalInt(oDragon, "AdeptCloudBreath")
&& GetLocalInt(oDragon, "AdeptShapedBreath")
&& !GetLocalInt(oDragon, "AdeptEnduringBreath")
&& GetSpellId() == BREATH_FIRE_CONE)
{
BreathUsed.bSpread = TRUE;
}
if(GetLocalInt(oDragon, "AdeptEnduringBreath")
&& !(GetLocalInt(oDragon, "AdeptCloudBreath") && GetLocalInt(oDragon, "AdeptShapedBreath"))
&& (GetSpellId() == BREATH_FIRE_CONE || GetSpellId() == BREATH_FIRE_LINE))
{
BreathUsed.nLingering = 1;
}
//breaths without recharge times can't use metabreath
if(BreathUsed.nRoundsUntilRecharge == 0)
return BreathUsed;
/* metabreath calculation*/
//Clinging breath - Main feat increments uses, secondary feat cancels.
if(GetLocalInt(oDragon, "ClingingBreath") > 0)
{
BreathUsed.nClinging = GetLocalInt(oDragon, "ClingingBreath");
BreathUsed.nRoundsUntilRecharge += BreathUsed.nClinging;
}
//Lingering breath - Main feat increments uses, secondary feat cancels.
if(GetLocalInt(oDragon, "LingeringBreath") > 0)
{
BreathUsed.nLingering = GetLocalInt(oDragon, "LingeringBreath");
BreathUsed.nRoundsUntilRecharge += (BreathUsed.nLingering * 2);
}
//Enlarge breath
if(GetLocalInt(oDragon, "EnlargeBreath"))
{
BreathUsed.bEnlarge = TRUE;
BreathUsed.nRoundsUntilRecharge += 1;
}
//Heighten breath - Feat increments uses, resets if incremented at max.
if(GetLocalInt(oDragon, "HeightenBreath") > 0)
{
BreathUsed.nHeighten = GetLocalInt(oDragon, "HeightenBreath");
BreathUsed.nRoundsUntilRecharge += BreathUsed.nHeighten;
}
//Maximize breath
if(GetLocalInt(oDragon, "MaximizeBreath"))
{
BreathUsed.bMaximize = TRUE;
BreathUsed.nRoundsUntilRecharge += 3;
}
//Shape breath
if(GetLocalInt(oDragon, "ShapeBreath"))
{
if(bLine) BreathUsed.bLine = FALSE;
else BreathUsed.bLine = TRUE;
BreathUsed.nRoundsUntilRecharge += 1;
}
//Spreading breath
if(GetLocalInt(oDragon, "SpreadingBreath"))
{
BreathUsed.bSpread = TRUE;
BreathUsed.nRoundsUntilRecharge += 2;
}
//Tempest breath
if(GetLocalInt(oDragon, "TempestBreath"))
{
BreathUsed.bTempest = TRUE;
BreathUsed.nRoundsUntilRecharge += 1;
}
//Recover Breath
if(GetHasFeat(FEAT_RECOVER_BREATH, oDragon)
&& (BreathUsed.nRoundsUntilRecharge > 1))
BreathUsed.nRoundsUntilRecharge += -1;
// Exhaled Barrier and Immunity don't apply Metabreath feats, so recharge time should be unaffected
if(BreathUsed.bBarrier || BreathUsed.bExhaleImmune)
BreathUsed.nRoundsUntilRecharge = nFinalRecharge;
return BreathUsed;
}
void ApplyBreath(struct breath BreathUsed, location lTargetArea, int bLinger = FALSE, float vSourceX = 0.0, float vSourceY = 0.0, float vSourceZ = 0.0)
{
//if using Exhaled Immunity, jump straight to it and ignore the rest
if(BreathUsed.bExhaleImmune)
{
ExhaleImmunity(BreathUsed.oDragon, BreathUsed.nDamageType, lTargetArea);
return;
}
//init variables
effect eBreath;
effect eVis;
float fDelay;
int nDamage = 0;
int nAdjustedDamage;
int nSaveDamageType = -1;
int nVisualType = -1;
int nEnergyAura = 0;
int bCanCling;
int nSaveDC;
int nShapedSpotsUsed = GetLocalInt(BreathUsed.oDragon, "AdeptShapedBreath") ? 0 : 4;
int nBreathShape = BreathUsed.bLine ? SHAPE_SPELLCYLINDER : SHAPE_SPELLCONE;
float fRange = FeetToMeters(BreathUsed.fRange);
int nKnockdownDC = 0;
vector vSource = Vector(vSourceX, vSourceY, vSourceZ);
vector vSourceOfBreath = bLinger ? vSource : GetPosition(BreathUsed.oDragon);
//Saving Throw setup
if (BreathUsed.nDCStat >= 10)
nSaveDC = BreathUsed.nDCStat;
else
nSaveDC = 10 + PRCMax(GetAbilityModifier(BreathUsed.nDCStat), 0) + BreathUsed.nOtherDCMod;
//Set up variables that depend on damage type
switch (BreathUsed.nDamageType)
{
case DAMAGE_TYPE_ACID:
nSaveDamageType = SAVING_THROW_TYPE_ACID;
nVisualType = VFX_IMP_ACID_S;
nEnergyAura = GetLocalInt(BreathUsed.oDragon, "AcidEnergyAura"); break;
case DAMAGE_TYPE_COLD:
nSaveDamageType = SAVING_THROW_TYPE_COLD;
nVisualType = VFX_IMP_FROST_S;
nEnergyAura = GetLocalInt(BreathUsed.oDragon, "ColdEnergyAura"); break;
case DAMAGE_TYPE_ELECTRICAL:
nSaveDamageType = SAVING_THROW_TYPE_ELECTRICITY;
nVisualType = VFX_IMP_LIGHTNING_S;
nEnergyAura = GetLocalInt(BreathUsed.oDragon, "ElecEnergyAura"); break;
case DAMAGE_TYPE_FIRE:
nSaveDamageType = SAVING_THROW_TYPE_FIRE;
nVisualType = VFX_IMP_FLAME_M;
nEnergyAura = GetLocalInt(BreathUsed.oDragon, "FireEnergyAura"); break;
case DAMAGE_TYPE_MAGICAL:
nSaveDamageType = SAVING_THROW_TYPE_NONE;
nVisualType = VFX_IMP_KNOCK; break;
case DAMAGE_TYPE_NEGATIVE:
nSaveDamageType = SAVING_THROW_TYPE_NEGATIVE;
nVisualType = VFX_IMP_NEGATIVE_ENERGY; break;
case DAMAGE_TYPE_POSITIVE:
nSaveDamageType = SAVING_THROW_TYPE_POSITIVE;
nVisualType = VFX_IMP_HOLY_AID; break;
case DAMAGE_TYPE_SONIC:
nSaveDamageType = SAVING_THROW_TYPE_SONIC;
nVisualType = VFX_IMP_SILENCE; break;
}
//apply Energy Draconic Aura bonus
if(nEnergyAura < 0) nEnergyAura = 0;
nSaveDC += nEnergyAura;
if(BreathUsed.nOverrideSpecial == BREATH_TOPAZ)
nVisualType = VFX_IMP_POISON_L;
if(BreathUsed.bBarrier)
{
//2d6 fire damage if the breath doesn't do damage
if(BreathUsed.nOverrideSpecial == BREATH_SHADOW
|| BreathUsed.nOverrideSpecial == BREATH_SLEEP
|| BreathUsed.nOverrideSpecial == BREATH_SLOW
|| BreathUsed.nOverrideSpecial == BREATH_PARALYZE
|| BreathUsed.nOverrideSpecial == BREATH_WEAKENING)
{
BreathUsed.nDiceType = 6;
BreathUsed.nDiceNumber = 2;
BreathUsed.nDamageType = DAMAGE_TYPE_FIRE;
}
//fire damage if damage isn't energy damage
if(BreathUsed.nDamageType == DAMAGE_TYPE_MAGICAL || BreathUsed.nDamageType == DAMAGE_TYPE_BLUDGEONING)
BreathUsed.nDamageType = DAMAGE_TYPE_FIRE;
//get the breath parameters and pass to the wall's scripts
SetLocalInt(BreathUsed.oDragon, "BarrierDiceType", BreathUsed.nDiceType);
SetLocalInt(BreathUsed.oDragon, "BarrierDiceNumber", BreathUsed.nDiceNumber);
SetLocalInt(BreathUsed.oDragon, "BarrierDamageType", BreathUsed.nDamageType);
//create the wall
effect eAOE = EffectAreaOfEffect(AOE_PER_WALLBREATH);
ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eAOE, lTargetArea, RoundsToSeconds(d4()));
//this overrides all other breath effects, so exit
return;
}
//roll damage
switch(BreathUsed.nDiceType)
{
case 4:
nDamage = d4(BreathUsed.nDiceNumber); break;
case 6:
nDamage = d6(BreathUsed.nDiceNumber); break;
case 8:
nDamage = d8(BreathUsed.nDiceNumber); break;
case 10:
nDamage = d10(BreathUsed.nDiceNumber); break;
}
if(DEBUG) DoDebug("prc_inc_breath: Rolling damage: " + IntToString(BreathUsed.nDiceNumber)
+ "d" + IntToString(BreathUsed.nDiceType) + "= " + IntToString(nDamage));
//Shadow breath does 1 neg level instead of damage
if(BreathUsed.nOverrideSpecial == BREATH_SHADOW)
nDamage = 1;
//adjust for metabreaths
if(BreathUsed.bEnlarge)
fRange = fRange * 1.5;
if(BreathUsed.nHeighten > 0)
nSaveDC += BreathUsed.nHeighten;
if(BreathUsed.bMaximize)
nDamage = BreathUsed.nDiceType * BreathUsed.nDiceNumber;
if(BreathUsed.bSpread)
{
fRange = PRCGetCreatureSize(BreathUsed.oDragon) * 5.0;
if(fRange < 1.0) fRange = 5.0;
fRange = FeetToMeters(fRange);
nBreathShape = SHAPE_SPHERE;
lTargetArea = GetLocation(BreathUsed.oDragon);
eVis = EffectVisualEffect(VFX_FNF_DRAGBREATHGROUND);
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVis, lTargetArea);
}
//Lingering Breath's effects are 1/2 of normal when lingering
if(bLinger)
{
nDamage /= 2;
BreathUsed.nDiceNumber /= 2;
}
//DCs for Tempest Breath
switch(PRCGetCreatureSize(BreathUsed.oDragon))
{
case CREATURE_SIZE_LARGE:
nKnockdownDC = 15; break;
case CREATURE_SIZE_HUGE:
nKnockdownDC = 18; break;
case CREATURE_SIZE_GARGANTUAN:
nKnockdownDC = 20; break;
case CREATURE_SIZE_COLOSSAL:
nKnockdownDC = 30; break;
}
//adjust for breath channeling
if(BreathUsed.bEntangling)
nDamage = nDamage / 2;
/* Begin application */
int nObjectFilter = OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE;
//Adept's Breath of Bahamut does not affect objects
if(GetSpellId() == BREATH_BAHAMUT_LINE) nObjectFilter = OBJECT_TYPE_CREATURE;
//Get first target in spell area
object oTarget = GetFirstObjectInShape(nBreathShape, fRange, lTargetArea, TRUE, nObjectFilter, vSourceOfBreath);
if(DEBUG) DoDebug("prc_inc_breath: Target Name: " + GetName(oTarget));
while(GetIsObjectValid(oTarget))
{
if(DEBUG) DoDebug("prc_inc_breath: Valid Target: " + GetName(oTarget));
if(oTarget != BreathUsed.oDragon && !IsBreathProtected(BreathUsed.oDragon, oTarget) && spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, BreathUsed.oDragon))
{
if(DEBUG) DoDebug("prc_inc_breath: Not Self, Not Protected");
//Adjust the damage based on the Reflex Save, Evasion and Improved Evasion.
//Determine effect delay
fDelay = GetDistanceBetween(BreathUsed.oDragon, oTarget)/20;
//check for Shaped Breath effect
if(IsAdeptBreath() && (nShapedSpotsUsed < 4) && !GetIsReactionTypeHostile(oTarget) && GetLocalInt(BreathUsed.oDragon, "AdeptShapedBreath"))
{
if(DEBUG) DoDebug("prc_inc_breath: Entering Shaped Breath. Slots used: " + IntToString(nShapedSpotsUsed));
nShapedSpotsUsed++;
}
//Adept's Sickening Breath breath effect
else if(BreathUsed.nOverrideSpecial == BREATH_SICKENING)
{
if(DEBUG) DoDebug("prc_inc_breath: sickening breath attack");
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
//2 rounds on a failed save
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE))
{
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_POISON_L), oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSickened(), oTarget, 12.0));
}
//1 round otherwise
else
{
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_POISON_L), oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSickened(), oTarget, 6.0));
}
}
//Brass alternate breath - sleep
else if(BreathUsed.nOverrideSpecial == BREATH_SLEEP)
{
if(DEBUG) DoDebug("prc_inc_breath: sleep breath attack");
//prepare effects
effect eBreath = EffectSleep();
effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
effect eLink = EffectLinkEffects(eBreath, eDur);
//get duration
int nSleepDuration = BreathUsed.nDiceNumber;
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE))
{
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nSleepDuration)));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
}
//Copper alternate breath - slow
else if(BreathUsed.nOverrideSpecial == BREATH_SLOW)
{
if(DEBUG) DoDebug("prc_inc_breath: slowing breath attack");
//prepare effects
effect eBreath = EffectSlow();
effect eVis = EffectVisualEffect(VFX_IMP_SLOW);
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
effect eLink = EffectLinkEffects(eBreath, eDur);
//get duration
int nSlowDuration = BreathUsed.nDiceNumber;
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE))
{
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nSlowDuration)));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
//Adept breath effect still lasts for one round if save is made
else if(IsAdeptBreath())
{
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(1)));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
}
//Gold alternate breath - drains strength
else if(BreathUsed.nOverrideSpecial == BREATH_WEAKENING)
{
if(DEBUG) DoDebug("prc_inc_breath: weakening breath attack");
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE))
{
//Set Damage and VFX - Bioware Gold used VFX_IMP_REDUCE_ABILITY_SCORE originally
eVis = EffectVisualEffect(VFX_IMP_POISON_L);
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
//adept breath penalty only last 4 rounds on failure, gold breath has no duration
if(IsAdeptBreath())
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAbilityDecrease(ABILITY_STRENGTH, BreathUsed.nDiceNumber), oTarget, 24.0));
else
DelayCommand(fDelay, ApplyAbilityDamage(oTarget, ABILITY_STRENGTH, BreathUsed.nDiceNumber, DURATION_TYPE_PERMANENT, TRUE));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
//Adept breath still happens for 2 rounds on successful save
else if(IsAdeptBreath())
{
//Set Damage and VFX - Bioware Gold used VFX_IMP_REDUCE_ABILITY_SCORE originally
eVis = EffectVisualEffect(VFX_IMP_POISON_L);
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAbilityDecrease(ABILITY_STRENGTH, BreathUsed.nDiceNumber), oTarget, 12.0));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
bCanCling = TRUE;
}
//Silver alternate breath - paralyze
else if(BreathUsed.nOverrideSpecial == BREATH_PARALYZE)
{
if(DEBUG) DoDebug("prc_inc_breath: paralyzing breath attack");
//prepare effects
effect eBreath = EffectParalyze();
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
effect eDur2 = EffectVisualEffect(VFX_DUR_PARALYZE_HOLD);
effect eParal = EffectVisualEffect(VFX_DUR_PARALYZED);
effect eLink = EffectLinkEffects(eBreath, eDur);
eLink = EffectLinkEffects(eLink, eDur2);
eLink = EffectLinkEffects(eLink, eParal);
//get duration
int nParalDuration = BreathUsed.nDiceNumber;
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, SAVING_THROW_TYPE_NONE))
{
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nParalDuration)));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
}
//Shadow Dragon breath - drains levels
else if(BreathUsed.nOverrideSpecial == BREATH_SHADOW)
{
if(DEBUG) DoDebug("prc_inc_breath: shadow breath attack");
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
int nLevelDrain = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, SAVING_THROW_TYPE_NEGATIVE);
if (nLevelDrain > 0)
{
//Set Damage and VFX
eBreath = EffectNegativeLevel(nLevelDrain);
eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY);
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
}
}
//Swift Wing Breath of Life - Positive to Undead only, heals living creatures
else if(BreathUsed.nOverrideSpecial == BREATH_SWIFT_WING)
{
if(DEBUG) DoDebug("prc_inc_breath: swift wing breath attack");
if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || (GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD))
{
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
nAdjustedDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, SAVING_THROW_TYPE_POSITIVE);
if (nAdjustedDamage > 0)
{
//Set Damage and VFX
eBreath = EffectDamage(nAdjustedDamage, BreathUsed.nDamageType);
eVis = EffectVisualEffect(VFX_IMP_SUNSTRIKE);
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
bCanCling = TRUE;
}
}
}
//normal damaging-type breath
else
{
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId()));
if(DEBUG) DoDebug("prc_inc_breath: normal breath attack");
if(BreathUsed.nSaveUsed == SAVING_THROW_FORT)
{
nAdjustedDamage = nDamage;
//make a fort save
if(PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC, nSaveDamageType))
{
//Mettle is Evasion for Fort saves
if (GetHasMettle(oTarget, SAVING_THROW_FORT))
nAdjustedDamage = 0;
nAdjustedDamage /= 2;
}
}
else
nAdjustedDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, nSaveDamageType);
if (nAdjustedDamage > 0)
{
if(DEBUG) DoDebug("prc_inc_breath: Damage > 0");
//Set Damage and VFX
if(BreathUsed.nOverrideSpecial == BREATH_PYROCLASTIC)
{
int chSaveth;
int chVisual;
int eleRoll;
int nNumDice = d2();
//Sets the random Element factor of the Chaos Dragons Breath Weapon.
//Affects damage, saving throw, and impact visual.
if (nNumDice==1)
{
chSaveth = SAVING_THROW_TYPE_SONIC;
chVisual = VFX_IMP_SILENCE;
}
else if (nNumDice==2)
{
chSaveth = SAVING_THROW_TYPE_FIRE;
chVisual = VFX_IMP_FLAME_M;
}
if(GetLocalInt(oTarget, "DragonWard"))
nAdjustedDamage -= 40;
effect eBreath1 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_FIRE);
effect eBreath2 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_SONIC);
eBreath = EffectLinkEffects(eBreath1, eBreath2);
eVis = EffectVisualEffect(chVisual);
}
else
{
if(GetLocalInt(oTarget, "DragonWard"))
nAdjustedDamage -= 20;
eBreath = EffectDamage(nAdjustedDamage, BreathUsed.nDamageType);
eVis = EffectVisualEffect(nVisualType);
}
//Apply the VFX impact and effects
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget));
SetLocalInt(oTarget, "AffectedByBreath", TRUE);
bCanCling = TRUE;
}
}
//Knockdown check for Tempest Breath
if(BreathUsed.bTempest && (PRCGetCreatureSize(BreathUsed.oDragon) > CREATURE_SIZE_MEDIUM))
{
if(DEBUG) DoDebug("prc_inc_breath: tempest breath attack");
if(PRCGetCreatureSize(BreathUsed.oDragon) - PRCGetCreatureSize(oTarget) > 1)
{
if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nKnockdownDC, SAVING_THROW_TYPE_NONE))
{
effect eWindblown = EffectKnockdown();
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eWindblown, oTarget, 6.0));
}
}
}
//Breath of Life healing
if(oTarget != BreathUsed.oDragon && BreathUsed.nOverrideSpecial == BREATH_SWIFT_WING && MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD && MyPRCGetRacialType(oTarget) != RACIAL_TYPE_CONSTRUCT
&& !(GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD))
{
if(DEBUG) DoDebug("prc_inc_breath: breath of life");
//Fire cast spell at event for the specified target
SignalEvent(oTarget, EventSpellCastAt(BreathUsed.oDragon, GetSpellId(), FALSE));
//Adjust the damage based on the Reflex Save, Evasion and Improved Evasion.
//Determine effect delay
fDelay = GetDistanceBetween(BreathUsed.oDragon, oTarget)/20;
int nHeal = BreathUsed.nDiceNumber;
//Warforged are only healed for half
if(GetIsWarforged(oTarget)) nHeal /= 2;
eBreath = EffectHeal(nHeal);
effect eHealVis = EffectVisualEffect(VFX_IMP_HEALING_S);
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBreath, oTarget));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eHealVis, oTarget));
}
//Entangling Exhalation
if(GetLocalInt(oTarget, "AffectedByBreath") && BreathUsed.bEntangling)
{
if(DEBUG) DoDebug("prc_inc_breath: entangling breath attack");
effect eEntangled = EffectEntangle();
effect eEntangleVis = EffectVisualEffect(VFX_DUR_ENTANGLE);
eEntangled = EffectLinkEffects(eEntangled, eEntangleVis);
int nEntangleRounds = d4();
//only does damage if the original breath did damage
if(BreathUsed.nDamageType > 0)
{
effect eDamage = EffectDamage(d6(), BreathUsed.nDamageType);
switch(nEntangleRounds)
{
case 4:
DelayCommand(fDelay + RoundsToSeconds(4), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget));
case 3:
DelayCommand(fDelay + RoundsToSeconds(3), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget));
case 2:
DelayCommand(fDelay + RoundsToSeconds(2), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget));
case 1:
DelayCommand(fDelay + RoundsToSeconds(1), ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget)); break;
}
}
//but always entangles if the breath affects the target
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEntangled, oTarget, RoundsToSeconds(nEntangleRounds)));
}
DeleteLocalInt(oTarget, "AffectedByBreath");
//Clinging Breath check
if(BreathUsed.nClinging > 0 && bCanCling)
{
if(DEBUG) DoDebug("prc_inc_breath: clinging breath attack");
int nCount;
int AdjustedAbilityDamage = BreathUsed.nDiceNumber;
for(nCount = 0; nCount < BreathUsed.nClinging; nCount++)
{
float fNewDelay = RoundsToSeconds(nCount + 1);
if(BreathUsed.nOverrideSpecial == BREATH_WEAKENING)
{
AdjustedAbilityDamage /= 2;
DelayCommand(fNewDelay, ApplyAbilityDamage(oTarget, ABILITY_STRENGTH, BreathUsed.nDiceNumber, DURATION_TYPE_PERMANENT, TRUE));
if(AdjustedAbilityDamage = 1) nCount = BreathUsed.nClinging;
}
nAdjustedDamage /= 2;
if(BreathUsed.nOverrideSpecial == BREATH_PYROCLASTIC)
{
if(nAdjustedDamage < 3) nCount = BreathUsed.nClinging;
effect eBreath1 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_FIRE);
effect eBreath2 = EffectDamage(nAdjustedDamage/2, DAMAGE_TYPE_SONIC);
effect eAfterBreath = EffectLinkEffects(eBreath1, eBreath2);
DelayCommand(fNewDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eAfterBreath, oTarget));
}
else
{
if(nAdjustedDamage < 2) nCount = BreathUsed.nClinging;
effect eAfterBreath = EffectDamage(nAdjustedDamage, BreathUsed.nDamageType);
DelayCommand(fNewDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eAfterBreath, oTarget));
}
DelayCommand(fNewDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
}
}
}
//Get next target in spell area
if(DEBUG) DoDebug("prc_inc_breath: Next target");
oTarget = GetNextObjectInShape(nBreathShape, fRange, lTargetArea, TRUE, nObjectFilter, vSourceOfBreath);
}
//Lingering Breath check
if(BreathUsed.nLingering > 0)
{
int nCount;
int nLingerDuration = BreathUsed.nLingering;
BreathUsed.nLingering = 0;
BreathUsed.nClinging = 0;
vector vOrigin = GetPosition(BreathUsed.oDragon);
effect eGroundVis = EffectVisualEffect(VFX_FNF_DRAGBREATHGROUND);
for(nCount = 0; nCount < nLingerDuration; nCount++)
{
float fNewDelay = RoundsToSeconds(nCount + 1);
DelayCommand(fNewDelay, ApplyBreath(BreathUsed, lTargetArea, TRUE, vOrigin.x, vOrigin.y, vOrigin.z));
DelayCommand(fNewDelay, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eGroundVis, lTargetArea));
}
}
}
void PRCPlayDragonBattleCry()
{
if(d100() > 50)
{
PlayVoiceChat(VOICE_CHAT_BATTLECRY1);
}
else
{
PlayVoiceChat(VOICE_CHAT_BATTLECRY2);
}
}