diff --git a/_module/ncs/codi_spawn.ncs b/_module/ncs/codi_spawn.ncs index 34a62639..e354b1b3 100644 Binary files a/_module/ncs/codi_spawn.ncs and b/_module/ncs/codi_spawn.ncs differ diff --git a/_module/ncs/make_paragon.ncs b/_module/ncs/make_paragon.ncs new file mode 100644 index 00000000..1fbf2132 Binary files /dev/null and b/_module/ncs/make_paragon.ncs differ diff --git a/_module/ncs/prc_pwonspawn.ncs b/_module/ncs/prc_pwonspawn.ncs index 70bd633a..dbda667f 100644 Binary files a/_module/ncs/prc_pwonspawn.ncs and b/_module/ncs/prc_pwonspawn.ncs differ diff --git a/_module/ncs/silenttrigger2.ncs b/_module/ncs/silenttrigger2.ncs index 90e8f4e5..2274260f 100644 Binary files a/_module/ncs/silenttrigger2.ncs and b/_module/ncs/silenttrigger2.ncs differ diff --git a/_module/ncs/spawnb_cc_activ.ncs b/_module/ncs/spawnb_cc_activ.ncs index b571555b..de4d1662 100644 Binary files a/_module/ncs/spawnb_cc_activ.ncs and b/_module/ncs/spawnb_cc_activ.ncs differ diff --git a/_module/ncs/spawnb_cc_dactiv.ncs b/_module/ncs/spawnb_cc_dactiv.ncs index 77e80d77..7e94385e 100644 Binary files a/_module/ncs/spawnb_cc_dactiv.ncs and b/_module/ncs/spawnb_cc_dactiv.ncs differ diff --git a/_module/nss/codi_spawn.nss b/_module/nss/codi_spawn.nss index 80f7d86d..dbe8a95d 100644 --- a/_module/nss/codi_spawn.nss +++ b/_module/nss/codi_spawn.nss @@ -53,45 +53,6 @@ void Embiggen(object oNPC, float fIncrease) SetObjectVisualTransform(OBJECT_SELF, OBJECT_VISUAL_TRANSFORM_SCALE, fIncrease); } -void EvolvedUndeadCheck(object oUndead) -{ - // Check if the creature is undead and intelligent - if (MyPRCGetRacialType(oUndead) != RACIAL_TYPE_UNDEAD || !GetAbilityModifier(ABILITY_INTELLIGENCE, oUndead) > -1) - { - return; // Exit if not an intelligent undead - } - - // Get the undead's area and its age - object oArea = GetArea(oUndead); - int iUndeadAge = GetAge(oUndead); - - // Base evolution chance: 1% for every 100 years - int iBaseChance = iUndeadAge / 100; - - // Add chance modifiers: ancient energy and previous evolutions - int iAncientBoost = GetLocalInt(oArea, "EVO_UNDEAD_BOOST"); // Area-specific boost - int iEvolutionCount = GetLocalInt(oUndead, "UNDEAD_EVOLUTION"); // Previous evolutions - int iChance = iBaseChance + iAncientBoost + iEvolutionCount; - - // Roll the dice (1-100) - int iRoll = Random(100) + 1; - - // Debug message to monitor rolls and chances - if(DEBUG) - { - DoDebug("Evolution Check: Roll = " + IntToString(iRoll) + ", Chance = " + IntToString(iChance)); - } - - // Check if the undead evolves - if (iRoll <= iChance) - { - // Apply evolution template - ExecuteScript("make_evolved", oUndead); // Apply template script - } -} - - - void main() { //:: User defined OnSpawn event requested? @@ -618,16 +579,6 @@ void main() ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, OBJECT_SELF); } -//:: Setup Evolved Undead - if(iRace == RACIAL_TYPE_UNDEAD) - { - SetAge(OBJECT_SELF, d20(6)); - - EvolvedUndeadCheck(OBJECT_SELF); - } - - - //:: Set or Randomize name ms_Nomenclature(OBJECT_SELF); diff --git a/_module/nss/make_paragon.nss b/_module/nss/make_paragon.nss new file mode 100644 index 00000000..10f70efa --- /dev/null +++ b/_module/nss/make_paragon.nss @@ -0,0 +1,494 @@ +/* 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); +} \ No newline at end of file diff --git a/_module/nss/prc_pwonspawn.nss b/_module/nss/prc_pwonspawn.nss index 012fc8e6..e552ecd9 100644 --- a/_module/nss/prc_pwonspawn.nss +++ b/_module/nss/prc_pwonspawn.nss @@ -135,6 +135,15 @@ void main() { nAveragePCLevel = 2; } + +//:: Paragon Check + if (Random(9999) == 0) + { + if(GetLocalInt(OBJECT_SELF, "TEMPLATE_PARAGON") < 1) + { + ExecuteScript("make_paragon", OBJECT_SELF); + } + } //:: Setup Evolved Undead if(iRacial == RACIAL_TYPE_UNDEAD) diff --git a/_release/Path of Ascension [PRC8-CEP3].7z b/_release/Path of Ascension [PRC8-CEP3].7z index 236fa095..100fda4a 100644 Binary files a/_release/Path of Ascension [PRC8-CEP3].7z and b/_release/Path of Ascension [PRC8-CEP3].7z differ