Added 1:10000 chance to spawn a Paragon version of any creature. Full compile. Updated release archive.
494 lines
14 KiB
Plaintext
494 lines
14 KiB
Plaintext
/* Paragon Creature Template
|
|
|
|
By: Jaysyn
|
|
Created: 2024-11-14 08:27:30
|
|
|
|
Among the population of every kind of creature are
|
|
some specimens that are its weakest, worst representatives.
|
|
Likewise, every population has its paragons: the strongest,
|
|
smartest, luckiest, and most powerful of the species.
|
|
Paragon creatures may represent the mythical First Creature,
|
|
created in its perfect form by some creator deity, or
|
|
perhaps the evolutionary endpoint of a race after
|
|
thousands of years of steady improvement. Sometimes,
|
|
paragons just spring up accidentally, when all the factors
|
|
are right.
|
|
*/
|
|
|
|
#include "nw_inc_gff"
|
|
#include "prc_inc_spells"
|
|
#include "prc_inc_util"
|
|
#include "npc_template_inc"
|
|
#include "inc_debug"
|
|
|
|
//:: Get a random General feat.
|
|
void ApplyParagonBonusFeat(object oCreature, int iFeat);
|
|
|
|
//:: Adds Paragon SLA's to jCreature.
|
|
//::
|
|
json json_AddParagonPowers(json jCreature)
|
|
{
|
|
// Get the existing SpecAbilityList (if it exists)
|
|
json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList");
|
|
|
|
// Create the SpecAbilityList if it doesn't exist
|
|
if (jSpecAbilityList == JsonNull())
|
|
{
|
|
jSpecAbilityList = JsonArray();
|
|
}
|
|
|
|
//:: Greater Dispelling 3x / Day
|
|
int i;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
json jSpecAbility = JsonObject();
|
|
jSpecAbility = GffAddWord(jSpecAbility, "Spell", 67);
|
|
jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15);
|
|
jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1);
|
|
|
|
// Manually add to the array
|
|
jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility);
|
|
}
|
|
|
|
//:: Add Haste 3x / Day
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
json jSpecAbility = JsonObject();
|
|
jSpecAbility = GffAddWord(jSpecAbility, "Spell", 78);
|
|
jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15);
|
|
jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1);
|
|
|
|
// Manually add to the array
|
|
jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility);
|
|
}
|
|
|
|
//:: See Invisiblity 3x / Day
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
json jSpecAbility = JsonObject();
|
|
jSpecAbility = GffAddWord(jSpecAbility, "Spell", 157);
|
|
jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", 15);
|
|
jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1);
|
|
|
|
// Manually add to the array
|
|
jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility);
|
|
}
|
|
|
|
//:: Add the list to the creature
|
|
jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList);
|
|
|
|
return jCreature;
|
|
}
|
|
|
|
//:: Directly modifies jCreature's Challenge Rating.
|
|
//:: This is useful for most XP calculations.
|
|
//::
|
|
json json_UpdateParagonCR(json jCreature, int nBaseCR, int nBaseHD)
|
|
{
|
|
int nNewCR;
|
|
|
|
//:: Calculate additional CR by HD
|
|
if(nBaseHD <= 6)
|
|
{
|
|
nNewCR = nBaseCR + 18;
|
|
}
|
|
else if(nBaseHD <= 16)
|
|
{
|
|
nNewCR = nBaseCR + 15;
|
|
}
|
|
else
|
|
{nNewCR = nBaseCR + 12;}
|
|
|
|
//:: Modify Challenge Rating
|
|
jCreature = GffReplaceFloat(jCreature, "ChallengeRating"/* /value" */, IntToFloat(nNewCR));
|
|
|
|
return jCreature;
|
|
}
|
|
|
|
//:: Get a random General feat.
|
|
void PickParagonBonusFeat(object oCreature)
|
|
{
|
|
//:: Paragon creatures get a +15 to all ability scores,
|
|
//:: so can always meet feat pre-reqs.
|
|
|
|
//:: Detect spellcasting classes (FOR FUTURE USE)
|
|
int i;
|
|
for (i = 1; i <= 8; i++)
|
|
{
|
|
if (GetIsArcaneClass(GetClassByPosition(i, oCreature)))
|
|
{
|
|
SetLocalInt(oCreature, "ParagonArcaneCaster", 0);
|
|
}
|
|
if (GetIsDivineClass(GetClassByPosition(i, oCreature)))
|
|
{
|
|
SetLocalInt(oCreature, "ParagonDivineCaster", 0);
|
|
}
|
|
}
|
|
switch (Random(18))
|
|
{
|
|
//:: Dodge -> Mobility -> Spring Attack
|
|
case 0:
|
|
{
|
|
int iDodge = GetHasFeat(FEAT_DODGE, oCreature);
|
|
int iMobility = GetHasFeat(FEAT_MOBILITY, oCreature);
|
|
int iSpringAttack = GetHasFeat(FEAT_SPRING_ATTACK, oCreature);
|
|
|
|
//:: Grant only the first missing feat in the chain
|
|
if (iDodge == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_DODGE);
|
|
}
|
|
else if (iMobility == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_MOBILITY);
|
|
}
|
|
else if (iSpringAttack == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_SPRING_ATTACK);
|
|
}
|
|
}
|
|
break;
|
|
//:: Power Attack -> Cleave -> Imp Power Attack -> Great Cleave
|
|
case 1:
|
|
{
|
|
int iPower = GetHasFeat(FEAT_POWER_ATTACK, oCreature);
|
|
int iCleave = GetHasFeat(FEAT_CLEAVE, oCreature);
|
|
int iImpPower = GetHasFeat(FEAT_IMPROVED_POWER_ATTACK, oCreature);
|
|
int iGrCleave = GetHasFeat(FEAT_GREAT_CLEAVE, oCreature);
|
|
|
|
//:: Grant only the first missing feat in the chain
|
|
if (iPower == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_POWER_ATTACK);
|
|
}
|
|
else if (iCleave == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_CLEAVE);
|
|
}
|
|
else if (iImpPower == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_POWER_ATTACK);
|
|
}
|
|
else if (iGrCleave == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_GREAT_CLEAVE);
|
|
}
|
|
}
|
|
break;
|
|
//:: Expertise -> Imp Expertise -> Whirlwind Attack -> Imp Whirlwind Attack
|
|
case 2:
|
|
{
|
|
int iEx = GetHasFeat(FEAT_EXPERTISE, oCreature);
|
|
int iImpEx = GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature);
|
|
int iWhirl = GetHasFeat(FEAT_WHIRLWIND_ATTACK, oCreature);
|
|
int iImpWhirl = GetHasFeat(FEAT_IMPROVED_WHIRLWIND, oCreature);
|
|
|
|
//:: Grant only the first missing feat in the chain
|
|
if (iEx == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_EXPERTISE);
|
|
}
|
|
else if (iImpEx == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_EXPERTISE);
|
|
}
|
|
else if (iWhirl == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_WHIRLWIND_ATTACK);
|
|
}
|
|
else if (iImpWhirl == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_WHIRLWIND);
|
|
}
|
|
}
|
|
break;
|
|
//:: Disarm -> Expertise -> Improved Disarm -> Imp Expertise
|
|
case 3:
|
|
{
|
|
int iDisarm = GetHasFeat(FEAT_DISARM, oCreature);
|
|
int iEx = GetHasFeat(FEAT_EXPERTISE, oCreature);
|
|
int iImpDisarm = GetHasFeat(FEAT_IMPROVED_DISARM, oCreature);
|
|
int iImpEx = GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature);
|
|
|
|
//:: Grant only the first missing feat in the chain
|
|
if (iDisarm == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_DISARM);
|
|
}
|
|
else if (iEx == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_EXPERTISE);
|
|
}
|
|
else if (iImpDisarm == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_DISARM);
|
|
}
|
|
else if (iImpEx == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_EXPERTISE);
|
|
}
|
|
}
|
|
break;
|
|
//:: Toughness
|
|
case 4:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_TOUGHNESS);
|
|
}
|
|
break;
|
|
//:: Great Fortitude
|
|
case 5:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_GREAT_FORTITUDE);
|
|
}
|
|
break;
|
|
//:: Lightining Reflexes
|
|
case 6:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_LIGHTNING_REFLEXES);
|
|
}
|
|
break;
|
|
//:: Iron Will -> Unnatural Will
|
|
case 7:
|
|
{
|
|
int iIronWill = GetHasFeat(FEAT_IRON_WILL, oCreature);
|
|
int iUnnaturalWill = GetHasFeat(FEAT_UNNATURAL_WILL, oCreature);
|
|
|
|
//:: Grant only the first missing feat in the chain
|
|
if (iIronWill == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IRON_WILL);
|
|
}
|
|
else if (iUnnaturalWill == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_UNNATURAL_WILL);
|
|
}
|
|
}
|
|
break;
|
|
//:: Blind-Fight
|
|
case 8:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_BLIND_FIGHT);
|
|
}
|
|
break;
|
|
//:: Improved Initiative
|
|
case 9:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_IMPROVED_INITIATIVE);
|
|
}
|
|
break;
|
|
//:: Alertness
|
|
case 10:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_ALERTNESS);
|
|
}
|
|
break;
|
|
//:: Blooded
|
|
case 11:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_BLOODED);
|
|
}
|
|
break;
|
|
//:: Side-step Charge
|
|
case 12:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_SIDESTEP_CHARGE);
|
|
}
|
|
break;
|
|
//:: Thug
|
|
case 13:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_THUG);
|
|
}
|
|
break;
|
|
//:: Dive for Cover
|
|
case 14:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_DIVE_FOR_COVER);
|
|
}
|
|
break;
|
|
//:: Endurance -> Strong Stomach
|
|
case 15:
|
|
{
|
|
int iEndurance = GetHasFeat(FEAT_ENDURANCE, oCreature);
|
|
int iStrStomach = GetHasFeat(FEAT_STRONG_STOMACH, oCreature);
|
|
|
|
//:: Grant only the first missing feat in the chain
|
|
if (iEndurance == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_ENDURANCE);
|
|
}
|
|
else if (iStrStomach == 0)
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_STRONG_STOMACH);
|
|
}
|
|
}
|
|
break;
|
|
//:: Resist Disease
|
|
case 16:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_RESIST_DISEASE);
|
|
}
|
|
break;
|
|
//:: Resist Poison
|
|
case 17:
|
|
{
|
|
ApplyParagonBonusFeat(oCreature, FEAT_RESIST_POISON);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//:: Check & apply the feat using EffectBonusFeat if it
|
|
//:: doesn't exist on the creature already
|
|
void ApplyParagonBonusFeat(object oCreature, int iFeat)
|
|
{
|
|
// If the creature does not already have the feat, apply it
|
|
if (!GetHasFeat(iFeat, oCreature))
|
|
{
|
|
effect eFeat = EffectBonusFeat(iFeat);
|
|
effect eLink = UnyieldingEffect(eFeat);
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oCreature);
|
|
}
|
|
else
|
|
{
|
|
DelayCommand(0.0f, PickParagonBonusFeat(oCreature));
|
|
}
|
|
}
|
|
|
|
//:: Apply Paragon effects to a non-PC creature
|
|
void ApplyParagonEffects(object oCreature, int nBaseHD, int nBaseCR)
|
|
{
|
|
//:: Declare major variables
|
|
int nNewCR;
|
|
|
|
effect eParagon;
|
|
|
|
//:: Set maximum hit points for each HD
|
|
int nParagonHP = (GetMaxPossibleHP(oCreature) + (nBaseHD * GetAbilityModifier(ABILITY_CONSTITUTION, oCreature)));
|
|
SetCurrentHitPoints(oCreature, nParagonHP);
|
|
|
|
//:: Tripling the speed for all movement types
|
|
eParagon = EffectLinkEffects(eParagon, EffectMovementSpeedIncrease(300));
|
|
|
|
//:: +25 luck bonus on all attack rolls
|
|
eParagon = EffectLinkEffects(eParagon, EffectAttackIncrease(25));
|
|
|
|
//:: +20 luck bonus on damage rolls for melee and thrown ranged attacks
|
|
eParagon = EffectLinkEffects(eParagon, EffectDamageIncrease(20));
|
|
|
|
//:: AC Bonuses: +12 insight, +12 luck
|
|
eParagon = EffectLinkEffects(eParagon, EffectACIncrease(12, AC_DODGE_BONUS));
|
|
eParagon = EffectLinkEffects(eParagon, EffectACIncrease(12, AC_DEFLECTION_BONUS));
|
|
|
|
//:: Boost caster & SLA level by 15
|
|
SetLocalInt(oCreature, PRC_CASTERLEVEL_ADJUSTMENT, 15);
|
|
|
|
//:: Fire and cold resistance 10, or keep the higher existing resistance if applicable
|
|
eParagon = EffectLinkEffects(eParagon, EffectDamageResistance(DAMAGE_TYPE_FIRE, 10));
|
|
eParagon = EffectLinkEffects(eParagon, EffectDamageResistance(DAMAGE_TYPE_COLD, 10));
|
|
|
|
//:: Damage Reduction 20/epic or retain existing DR if higher
|
|
eParagon = EffectLinkEffects(eParagon, EffectDamageReduction(20, DAMAGE_POWER_ENERGY));
|
|
|
|
//:: Spell Resistance equal to CR +10, or retain existing SR if higher
|
|
int iExSR = GetSpellResistance(oCreature);
|
|
int nSpellResistance;
|
|
|
|
if (iExSR < nBaseCR + 10)
|
|
{
|
|
nSpellResistance = nBaseCR + 10;
|
|
}
|
|
else
|
|
{
|
|
nSpellResistance = 0;
|
|
}
|
|
|
|
eParagon = EffectLinkEffects(eParagon, EffectSpellResistanceIncrease(nSpellResistance));
|
|
|
|
//:: Fast Healing 20
|
|
eParagon = EffectLinkEffects(eParagon, EffectRegenerate(20, 6.0f));
|
|
|
|
//:: Saving Throws: +10 insight bonus on all saving throws
|
|
eParagon = EffectLinkEffects(eParagon, EffectSavingThrowIncrease(SAVING_THROW_ALL, 10));
|
|
|
|
//:: Skills: +10 competence bonus to all skill checks
|
|
int nSkillID = 0;
|
|
|
|
while (TRUE)
|
|
{
|
|
//:: Get & check skill
|
|
string sSkillLabel = Get2DACache("skills", "Label", nSkillID);
|
|
|
|
//:: Break when out of skills
|
|
if (sSkillLabel == "")
|
|
break;
|
|
|
|
//:: Apply the skill increase effect for the current skill
|
|
eParagon = EffectLinkEffects(eParagon, EffectSkillIncrease(nSkillID, 10));
|
|
|
|
|
|
//:: Move to the next skill ID
|
|
nSkillID++;
|
|
}
|
|
|
|
//:: Two free general feats.
|
|
PickParagonBonusFeat(oCreature);
|
|
PickParagonBonusFeat(oCreature);
|
|
|
|
eParagon = UnyieldingEffect(eParagon);
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eParagon, oCreature);
|
|
}
|
|
|
|
|
|
void main ()
|
|
{
|
|
//:: Declare major variables
|
|
object oBaseCreature = OBJECT_SELF;
|
|
object oNewCreature;
|
|
|
|
//:: No Template Stacking
|
|
if(GetLocalInt(oBaseCreature, "TEMPLATE_PARAGON") > 0)
|
|
{
|
|
if(DEBUG) DoDebug("No Template Stacking");
|
|
return;
|
|
}
|
|
|
|
//:: Creatures & NPCs only
|
|
if ((GetObjectType(oBaseCreature) != OBJECT_TYPE_CREATURE) || (GetIsPC(oBaseCreature) == TRUE))
|
|
{
|
|
if(DEBUG) DoDebug("Not a creature");
|
|
return;
|
|
}
|
|
|
|
int nBaseHD = GetHitDice(oBaseCreature);
|
|
int nBaseCR = FloatToInt(GetChallengeRating(oBaseCreature));
|
|
|
|
json jBaseCreature = ObjectToJson(oBaseCreature, TRUE);
|
|
json jNewCreature;
|
|
json jFinalCreature;
|
|
|
|
jNewCreature = json_AddParagonPowers(jBaseCreature);
|
|
jNewCreature = json_UpdateParagonCR(jNewCreature, nBaseCR, nBaseHD);
|
|
jNewCreature = json_UpdateBaseAC(jNewCreature, 5);
|
|
jFinalCreature = json_UpdateStats(jNewCreature, oBaseCreature, 15, 15, 15, 15, 15, 15);
|
|
|
|
//:: Update the creature
|
|
oNewCreature = JsonToObject(jFinalCreature, GetLocation(oBaseCreature));
|
|
DestroyObject(oBaseCreature, 0.0f);
|
|
|
|
//:: Apply effects
|
|
ApplyParagonEffects(oNewCreature, nBaseHD, nBaseCR);
|
|
|
|
PRCForceRest(oNewCreature);
|
|
|
|
//:: Adding extra 12 HP per HD as Temporary HP.
|
|
effect eTempHP = EffectTemporaryHitpoints(nBaseHD * 12);
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eTempHP, oNewCreature);
|
|
|
|
//:: Update creature's name
|
|
string sBaseName = GetName(oNewCreature);
|
|
SetName(oNewCreature, "Paragon "+ sBaseName);
|
|
|
|
SetLocalInt(oNewCreature, "TEMPLATE_PARAGON", 1);
|
|
} |