Setup onSpawn Evolved Undead template

Setup onSpawn Evolved Undead template.  Started baseitem work for new iprop forge system.  Full compile.  Updated release archive.
This commit is contained in:
Jaysyn904
2024-12-13 14:05:19 -05:00
parent 1c5cbbe7ea
commit 582b9dbb49
23 changed files with 1387 additions and 17 deletions

View File

@@ -43,7 +43,8 @@ const int EVENT_USER_DEFINED_POSTSPAWN = 1511;
#include "ms_name_inc"
#include "x2_inc_switches"
#include "prc_inc_racial"
#include "sd_lootsystem"
void Embiggen(object oNPC, float fIncrease);
@@ -52,6 +53,44 @@ 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()
{
@@ -66,6 +105,8 @@ void main()
//:: Declare major variables
object oNPC;
int iRace = MyPRCGetRacialType(OBJECT_SELF);
string sTag;
string sResRef = GetResRef(OBJECT_SELF);
@@ -577,9 +618,22 @@ 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);
//:: Set threatlevel name text
SetThreatLevel(OBJECT_SELF);
//:: Execute OnSpawn script.
int nSpider = GetStringLeft(GetTag(OBJECT_SELF), 12) == "MONST_SPIDER" ? TRUE : FALSE;
if(nSpider)

View File

