2025/11/25

Added Spell Cancelation tool to end spells early.
Made several Exalted feats available generally.
Fixed prereqs for several Exalted feats.
Fixed typo in Chasing Perfection related itemprops.
Grouped Exalted feats under a masterfeat.
Moved PRC8 Packages far down the packages.2da so as to not conflict with modules.
Updated PRC8 Tester module.
Epic Spell: Summon Aberration no longer sucks.  Creatures were updated to match PnP and now level with caster.
Twinfiend summon now receives the correct number of skill points for their bonus HD.
Added LevelUpSummon() function for handling creatures w/ class levels.
Further tweaking for the prc_2da_cache creature to prevent NPCs from attacking it.
Add paragon & psuedonatural template related json functions.
Gated errant debug message in prc_amagsys_gain.nss.
Add DM Tool for viewing PC's current character sheet, templates & spell effects.
Arrow of Bone shouldn't provide free mundane arrows anymore.  Needs testing.
Fixed a bunch of minor TLK typos.
This commit is contained in:
Jaysyn904
2025-11-25 09:00:22 -05:00
parent 80070703b4
commit 257cb23488
41 changed files with 3440 additions and 621 deletions

View File

@@ -20,7 +20,13 @@
#include "inc_debug"
#include "prc_inc_racial"
#include "prc_inc_nwscript"
#include "prc_inc_spells"
#include "prc_inc_util"
#include "prc_inc_fork"
#include "prc_inc_natweap"
//:: Get a random General feat.
void ApplyParagonBonusFeat(object oCreature, int iFeat);
//::---------------------------------------------|
//:: Helper functions |
@@ -131,6 +137,440 @@ int GetAbilityModFromValue(int nAbilityValue)
return nMod;
}
//:: 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 ReallyEquipItemInSlot(object oNPC, object oItem, int nSlot)
{
if (GetItemInSlot(nSlot) != oItem)
{
//ClearAllActions();
AssignCommand(oNPC, ActionEquipItem(oItem, nSlot));
DelayCommand(0.5, ReallyEquipItemInSlot(oNPC, oItem, nSlot));
}
}
// Get the size of a JSON array
int GetJsonArraySize(json jArray)
{
int iSize = 0;
while (JsonArrayGet(jArray, iSize) != JsonNull())
{
iSize++;
}
return iSize;
}
int CheckForWeapon(object oCreature)
{
if (GetIsWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature)) == 1 || GetIsWeapon(GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature)) == 1)
{
// oCreature has a weapon in at least one hand
return TRUE;
}
else
{
// oCreature doesn't have a weapon in either hand
return FALSE;
}
}
//:: Adds Psuedonatural resistances & DR.
void ApplyPseudonaturalEffects(object oCreature)
{
if(!GetIsObjectValid(oCreature)) return;
int nHD = GetHitDice(oCreature);
if(DEBUG) DoDebug("prc_inc_json >> ApplyPseudonaturalEffects: nHD is: "+IntToString(nHD)+".");
// -------------------------
// Spell Resistance
// SR = 10 + HD (max 25)
// -------------------------
int nSR = 10 + nHD;
if(nSR > 25) nSR = 25;
effect eSR = EffectSpellResistanceIncrease(nSR);
eSR = TagEffect(eSR, "PSEUDO_SR");
eSR = EffectLinkEffects(eSR, UnyieldingEffect(eSR));
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSR, oCreature);
// -------------------------
// Acid/Electricity Resistance
// Reference Table:
// HD 1<>3 : Resist 5
// HD 4<>7 : Resist 5
// HD 8<>11 : Resist 10
// HD >=12 : Resist 15
// -------------------------
int nResist;
if(nHD <= 7) nResist = 5;
else if(nHD <=11) nResist = 10;
else nResist = 15;
effect eResAcid = EffectDamageResistance(DAMAGE_TYPE_ACID, nResist);
eResAcid = TagEffect(eResAcid, "PSEUDO_RES_ACID");
eResAcid = EffectLinkEffects(eResAcid, UnyieldingEffect(eResAcid));
effect eResElec = EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, nResist);
eResElec = TagEffect(eResElec, "PSEUDO_RES_ELEC");
eResElec = EffectLinkEffects(eResElec, UnyieldingEffect(eResElec));
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eResAcid, oCreature);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eResElec, oCreature);
// -------------------------
// Damage Reduction
// Reference Table:
// HD 1<>3 : none
// HD 4<>7 : DR 5 / magic
// HD 8<>11 : DR 5 / magic
// HD >=12 : DR 10 / magic
// -------------------------
int nDR;
if(nHD <= 3) { nDR = 0; }
else if(nHD <= 11) { nDR = 5; }
else { nDR = 10; }
effect eDR = EffectDamageReduction(nDR, DAMAGE_POWER_PLUS_ONE, 0, FALSE);
eDR = TagEffect(eDR, "PSEUDO_DR_MAGIC");
eDR = EffectLinkEffects(eDR, UnyieldingEffect(eDR));
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDR, oCreature);
}
//::---------------------------------------------|
//:: JSON functions |
@@ -150,6 +590,23 @@ int json_GetCONValue(json jCreature)
return nCon;
}
//:: Returns the Challenge Rating from a GFF creature UTC
float json_GetChallengeRating(json jCreature)
{
float fCR = 0.25; // default if missing
if (GffGetFieldExists(jCreature, "ChallengeRating"))
{
json jCR = GffGetFloat(jCreature, "ChallengeRating");
if (jCR != JsonNull())
{
fCR = JsonGetFloat(jCR);
}
}
return fCR;
}
//:: Returns the integer value of a VarTable entry named sVarName, or 0 if not found.
int json_GetLocalIntFromVarTable(json jCreature, string sVarName)
{
@@ -190,6 +647,46 @@ int json_GetLocalIntFromVarTable(json jCreature, string sVarName)
return 0;
}
//:: Returns the string value of a VarTable entry named sVarName, or "" if not found.
string json_GetLocalStringFromVarTable(json jCreature, string sVarName)
{
json jVarTable = GffGetList(jCreature, "VarTable");
if (jVarTable == JsonNull())
return "";
int nCount = JsonGetLength(jVarTable);
int i;
for (i = 0; i < nCount; i++)
{
json jEntry = JsonArrayGet(jVarTable, i);
if (jEntry == JsonNull()) continue;
// Get the Name field using GFF functions
json jName = GffGetString(jEntry, "Name");
if (jName == JsonNull()) continue;
string sName = JsonGetString(jName);
if (sName == sVarName)
{
// Get the Type field to verify it's a string
json jType = GffGetDword(jEntry, "Type");
if (jType != JsonNull())
{
int nType = JsonGetInt(jType);
if (nType == 3) // Type 3 = string
{
// Get the Value field using GFF functions
json jValue = GffGetString(jEntry, "Value");
if (jValue == JsonNull()) return "";
return JsonGetString(jValue);
}
}
}
}
return "";
}
//:: Returns the total Hit Dice from a JSON'd creature GFF.
int json_GetCreatureHD(json jCreature)
{
@@ -219,7 +716,6 @@ int json_GetCreatureHD(json jCreature)
return nHD;
}
json json_RecalcMaxHP(json jCreature, int iHitDieValue)
{
int iHD = json_GetCreatureHD(jCreature);
@@ -273,9 +769,7 @@ json json_RecalcMaxHP(json jCreature, int iHitDieValue)
return jCreature;
}
//:: Reads ABILITY_TO_INCREASE from creature's VarTable and applies stat boosts based on increased HD
json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD)
{
if (jCreature == JsonNull())
@@ -313,6 +807,8 @@ json json_ApplyAbilityBoostFromHD(json jCreature, int nOriginalHD)
}
}
}
if(DEBUG) DoDebug("prc_inc_json >> json_ApplyAbilityBoostFromHD: nCurrentTotalHD = "+IntToString(nCurrentTotalHD)+".");
if (nCurrentTotalHD <= 0)
{
@@ -714,7 +1210,6 @@ json json_AddHitDice(json jCreature, int nAmount)
// Grab the first class entry
json jFirstClass = JsonArrayGet(jClasses, 0);
// Only touch ClassLevel; do NOT modify Class type
json jCurrentLevel = GffGetShort(jFirstClass, "ClassLevel");
int nCurrentLevel = JsonGetInt(jCurrentLevel);
int nNewLevel = nCurrentLevel + nAmount;
@@ -875,16 +1370,16 @@ json json_AdjustCreatureSize(json jCreature, int nSizeDelta, int nIncorporeal =
}
//:: Changes jCreature's creature type.
json JsonModifyRacialType(json jCreature, int nNewRacialType)
json json_ModifyRacialType(json jCreature, int nNewRacialType)
{
if(DEBUG)DoDebug("prc_inc_function >> JsonModifyRacialType: Entering function");
if(DEBUG)DoDebug("prc_inc_json >> json_ModifyRacialType: Entering function");
// Retrieve the RacialType field
json jRacialTypeField = JsonObjectGet(jCreature, "Race");
if (JsonGetType(jRacialTypeField) == JSON_TYPE_NULL)
{
DoDebug("prc_inc_function >> JsonModifyRacialType: JsonGetType error 1: " + JsonGetError(jRacialTypeField));
DoDebug("prc_inc_json >> json_ModifyRacialType: JsonGetType error 1: " + JsonGetError(jRacialTypeField));
//SpeakString("JsonGetType error 1: " + JsonGetError(jRacialTypeField));
return JsonNull();
}
@@ -894,7 +1389,7 @@ json JsonModifyRacialType(json jCreature, int nNewRacialType)
if (JsonGetType(jRacialTypeValue) != JSON_TYPE_INTEGER)
{
DoDebug("prc_inc_function >> JsonModifyRacialType: JsonGetType error 2: " + JsonGetError(jRacialTypeValue));
DoDebug("prc_inc_json >> json_ModifyRacialType: JsonGetType error 2: " + JsonGetError(jRacialTypeValue));
//SpeakString("JsonGetType error 2: " + JsonGetError(jRacialTypeValue));
return JsonNull();
}
@@ -905,6 +1400,206 @@ json JsonModifyRacialType(json jCreature, int nNewRacialType)
return jCreature;
}
//:: 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;
}
//:: Adds Psuedonatural SLA's to jCreature.
//::
json json_AddPsuedonaturalPowers(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();
}
//:: True Strike 1x / Day
int i;
for (i = 0; i < 1; i++)
{
json jSpecAbility = JsonObject();
jSpecAbility = GffAddWord(jSpecAbility, "Spell", 415);
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_UpdatePsuedonaturalCR(json jCreature, int nBaseCR, int nBaseHD)
{
int nNewCR;
//:: Calculate additional CR by HD
if (nBaseHD >= 4 && nBaseHD <= 11)
{
nNewCR = nBaseCR + 1;
}
else if (nBaseHD >= 12)
{
nNewCR = nBaseCR + 2;
}
//:: Modify Challenge Rating
jCreature = GffReplaceFloat(jCreature, "ChallengeRating"/* /value" */, IntToFloat(nNewCR));
return jCreature;
}
//:: Spawns a Psuedonatural creature from a template
object MakePsuedonaturalCreatureFromTemplate(string sResref, location lSpawnLoc)
{
json jPsuedo = TemplateToJson(sResref, RESTYPE_UTC);
if (jPsuedo == JSON_NULL)
{
DoDebug("prc_inc_json >> SpawnPsuedonaturalCreatureFromTemplate: TemplateToJson failed <20> bad resref or resource missing.");
return OBJECT_INVALID;
}
//:: Get current HD
int nCurrentHD = json_GetCreatureHD(jPsuedo);
if (nCurrentHD <= 0)
{
DoDebug("make_psuedonat >> MakePsuedonaturalCreatureFromTemplate failed <20> template missing HD data.");
return OBJECT_INVALID;
}
//:: Get current CR
int nBaseCR = 1;
nBaseCR = json_GetCreatureHD(jPsuedo);
if (nBaseCR <= 0)
{
DoDebug("make_psuedonat >> MakePsuedonaturalCreatureFromTemplate failed <20> template missing CR data.");
return OBJECT_INVALID;
}
//:: Get local vars to transfer over.
int iMinHD = json_GetLocalIntFromVarTable(jPsuedo, "iMinHD");
int iMaxHD = json_GetLocalIntFromVarTable(jPsuedo, "iMaxHD");
int nOriginalHD = json_GetLocalIntFromVarTable(jPsuedo, "nOriginalHD");
int iClass2 = json_GetLocalIntFromVarTable(jPsuedo, "Class2");
int iClass2Package = json_GetLocalIntFromVarTable(jPsuedo, "Class2Package");
int iClass2Start = json_GetLocalIntFromVarTable(jPsuedo, "Class2Start");
int iMagicUse = json_GetLocalIntFromVarTable(jPsuedo, "X2_L_BEH_MAGIC");
string sAI = json_GetLocalStringFromVarTable(jPsuedo, "X2_SPECIAL_COMBAT_AI_SCRIPT");
//:: Adds True Strike 1x / day to jCreature.
jPsuedo = json_AddPsuedonaturalPowers(jPsuedo);
//:: Change jCreature's racialtype to outsider
jPsuedo = json_ModifyRacialType(jPsuedo, RACIAL_TYPE_OUTSIDER);
jPsuedo = json_UpdatePsuedonaturalCR(jPsuedo, nBaseCR, nCurrentHD);
//:: Spawn the creature
object oPsuedo = JsonToObject(jPsuedo, lSpawnLoc);
//:: Set variables
SetLocalInt(oPsuedo, "TEMPLATE_PSUEDONATURAL", 1);
SetLocalInt(oPsuedo, "iMinHD", iMinHD);
SetLocalInt(oPsuedo, "iMaxHD", iMaxHD);
SetLocalInt(oPsuedo, "nOriginalHD", nOriginalHD);
SetLocalInt(oPsuedo, "Class2", iClass2);
SetLocalInt(oPsuedo, "Class2Package", iClass2Package);
SetLocalInt(oPsuedo, "Class2Start", iClass2Start);
SetLocalInt(oPsuedo, "X2_L_BEH_MAGIC", iMagicUse);
SetLocalString(oPsuedo, "X2_SPECIAL_COMBAT_AI_SCRIPT", sAI);
return oPsuedo;
}
//:: Test void
//:: void main (){}