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