@@ -0,0 +1,414 @@
/* 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"
//:: Adds Evolved SLA's to jCreature.
//::
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", max(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", max(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", max(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", max(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", max(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", max(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", max(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", max(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", max(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", max(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", max(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", max(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", max(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, max(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;
int bIncorporeal = GetIsIncorporeal(oBaseCreature);
int iBaseRace = MyPRCGetRacialType(oBaseCreature);
int nCasterLevel = PRCGetCasterLevel(oBaseCreature);
int iEvolution = GetLocalInt(oBaseCreature, "UNDEAD_EVOLUTION");
//:: Creatures & NPCs only
if ((GetObjectType(oBaseCreature) != OBJECT_TYPE_CREATURE) || (GetIsPC(oBaseCreature) == TRUE))
{
if(DEBUG) DoDebug("Not a creature");
return;
}
//:: Undead only
if(iBaseRace != RACIAL_TYPE_UNDEAD)
{
//SendMessageToPC(GetFirstPC(), "make_evolved: Invalid racial type for template.");
if(DEBUG) DoDebug("make_evolved: Invalid racial type for template.");
return;
}
int nBaseHD = GetHitDice(oBaseCreature);
int nBaseCR = FloatToInt(GetChallengeRating(oBaseCreature));
json jBaseCreature = ObjectToJson(oBaseCreature, TRUE);
json jNewCreature;
json jFinalCreature;
//:: Add Spell-like abilities
jNewCreature = json_AddEvolvedPowers(jBaseCreature, nBaseHD, nCasterLevel, iEvolution);
//:: Update stats
if(bIncorporeal)
{
//:: Incorporeal = CHA only
jNewCreature = json_UpdateStats(jNewCreature, oBaseCreature, 0, 0, 0, 0, 0, 2);
}
else
{
jNewCreature = json_UpdateStats(jNewCreature, oBaseCreature, 2, 0, 0, 0, 0, 2);
}
//:: Update CR
jFinalCreature = json_UpdateCR(jNewCreature, nBaseCR, 1);
//:: Update the creature
oNewCreature = JsonToObject(jFinalCreature, GetLocation(oBaseCreature));
DestroyObject(oBaseCreature, 0.0f);
//:: Apply effects
ApplyEvolvedEffects(oNewCreature, nBaseHD, nCasterLevel, iEvolution);
PRCForceRest(oNewCreature);
//:: Update creature's name on first advancement
string sBaseName = GetName(oNewCreature);
if(iEvolution < 1)
{
SetName(oNewCreature, "Evolved "+ sBaseName);
}
if(iEvolution < 4)
{
SetName(oNewCreature, "Greater "+ sBaseName);
}
//:: Update race field
SetSubRace(oNewCreature, "Undead (Augmented)");
SetAge(oNewCreature, GetAge(oNewCreature) + d100(1));
//:: Set variables
SetLocalInt(oNewCreature, "UNDEAD_EVOLUTION", iEvolution+1);
SetLocalInt(oNewCreature, "TEMPLATE_EVOLVED", 1);
}

View File

@@ -0,0 +1,174 @@
/* 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));
}
}
//:: Immunity to all gaze attacks
effect EffectGazeImmune()
{
effect eBlank;
effect eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_CHARM);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_CHARM);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_CONFUSION);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DAZE);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DEATH);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DESTROY_CHAOS);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DESTROY_EVIL);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DESTROY_GOOD);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DESTROY_LAW);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DOMINATE);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_DOOM);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_FEAR);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_PARALYSIS);
eReturn = EffectSpellImmunity(SPELLABILITY_GAZE_STUNNED);
eReturn = TagEffect(eReturn, "PRCGazeImmune");
return eReturn;
}
// 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;
}
}
//:: Directly modifies jCreature's Challenge Rating.
//:: This is useful for most XP calculations.
//::
json json_UpdateCR(json jCreature, int nBaseCR, int nCRMod)
{
int nNewCR;
//:: Add CRMod to current CR
nNewCR = nBaseCR + nCRMod;
//:: Modify Challenge Rating
jCreature = GffReplaceFloat(jCreature, "ChallengeRating", IntToFloat(nNewCR));
return jCreature;
}
//:: Directly modifies oCreature's ability scores.
//::
json json_UpdateStats(json jCreature, object oBaseCreature, int iModStr = 0, int iModDex = 0, int iModCon = 0, int iModInt = 0, int iModWis = 0, int iModCha = 0)
{
//:: Retrieve and modify ability scores
int iCurrentStr = GetAbilityScore(oBaseCreature, ABILITY_STRENGTH);
int iCurrentDex = GetAbilityScore(oBaseCreature, ABILITY_DEXTERITY);
int iCurrentCon = GetAbilityScore(oBaseCreature, ABILITY_CONSTITUTION);
int iCurrentInt = GetAbilityScore(oBaseCreature, ABILITY_INTELLIGENCE);
int iCurrentWis = GetAbilityScore(oBaseCreature, ABILITY_WISDOM);
int iCurrentCha = GetAbilityScore(oBaseCreature, ABILITY_CHARISMA);
jCreature = GffReplaceByte(jCreature, "Str", iCurrentStr + iModStr);
jCreature = GffReplaceByte(jCreature, "Dex", iCurrentDex + iModDex);
jCreature = GffReplaceByte(jCreature, "Con", iCurrentCon + iModCon);
jCreature = GffReplaceByte(jCreature, "Int", iCurrentInt + iModInt);
jCreature = GffReplaceByte(jCreature, "Wis", iCurrentWis + iModWis);
jCreature = GffReplaceByte(jCreature, "Cha", iCurrentCha + iModCha);
return jCreature;
}
//:: Directly modifies oCreature's Base Natural AC if iNewAC is higher.
//::
json json_UpdateBaseAC(json jCreature, int iNewAC)
{
//json jBaseAC = GffGetByte(jCreature, "Creature/value/NaturalAC/value");
json jBaseAC = GffGetByte(jCreature, "NaturalAC");
if (jBaseAC == JsonNull())
{
return JsonNull();
}
else if (JsonGetInt(jBaseAC) > iNewAC)
{
return jCreature;
}
else
{
jCreature = GffReplaceByte(jCreature, "NaturalAC", iNewAC);
return jCreature;
}
}
//:: Function to calculate the maximum possible hitpoints for oCreature
int GetMaxPossibleHP(object oCreature)
{
int nMaxHP = 0; // Stores the total maximum hitpoints
int i = 1; // Initialize position for class index
int nConb = GetAbilityModifier(ABILITY_CONSTITUTION, oCreature);
// Loop through each class position the creature may have, checking each class in turn
while (TRUE)
{
// Get the class ID at position i
int nClassID = GetClassByPosition(i, oCreature);
// If class is invalid (no more classes to check), break out of loop
if (nClassID == CLASS_TYPE_INVALID)
break;
// Get the number of levels in this class
int nClassLevels = GetLevelByClass(nClassID, oCreature);
// Get the row index of the class in classes.2da by using class ID as the row index
int nHitDie = StringToInt(Get2DAString("classes", "HitDie", nClassID));
// Add maximum HP for this class (Hit Die * number of levels in this class)
nMaxHP += nClassLevels * nHitDie;
// Move to the next class position
i++;
}
nMaxHP += nConb * GetHitDice(oCreature);
return nMaxHP;
}
//:: void main(){}

View File

@@ -20,16 +20,20 @@ void SendMessageToAllPC(string sMessage);
void SetTlkOverrideForMaximumLevel(object oPC)
{
int nLevel = GetHitDice(oPC);
if (nLevel < 40)
{
NWNX_Player_SetTlkOverride(oPC, 315, "");
}
else
{
NWNX_Player_SetTlkOverride(oPC, 315, "Next Level: " + IntToString((nLevel + 1) * nLevel * 500) + "\n");
}
int nLevel = GetHitDice(oPC);
if (nLevel >= 60)
{
NWNX_Player_SetTlkOverride(oPC, 315, "Maximum Level");
}
else if (nLevel > 39)
{
NWNX_Player_SetTlkOverride(oPC, 315, "Next Level: " + IntToString((nLevel + 1) * nLevel * 500) + "\n");
}
else
{
NWNX_Player_SetTlkOverride(oPC, 315, "");
}
}
void SendMessageToAllPC(string sMessage)

View File

@@ -27,6 +27,44 @@ void ReallyEquipItemInSlot(object oNPC, object oItem, int nSlot)
}
}
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()
{
//:: Initialize Major Variables
@@ -47,10 +85,6 @@ void main()
object oSkelly;
object oPC = GetFirstObjectInArea(oArea);
//:: Set threatlevel name text
SetThreatLevel(OBJECT_SELF);
//:: Check if the creature is an animal, beast, construct, or ooze, which should never carry treasure
if (iRacial == RACIAL_TYPE_ANIMAL ||
iRacial == RACIAL_TYPE_BEAST ||
@@ -101,6 +135,17 @@ void main()
{
nAveragePCLevel = 2;
}
//:: Setup Evolved Undead
if(iRacial == RACIAL_TYPE_UNDEAD)
{
SetAge(OBJECT_SELF, d20(6));
EvolvedUndeadCheck(OBJECT_SELF);
}
//:: Set threatlevel name text
SetThreatLevel(OBJECT_SELF);
//:: Only active during Halloween week.
if ((sMonthDay == "10/24") ||