diff --git a/_module/ncs/en3_ronus_3.ncs b/_module/ncs/en3_ronus_3.ncs index b673d9f9..f70514cb 100644 Binary files a/_module/ncs/en3_ronus_3.ncs and b/_module/ncs/en3_ronus_3.ncs differ diff --git a/_module/ncs/make_evolved.ncs b/_module/ncs/make_evolved.ncs new file mode 100644 index 00000000..152072b4 Binary files /dev/null and b/_module/ncs/make_evolved.ncs differ diff --git a/_module/ncs/make_greenbound.ncs b/_module/ncs/make_greenbound.ncs new file mode 100644 index 00000000..dfe0fd78 Binary files /dev/null and b/_module/ncs/make_greenbound.ncs differ diff --git a/_module/ncs/make_paragon.ncs b/_module/ncs/make_paragon.ncs new file mode 100644 index 00000000..243526a6 Binary files /dev/null and b/_module/ncs/make_paragon.ncs differ diff --git a/_module/ncs/qst_hasitem.ncs b/_module/ncs/qst_hasitem.ncs index c7c95989..8fca229d 100644 Binary files a/_module/ncs/qst_hasitem.ncs and b/_module/ncs/qst_hasitem.ncs differ diff --git a/_module/nss/make_evolved.nss b/_module/nss/make_evolved.nss new file mode 100644 index 00000000..76553e16 --- /dev/null +++ b/_module/nss/make_evolved.nss @@ -0,0 +1,526 @@ +/* Evolved Undead + + By: Jaysyn + Created: 2024-11-14 17:50:32 + + An evolved undead is an undead whose body is flushed with more negative + energy than normal due to an exceptionally long lifetime. Any undead + may gain this template, and in doing so, it retains all its previous + abilities, but becomes more powerful than before. + + */ +#include "nw_inc_gff" +#include "npc_template_inc" +#include "prc_inc_spells" +#include "prc_inc_util" +#include "prc_inc_json" + +//:: Adds Evolved SLA's to jCreature. +//:: +json json_AddEvolvedPowers(json jCreature, int nBaseHD, int nCasterLevel, int iEvolution) +{ + int nAttempts = 0; + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + if (jSpecAbilityList == JsonNull()) jSpecAbilityList = JsonArray(); + + while (nAttempts < 20) // safety cap + { + nAttempts++; + int nRandom = d12(1); + json jSpecAbility = JsonObject(); + + switch(nRandom) + { + case 1: + if (nBaseHD < 6) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 18); + break; + case 2: + if (nBaseHD < 5) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 23); + break; + case 3: + if (nBaseHD < 4) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 25); + break; + case 4: + if (nBaseHD < 3) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 26); + break; + case 5: + if (nBaseHD < 3) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 27); + break; + case 6: + if (nBaseHD < 7) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 364); + break; + case 7: + if (nBaseHD < 5) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 67); + break; + case 8: + if (nBaseHD < 4) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 88); + break; + case 9: + if (nBaseHD < 3) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 78); + break; + case 10: + if (nBaseHD < 4) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 82); + break; + case 11: + if (nBaseHD < 2) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 157); + break; + case 12: + if (nBaseHD < 5) continue; + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 566); + break; + default: + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 46); // Doom fallback + break; + } + + // If jSpecAbility still empty for some reason, retry + if (JsonGetType(jSpecAbility) != JSON_TYPE_OBJECT) continue; + + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + break; + } + + return GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); +} + + +/* json json_AddEvolvedPowers(json jCreature, int nBaseHD, int nCasterLevel, int iEvolution) +{ + int nRandom = d12(1); + + // 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(); + } + + /* 1 circle of death 6 + 2 cloudkill 5 + 3 cone of cold 4 + 4 confusion 3 + 5 contagion 27 - 3rd + 6 creeping doom 364 - 7th + 7 greater dispel magic 67 - 5th + 8 greater invisibility 88 - 4th + 9 haste 78 - 3rd + 10 hold monster 82 - 4th + 11 see invisibility 157 - 2nd + 12 unholy blight 566 - 5th + */ + +/* switch(nRandom) + { + case 1: + if (nBaseHD >= 6) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 18); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 2: + if (nBaseHD >= 5) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 23); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 3: + if (nBaseHD >= 4) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 25); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 4: + if (nBaseHD >= 3) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 26); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 5: + if (nBaseHD >= 3) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 27); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 6: + if (nBaseHD >= 7) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 364); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 7: + if (nBaseHD >= 5) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 67); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 8: + if (nBaseHD >= 4) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 88); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 9: + if (nBaseHD >= 3) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 78); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 10: + if (nBaseHD >= 4) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 82); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 11: + if (nBaseHD >= 2) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 157); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + case 12: + if (nBaseHD >= 5) + { + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 566); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + } + else + { + return json_AddEvolvedPowers(jCreature, nBaseHD, nCasterLevel, iEvolution); + } + break; + default: + // Fallback to Doom for creatures with 1HD or less. + int i; + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 46); // Doom spell + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", PRCMax(nCasterLevel, nBaseHD)); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + break; + } + + return jCreature = GffAddList(jCreature, "SpecAbilityList", jSpecAbilityList); +} + */ + +//:: Apply Evolved effects to a non-PC creature +void ApplyEvolvedEffects(object oCreature, int nBaseHD, int nCasterLevel, int iEvolution) +{ +//:: Declare major variables + int bIncorporeal = GetIsIncorporeal(oCreature); + effect eVolved; + +//:: Boost caster & SLA level + SetLocalInt(oCreature, PRC_CASTERLEVEL_ADJUSTMENT, PRCMax(nCasterLevel, nBaseHD)); + +//:: AC Bonuses: +1 natural or +1 deflection if Incorporal + if(bIncorporeal) + { + eVolved = EffectACIncrease(1+iEvolution, AC_DEFLECTION_BONUS); + } + else + { + eVolved = EffectACIncrease(1+iEvolution, AC_NATURAL_BONUS); + } + +//:: Fast Healing 3 + eVolved = EffectLinkEffects(eVolved, EffectRegenerate(3, 6.0f)); + +//:: Make *really* permanent + eVolved = UnyieldingEffect(eVolved); + +//:: Apply everything + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eVolved, oCreature); +} + + +void main() +{ +//:: Declare major variables + object oBaseCreature = OBJECT_SELF; + object oNewCreature; + + GetObjectUUID(oBaseCreature); + + int bIncorporeal = GetIsIncorporeal(oBaseCreature); + int iBaseRace = MyPRCGetRacialType(oBaseCreature); + int nCasterLevel = PRCGetCasterLevel(oBaseCreature); + + int iEvolution = 1; + int iOldEvolution = GetLocalInt(oBaseCreature, "UNDEAD_EVOLUTION"); + +//:: Creatures & NPCs only + if ((GetObjectType(oBaseCreature) != OBJECT_TYPE_CREATURE) || (GetIsPC(oBaseCreature) == TRUE)) + { + DoDebug("Not a creature"); + return; + } + +//:: Undead only + if(iBaseRace != RACIAL_TYPE_UNDEAD) + { + DoDebug("make_evolved: Invalid racial type for template."); + return; + } + + if(DEBUG) DoDebug("make_evolved: Previous Evolution is: " +IntToString(iOldEvolution)); + + iEvolution = iEvolution + iOldEvolution; + + if(DEBUG) DoDebug("make_evolved: Evolution is: " +IntToString(iEvolution)); + + int nBaseHD = GetHitDice(oBaseCreature); + int nBaseCR = FloatToInt(GetChallengeRating(oBaseCreature)); + + location lSpawnLoc = GetLocation(oBaseCreature); + + json jBaseCreature = ObjectToJson(oBaseCreature, FALSE); + json jNewCreature; + json jFinalCreature; + +//:: Get original name + string sBaseName = GetName(oBaseCreature); + +//:: Add Spell-like abilities + jNewCreature = json_AddEvolvedPowers(jBaseCreature, nBaseHD, nCasterLevel, iEvolution); + +//:: Update stats + if(bIncorporeal) + { + //:: Incorporeal = CHA only + jNewCreature = json_UpdateCreatureStats(jNewCreature, oBaseCreature, 0, 0, 0, 0, 0, 2); + } + else + { + jNewCreature = json_UpdateCreatureStats(jNewCreature, oBaseCreature, 2, 0, 0, 0, 0, 2); + } + +//:: Delete original creature. + if (GetIsObjectValid(oBaseCreature)) + { + AssignCommand(oBaseCreature, ClearAllActions(TRUE)); + + // optional fade / vanish visuals + effect eBlank = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBlank, oBaseCreature, 6.0f); + + DestroyObject(oBaseCreature, 0.1f); + } + +//:: Update CR + jFinalCreature = json_UpdateCR(jNewCreature, nBaseCR, 1); + +//:: Update the creature + oNewCreature = JsonToObject(jFinalCreature, lSpawnLoc); + +//:: Apply effects + ApplyEvolvedEffects(oNewCreature, nBaseHD, nCasterLevel, iEvolution); + +//:: Update name + if(DEBUG) DoDebug("make_evolved: Final evolution is: " +IntToString(iEvolution)); + if (iEvolution == 1) + { + SetName(oNewCreature, "Evolved " + sBaseName); + } + else if (iEvolution == 2) + { + SetName(oNewCreature, "Greater " + sBaseName); + } + else + { + SetName(oNewCreature, sBaseName); + } +//:: Update race field + SetSubRace(oNewCreature, "Undead (Augmented)"); + +//:: Update age + SetAge(oNewCreature, GetAge(oNewCreature) + d100(1)); + +//:: Freshen up + //DelayCommand(0.0f, PRCForceRest(oNewCreature)); + +//:: Set variables + SetLocalInt(oNewCreature, "UNDEAD_EVOLUTION", iEvolution); + SetLocalInt(oNewCreature, "TEMPLATE_EVOLVED", 1); +} \ No newline at end of file diff --git a/_module/nss/make_greenbound.nss b/_module/nss/make_greenbound.nss new file mode 100644 index 00000000..6c929f89 --- /dev/null +++ b/_module/nss/make_greenbound.nss @@ -0,0 +1,259 @@ +/* Greenbound Creature Template + + By: Jaysyn + Created: 2025-09-06 22:24:15 + + A greenbound creature looks much like it did before + transformation, although certain changes are apparent. + The creature's flesh has been replaced by pulpy wood + and thickly corded creepers, and tiny branches stick + out from its torso, arms, and legs. Any feathers, hair, + or fur it once had have been replaced by some combination + of green vines, moss, flowers, and leaves. + + Greenbound creatures speak any languages they knew before + transformation, although their voices are now deep and + gravelly. + +/*/////////////////////////////////////////////////////////// + +#include "nw_inc_gff" +#include "prc_inc_spells" +#include "prc_inc_util" +#include "npc_template_inc" +#include "inc_debug" +#include "prc_inc_json" + +//:: Adds Greenbound SLA's to jCreature. +//:: +json json_AddGreenboundPowers(json jCreature) +{ + // Get the existing SpecAbilityList (if it exists) + json jSpecAbilityList = GffGetList(jCreature, "SpecAbilityList"); + + //:: Get creature's HD + int iHD = json_GetCreatureHD(jCreature); + + // Create the SpecAbilityList if it doesn't exist + if (jSpecAbilityList == JsonNull()) + { + jSpecAbilityList = JsonArray(); + } + +//:: Add Entangle at will (capped @ 20) + int i; + for (i = 0; i < 20; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 53); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", iHD); + jSpecAbility = GffAddByte(jSpecAbility, "SpellFlags", 1); + + // Manually add to the array + jSpecAbilityList = JsonArrayInsert(jSpecAbilityList, jSpecAbility); + } + +//:: Add Vine Mine 1x / Day + for (i = 0; i < 1; i++) + { + json jSpecAbility = JsonObject(); + jSpecAbility = GffAddWord(jSpecAbility, "Spell", 529); + jSpecAbility = GffAddByte(jSpecAbility, "SpellCasterLevel", iHD); + 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; +} + +//:: Apply Greenbound effects +void ApplyGreenboundEffects(object oCreature, int nBaseHD) +{ +//:: Declare major variables + int nNewCR; + object oSkin = GetPCSkin(oCreature); + + itemproperty ipIP; + + effect eGreenbound; + +//:: Give it a barkskin vfx + eGreenbound = EffectLinkEffects(eGreenbound, EffectVisualEffect(VFX_DUR_PROT_BARKSKIN)); + +//:: Plant Immunities + eGreenbound = EffectLinkEffects(eGreenbound, EffectImmunity(IMMUNITY_TYPE_STUN)); + + ipIP =ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_PARALYSIS); + IPSafeAddItemProperty(oSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + + ipIP =ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_POISON); + IPSafeAddItemProperty(oSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + + ipIP =ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_MINDSPELLS); + IPSafeAddItemProperty(oSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + + ipIP =ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_CRITICAL_HITS); + IPSafeAddItemProperty(oSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + + ipIP =ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB); + IPSafeAddItemProperty(oSkin, ipIP, 0.0, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING, FALSE, FALSE); + +//:: Set maximum hit points for each HD + int nMaxHP = GetMaxPossibleHP(oCreature); + SetCurrentHitPoints(oCreature, nMaxHP); + + DoDebug("nMaxHP is: "+IntToString(nMaxHP)+","); + +//:: Resistance to Cold and Electricity (Ex): A greenbound creature gains resistance 10 to cold and electricity. + eGreenbound = EffectLinkEffects(eGreenbound, EffectDamageResistance(DAMAGE_TYPE_ELECTRICAL, 10)); + eGreenbound = EffectLinkEffects(eGreenbound, EffectDamageResistance(DAMAGE_TYPE_COLD, 10)); + +//:: Damage Reduction (Ex): A greenbound creature has damage reduction 10/magic and slashing. + eGreenbound = EffectLinkEffects(eGreenbound, EffectDamageReduction(10, DAMAGE_POWER_PLUS_ONE)); + eGreenbound = EffectLinkEffects(eGreenbound, EffectDamageResistance(DAMAGE_TYPE_BLUDGEONING, 10)); + eGreenbound = EffectLinkEffects(eGreenbound, EffectDamageResistance(DAMAGE_TYPE_PIERCING, 10)); + +//:: Fast Healing (Ex): A greenbound creature heals 3 points of damage each round so long as it has at least 1 hit point. + eGreenbound = EffectLinkEffects(eGreenbound, EffectRegenerate(3, 6.0f)); + +//:: Tremorsense (Ex): Greenbound creatures can automatically sense the location of +//:: anything within 60 feet that is in contact with the ground. + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(488)); + +//:: Grapple Bonus (Ex): The thorny hooks on a greenbound creature's hands and feet +//:: grant it a +4 bonus on grapple checks. (Imp. Grapple) + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(2804)); + +//:: Immunity to Critical Hits + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(3585)); + +//:: Immunity to Sneak Attack + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(3591)); + +//:: Immunity to Poison + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(3590)); + +//:: Immunity to Mind Effects + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(3588)); + +//:: Low-Light Vision + eGreenbound = EffectLinkEffects(eGreenbound, EffectBonusFeat(354)); + +//:: Make *really* permanent + eGreenbound = UnyieldingEffect(eGreenbound); + +//:: Apply everything + ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGreenbound, oCreature); + +//:: Add slam attack + string sResRef; + int nSize = PRCGetCreatureSize(oCreature); + //primary weapon + sResRef = "prc_warf_slam_"; + sResRef += GetAffixForSize(nSize+1); + AddNaturalPrimaryWeapon(oCreature, sResRef, 1); + +} + + +void main () +{ +//:: Declare major variables + object oBaseCreature = OBJECT_SELF; + object oNewCreature; + + string sBaseName = GetName(oBaseCreature); + + GetObjectUUID(oBaseCreature); + + int nRacial = MyPRCGetRacialType(oBaseCreature); + + //:: No Template Stacking + if(GetLocalInt(oBaseCreature, "TEMPLATE_GREENBOUND") > 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; + } +/* + + A "greenbound creature" is an acquired template that can be added to + any animal, fey, giant, humanoid, monstrous humanoid, or vermin. + +*/ + if(nRacial == RACIAL_TYPE_ABERRATION || nRacial == RACIAL_TYPE_CONSTRUCT || nRacial == RACIAL_TYPE_DRAGON || + nRacial == RACIAL_TYPE_ELEMENTAL || nRacial == RACIAL_TYPE_MAGICAL_BEAST || nRacial == RACIAL_TYPE_OOZE || + nRacial == RACIAL_TYPE_OUTSIDER || nRacial == RACIAL_TYPE_PLANT || nRacial == RACIAL_TYPE_UNDEAD ) + { + DoDebug("make_greenbound: Invalid racial type for template."); + return; + } + + int nBaseHD = GetHitDice(oBaseCreature); + int nBaseCR = FloatToInt(GetChallengeRating(oBaseCreature)); + + location lSpawnLoc = GetLocation(oBaseCreature); + + json jBaseCreature = ObjectToJson(oBaseCreature, TRUE); + json jNewCreature; + json jFinalCreature; + +//:: The creature's type changes to plant with the appropriate augmented subtype. +//:: Hit Dice: Change all current Hit Dice to d8s. + jNewCreature = JsonModifyRacialType(jBaseCreature, RACIAL_TYPE_PLANT); + +//:: Armor Class: A greenbound creature's natural armor bonus improves by 6 over that of the base creature. + jNewCreature = json_IncreaseBaseAC(jNewCreature, 6); + +//:: Abilities: Increase from the base creature as follows: Str +6, Dex +2, Con +4, Cha +4. + jNewCreature = json_UpdateCreatureStats(jNewCreature, oBaseCreature, 6, 2, 4, 0, 0, 4); + + if (GetIsObjectValid(oBaseCreature)) + { + AssignCommand(oBaseCreature, ClearAllActions(TRUE)); + + // optional fade / vanish visuals + effect eBlank = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBlank, oBaseCreature, 6.0f); + + DestroyObject(oBaseCreature, 0.1f); + } + +//:: Spell-Like Abilities: At will - entangle, pass without trace, speak with plants; 1/day - wall of thorns. + jNewCreature = json_AddGreenboundPowers(jNewCreature); + +//:: Hit Dice: Change all current Hit Dice to d8s. + jNewCreature = json_RecalcMaxHP(jNewCreature, 8); + +//:: Challenge Rating: Same as the base creature +2 + jFinalCreature = json_UpdateCR(jNewCreature, nBaseCR, 2); + +//:: Update the creature + oNewCreature = JsonToObject(jFinalCreature, lSpawnLoc); + +//:: Apply the non-json effects + ApplyGreenboundEffects(oNewCreature, nBaseHD); + +//:: Update creature's name + SetName(oNewCreature, "Greenbound "+ sBaseName); + +//:: Update race field + SetSubRace(oNewCreature, "Plant (Augmented)"); + +//:: Freshen Up + //DelayCommand(0.0f, PRCForceRest(oNewCreature)); + +//:: Set variables + SetLocalInt(oNewCreature, "TEMPLATE_GREENBOUND", 1); +} \ No newline at end of file diff --git a/_module/nss/make_paragon.nss b/_module/nss/make_paragon.nss new file mode 100644 index 00000000..b7e6796b --- /dev/null +++ b/_module/nss/make_paragon.nss @@ -0,0 +1,512 @@ +/* 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" +#include "prc_inc_json" + +//:: 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; + + location lSpawnLoc = GetLocation(oBaseCreature); + + GetObjectUUID(oBaseCreature); + +//:: 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_UpdateCreatureStats(jNewCreature, oBaseCreature, 15, 15, 15, 15, 15, 15); + +//:: Delete original creature. + if (GetIsObjectValid(oBaseCreature)) + { + AssignCommand(oBaseCreature, ClearAllActions(TRUE)); + + // optional fade / vanish visuals + effect eBlank = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY); + ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBlank, oBaseCreature, 6.0f); + + DestroyObject(oBaseCreature, 0.1f); + } + +//:: Update the creature + oNewCreature = JsonToObject(jFinalCreature, lSpawnLoc); + +//:: Apply effects + ApplyParagonEffects(oNewCreature, nBaseHD, nBaseCR); + +//:: 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); + +//:: Freshen Up + //DelayCommand(0.0f, PRCForceRest(oNewCreature)); + +//:: Set variables + SetLocalInt(oNewCreature, "TEMPLATE_PARAGON", 1); +} \ No newline at end of file diff --git a/_module/nss/npc_template_inc.nss b/_module/nss/npc_template_inc.nss new file mode 100644 index 00000000..6d2e1b73 --- /dev/null +++ b/_module/nss/npc_template_inc.nss @@ -0,0 +1,50 @@ +/* npc_template_inc + + Common functions for Creature Templates + + By: Jaysyn + Created: 2024-11-14 08:27:30 + +*/ +#include "prc_inc_fork" +#include "nw_inc_gff" +#include "prc_inc_natweap" +#include "prc_inc_util" + +void ReallyEquipItemInSlot(object oNPC, object oItem, int nSlot); + +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; + } +} +//:: void main(){} \ No newline at end of file