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.
151 lines
6.1 KiB
Plaintext
151 lines
6.1 KiB
Plaintext
/*
|
|
----------------
|
|
Tongue of Fire
|
|
Brimstone Speaker level 1
|
|
|
|
true_brim_tongue
|
|
----------------
|
|
|
|
19/7/06 by Stratovarius
|
|
*/ /** @file
|
|
|
|
Type of Feat: Class
|
|
Prerequisite: Brimstone Speaker 1
|
|
Specifics: The Brimstone Speaker can speak a truename that translates as "tongue of fire". This ability allows them to create a line of fire 20 feet long. Those hit by the fire have a DC vs 10 + Class levels + Con modifier. There are three versions of the Tongue of Fire: 3d6 with a Truespeak DC of 25, 5d6 with a DC of 30, and 8d6 with a DC of 35. This ability is affected by the Law of Resistance.
|
|
Use: Selected.
|
|
*/
|
|
|
|
#include "true_inc_trufunc"
|
|
//#include "prc_alterations"
|
|
|
|
float GetVFXLength(location lTrueSpeaker, float fLength, float fAngle);
|
|
|
|
void main()
|
|
{
|
|
object oTrueSpeaker = OBJECT_SELF;
|
|
object oTarget = PRCGetSpellTargetObject();
|
|
int nSpellId = PRCGetSpellId();
|
|
int nDC;
|
|
int nDamage;
|
|
if (BRIMSTONE_FIRE_3D6 == nSpellId)
|
|
{
|
|
nDC = 25;
|
|
nDamage = d6(3);
|
|
}
|
|
else if (BRIMSTONE_FIRE_5D6 == nSpellId)
|
|
{
|
|
nDC = 30;
|
|
nDamage = d6(5);
|
|
}
|
|
else if (BRIMSTONE_FIRE_8D6 == nSpellId)
|
|
{
|
|
nDC = 35;
|
|
nDamage = d6(8);
|
|
}
|
|
|
|
//Account for Energy Draconic Aura
|
|
if (GetLocalInt(oTrueSpeaker, "FireEnergyAura") > 0)
|
|
{
|
|
nDC += GetLocalInt(oTrueSpeaker, "FireEnergyAura");
|
|
}
|
|
|
|
// Account for the law of resistance
|
|
nDC += GetLawOfResistanceDCIncrease(oTrueSpeaker, nSpellId);
|
|
|
|
if(GetIsSkillSuccessful(oTrueSpeaker, SKILL_TRUESPEAK, nDC))
|
|
{
|
|
int nSaveDC = 10 + GetLevelByClass(CLASS_TYPE_BRIMSTONE_SPEAKER, oTrueSpeaker) + GetAbilityModifier(ABILITY_CONSTITUTION, oTrueSpeaker);
|
|
// Increases the DC of the subsequent utterances
|
|
DoLawOfResistanceDCIncrease(oTrueSpeaker, nSpellId);
|
|
|
|
location lTrueSpeaker = GetLocation(oTrueSpeaker);
|
|
location lTarget = PRCGetSpellTargetLocation();
|
|
vector vOrigin = GetPosition(oTrueSpeaker);
|
|
float fLength = FeetToMeters(20.0f);
|
|
float fDelay;
|
|
|
|
// Do VFX. This is moderately heavy, so it isn't duplicated by Twin Power
|
|
float fAngle = GetRelativeAngleBetweenLocations(lTrueSpeaker, lTarget);
|
|
float fSpiralStartRadius = FeetToMeters(1.0f);
|
|
float fRadius = FeetToMeters(5.0f);
|
|
float fDuration = 4.5f;
|
|
float fVFXLength = GetVFXLength(lTrueSpeaker, fLength, GetRelativeAngleBetweenLocations(lTrueSpeaker, lTarget));
|
|
// A tube of beams, radius 5ft, starting 1m from TrueSpeaker and running for the length of the line
|
|
BeamGengon(DURATION_TYPE_TEMPORARY, VFX_BEAM_FIRE, lTrueSpeaker, fRadius, fRadius,
|
|
1.0f, fVFXLength, // Start 1m from the TrueSpeaker, end at LOS end
|
|
8, // 8 sides
|
|
fDuration, "prc_invisobj",
|
|
0.0f, // Drawn instantly
|
|
0.0f, 0.0f, 45.0f, "y", fAngle, 0.0f,
|
|
-1, -1, 0.0f, 1.0f, // No secondary VFX
|
|
fDuration
|
|
);
|
|
// A spiral inside the tube, starting from the TrueSpeaker with with radius 1ft and ending with radius 5ft at the end of the line
|
|
BeamPolygonalSpring(DURATION_TYPE_TEMPORARY, VFX_BEAM_FIRE, lTrueSpeaker, fSpiralStartRadius, fRadius,
|
|
0.0f, fVFXLength, // Start at the TrueSpeaker, end at LOS end
|
|
5, // 5 sides per revolution
|
|
fDuration, "prc_invisobj",
|
|
fVFXLength / 5, // Revolution per 5 meters
|
|
0.0f, // Drawn instantly
|
|
0.0f, "y", fAngle, 0.0f,
|
|
-1, -1, 0.0f, 1.0f, // No secondary VFX
|
|
fDuration
|
|
);
|
|
|
|
oTarget = MyFirstObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, vOrigin);
|
|
while(GetIsObjectValid(oTarget))
|
|
{
|
|
if(oTarget != oTrueSpeaker &&
|
|
spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oTrueSpeaker)
|
|
)
|
|
{
|
|
// Let the AI know
|
|
SignalEvent(oTarget, EventSpellCastAt(oTrueSpeaker, nSpellId));
|
|
|
|
nDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nSaveDC, SAVING_THROW_TYPE_FIRE);
|
|
if(nDamage > 0)
|
|
{
|
|
float fDelay = GetDistanceBetweenLocations(lTrueSpeaker, GetLocation(oTarget)) / 20.0f;
|
|
effect eDamage = EffectDamage(nDamage, DAMAGE_TYPE_FIRE);
|
|
effect eVis = EffectVisualEffect(VFX_IMP_FLAME_M);
|
|
DelayCommand(1.0f + fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget));
|
|
DelayCommand(1.0f + fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
|
|
}
|
|
}// end if - Target validity check
|
|
|
|
// Get next target
|
|
oTarget = MyNextObjectInShape(SHAPE_SPELLCYLINDER, fLength, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, vOrigin);
|
|
}
|
|
}// end if - Successful utterance
|
|
}
|
|
|
|
float GetVFXLength(location lTrueSpeaker, float fLength, float fAngle)
|
|
{
|
|
float fLowerBound = 0.0f;
|
|
float fUpperBound = fLength;
|
|
float fVFXLength = fLength / 2;
|
|
vector vVFXOrigin = GetPositionFromLocation(lTrueSpeaker);
|
|
vector vAngle = AngleToVector(fAngle);
|
|
vector vVFXEnd;
|
|
int bConverged = FALSE;
|
|
while(!bConverged)
|
|
{
|
|
// Create the test vector for this loop
|
|
vVFXEnd = vVFXOrigin + (fVFXLength * vAngle);
|
|
|
|
// Determine which bound to move.
|
|
if(LineOfSightVector(vVFXOrigin, vVFXEnd))
|
|
fLowerBound = fVFXLength;
|
|
else
|
|
fUpperBound = fVFXLength;
|
|
|
|
// Get the new middle point
|
|
fVFXLength = (fUpperBound + fLowerBound) / 2;
|
|
|
|
// Check if the locations have converged
|
|
if(fabs(fUpperBound - fLowerBound) < 2.5f)
|
|
bConverged = TRUE;
|
|
}
|
|
|
|
return fVFXLength;
|
|
} |