2025/08/15 Update

Added Verdant Lord PrC.
Added Create Infusion feat & crafting system.
Added Magical Artisan: Create Infusion feat.
Added Plant Defiance feat.
Added Plant Control feat.
Added Control Plants spell.
Added Forestfold spell.
Added Immunity from Elements spell.
Added Creeping Cold & Greater Creeping Cold spells.
Added Adrenaline Surge spell.
Added Mundane & Infused Herb baseitem types.
Added Mundane & Enchanted Scepter baseitem types.
Added EffectGazeImmunity() effect.
Added Botanical material type.
Created json library for summoning support.
Updated Plant Domain spells.
Fixed bug w/ Regen Circle.
Fixed weapon size bug with Enlarge & Reduce Person.
Fixed TMI bug in Swarm of Arrows (hopefully)
Fixed Blood in the Water.
Fixed Iron Soul / Master of Nine prereq bug.
Fixed Happo Zanshin to work more like PnP.
Fixed targeting bug w/ Ultrablast.
Fixed Ubiquitous Vision.
Fixed Magic Staves for small creatures.
Gave the summoned "treant" from Treebrother a Barkskin vfx.
Radial spells can now be scribed w/ Scribe Scroll.
Fixed Martial Stances not counting bug w/ levelup NUI (@Rakiov)
This commit is contained in:
Jaysyn904
2025-08-15 13:42:37 -04:00
parent dd67019103
commit 9c7b452b9a
3518 changed files with 50047 additions and 45886 deletions

View File

@@ -168,7 +168,13 @@ void AddDomainFeat(object oPC, object oSkin, int bFuncs)
eBonusFeat = EffectBonusFeat(FEAT_EXTRA_TURNING);
eBonusFeat = SupernaturalEffect(eBonusFeat);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBonusFeat, oPC);
}
}
if (GetHasFeat(FEAT_DOMAIN_POWER_RUNE, oPC))
{
eBonusFeat = EffectBonusFeat(FEAT_SCRIBE_SCROLL);
eBonusFeat = SupernaturalEffect(eBonusFeat);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBonusFeat, oPC);
}
if (GetHasFeat(FEAT_DOMAIN_POWER_DOMINATION, oPC))
{
eBonusFeat = EffectBonusFeat(FEAT_SPELL_FOCUS_ENCHANTMENT);

View File

@@ -256,6 +256,12 @@ int UltMagusMarkerFeats();
//:; Enforces Unseen Seer marker feats
int UnseenMarkerFeats();
//:; Enforces Verdant Lord marker feats
int VerdantLordMarkerFeats();
//:; Enforces Verdant Lord marker feats
int VirtuosoMarkerFeats();
//:; Enforces Warpriest marker feats
int WarpriestMarkerFeats();
@@ -2115,7 +2121,6 @@ int LionofTalisidMarkerFeats()
return FALSE;
}
//:; Enforces Mighty Contender of Kord marker feats
int MCoKMarkerFeats()
{
@@ -3850,6 +3855,47 @@ int UnseenMarkerFeats()
return FALSE;
}
//:; Verdant Lord marker feats
int VerdantLordMarkerFeats()
{
if(GetLevelByClass(CLASS_TYPE_VERDANT_LORD))
{
int nVerdant = GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_ARCHIVIST)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_CLERIC)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_DRUID)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_FAVOURED_SOUL)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_HEALER)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_JOWAW)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_KOTC)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_KOTMC)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_NENTYAR_HUNTER)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_PALADIN)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_RANGER)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_SOHEI)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_SOL)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_OASHAMAN)
+ GetHasFeat(FEAT_VERDANT_LORD_SPELLCASTING_SPSHAMAN);
if(nVerdant > 1)
{
FloatingTextStringOnCreature("A Verdant Lord may only advance a single divine class.", OBJECT_SELF, FALSE);
FloatingTextStringOnCreature("Please reselect your feats.", OBJECT_SELF, FALSE);
return TRUE;
}
if(nVerdant < 1)
{
FloatingTextStringOnCreature("A Verdant Lord must pick one divine class to advance at first level.", OBJECT_SELF, FALSE);
FloatingTextStringOnCreature("Please reselect your feats.", OBJECT_SELF, FALSE);
return TRUE;
}
}
return FALSE;
}
//:; Enforces Virtuoso marker feats
int VirtuosoMarkerFeats()
{
@@ -4142,6 +4188,7 @@ void main()
|| TrueNecroMarkerFeats()
|| UltMagusMarkerFeats()
|| UnseenMarkerFeats()
|| VerdantLordMarkerFeats()
|| VirtuosoMarkerFeats()
|| WarpriestMarkerFeats()
|| WayfarerMarkerFeats()

View File

@@ -108,8 +108,8 @@ void main()
eNoStun = ExtraordinaryEffect(eNoStun);
DelayCommand(0.0f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eNoStun, oPC));
//:: These are handled via cls_feat_formast.2da
/* ipIP =ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_PARALYSIS);
//:: These are handled via cls_feat_formast.2da (they don't seem to be)
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);
@@ -119,7 +119,7 @@ void main()
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); */
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);

View File

@@ -25,9 +25,16 @@ void main ()
{
if(GetLocalInt(oSkin, "Happo"))
return;
AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB), oSkin);
SetLocalInt(oSkin, "Happo", TRUE);
effect eHappo = EffectBonusFeat(FEAT_PRESTIGE_DEFENSIVE_AWARENESS_2);
effect eLink = EffectLinkEffects(eLink, eHappo);
eLink = ExtraordinaryEffect(eLink);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oPC);
//AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyImmunityMisc(IP_CONST_IMMUNITYMISC_BACKSTAB), oSkin);
//SetLocalInt(oSkin, "Happo", TRUE);
}
// Interaction - +4 to Taunt, Persuade, Bluff, and Intimidate

View File

@@ -35,6 +35,54 @@ void SetRancorVar(object oPC);
void SetImprovedRicochetVar(object oPC);
void DoImprovedRicochet(object oPC, object oTarget);
// Called when checking decay
void CheckBloodInTheWaterDecay(object oTarget)
{
int nLastCritTime = GetLocalInt(oTarget, "BITW_LASTCRIT");
int nNow = (GetTimeHour() * 3600) + (GetTimeMinute() * 60) + GetTimeSecond();
// Handle wrap-around at midnight
if (nNow < nLastCritTime)
{
nNow += 24 * 3600; // add one day in seconds
}
if (nNow - nLastCritTime >= 60)
{
// Clear stacks & buff
DeleteLocalInt(oTarget, "BITW_STACKS");
DeleteLocalInt(oTarget, "BITW_LASTCRIT");
effect eOld = GetFirstEffect(oTarget);
while (GetIsEffectValid(eOld))
{
if (GetEffectTag(eOld) == "BITW_BUFF")
{
RemoveEffect(oTarget, eOld);
}
eOld = GetNextEffect(oTarget);
}
}
}
// Returns the blood color string from the creature's appearance.2da entry.
// This corresponds to the "bloodcolr" column in appearance.2da.
//
// Expected results: "(R)ed", "(Y)ellow", "(W)hite", "(G)reen", "(N)one"
// Returns "" on failure or if object is not a creature.
string GetCreatureBloodColor(object oCreature)
{
if (!GetIsObjectValid(oCreature) || GetObjectType(oCreature) != OBJECT_TYPE_CREATURE)
{
return "";
}
int nAppearanceType = GetAppearanceType(oCreature);
return Get2DAString("appearance", "bloodcolr", nAppearanceType);
}
object GetProperTarget(object oPC, object oTarget)
{
location lTarget = GetLocation(oPC);
@@ -342,19 +390,165 @@ void main()
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HEALING_L_LAW), oHealTarget);
}
// Blood in the Water
if(GetHasSpellEffect(MOVE_TC_BLOOD_WATER, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR && !GetIsImmune(oSpellTarget, IMMUNITY_TYPE_CRITICAL_HIT))
// Blood in the Water
if (GetHasSpellEffect(MOVE_TC_BLOOD_WATER, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR)
{
// Fake critical hit check
if (d20() >= GetWeaponCriticalRange(oSpellOrigin, oItem))
{
string sBlood = GetCreatureBloodColor(oSpellTarget);
int bGhost = GetIsIncorporeal(oSpellTarget);
int nRace = MyPRCGetRacialType(oSpellTarget);
effect eVFX;
if (sBlood == "R") eVFX = EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL);
else if (sBlood == "Y") eVFX = EffectVisualEffect(VFX_COM_CHUNK_YELLOW_SMALL);
else if (sBlood == "W") eVFX = EffectVisualEffect(VFX_COM_BLOOD_SPARK_SMALL);
else if (sBlood == "G") eVFX = EffectVisualEffect(VFX_COM_CHUNK_GREEN_SMALL);
else if (sBlood == "N")
{
if (nRace == RACIAL_TYPE_UNDEAD)
{
if (bGhost) eVFX = EffectVisualEffect(VFX_COM_HIT_DIVINE);
else eVFX = EffectVisualEffect(VFX_COM_CHUNK_BONE_MEDIUM);
}
else eVFX = EffectVisualEffect(VFX_COM_CHUNK_STONE_SMALL);
}
else
{
eVFX = EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL); // fallback VFX
}
// Increase total bonus stack count
int nStacks = GetLocalInt(oSpellOrigin, "BITW_STACKS") + 1;
SetLocalInt(oSpellOrigin, "BITW_STACKS", nStacks);
// Store time of last crit as integer seconds
SetLocalInt(oSpellOrigin, "BITW_LASTCRIT", (GetTimeHour() * 3600) + (GetTimeMinute() * 60) + GetTimeSecond());
// Remove old BITW_BUFF effect before applying updated buff
effect eOld = GetFirstEffect(oSpellOrigin);
while (GetIsEffectValid(eOld))
{
if (GetEffectTag(eOld) == "BITW_BUFF")
{
RemoveEffect(oSpellOrigin, eOld);
}
eOld = GetNextEffect(oSpellOrigin);
}
// Apply new combined attack and damage bonus with total stacks
effect eBuff = EffectLinkEffects(
EffectAttackIncrease(nStacks),
EffectDamageIncrease(IPGetDamageBonusConstantFromNumber(nStacks), DAMAGE_TYPE_SLASHING)
);
eBuff = TagEffect(eBuff, "BITW_BUFF");
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBuff, oSpellOrigin);
// Schedule decay check in 60 seconds (will only reset if no new crit since last)
DelayCommand(60.0, CheckBloodInTheWaterDecay(oSpellOrigin));
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oSpellTarget);
}
}
/* if (GetHasSpellEffect(MOVE_TC_BLOOD_WATER, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR)
{
// Fake critical hit check
if (d20() >= GetWeaponCriticalRange(oSpellOrigin, oItem))
{
string sBlood = GetCreatureBloodColor(oSpellTarget);
int bGhost = GetIsIncorporeal(oSpellTarget);
int nRace = MyPRCGetRacialType(oSpellTarget);
effect eVFX;
if (sBlood == "R") eVFX = EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL);
if (sBlood == "Y") eVFX = EffectVisualEffect(VFX_COM_CHUNK_YELLOW_SMALL);
if (sBlood == "W") eVFX = EffectVisualEffect(VFX_COM_BLOOD_SPARK_SMALL);
if (sBlood == "G") eVFX = EffectVisualEffect(VFX_COM_CHUNK_GREEN_SMALL);
if (sBlood == "N")
{
if (nRace == RACIAL_TYPE_UNDEAD)
{
if (bGhost) eVFX = EffectVisualEffect(VFX_COM_HIT_DIVINE);
else eVFX = EffectVisualEffect(VFX_COM_CHUNK_BONE_MEDIUM);
}
else eVFX = EffectVisualEffect(VFX_COM_CHUNK_STONE_SMALL);
}
// --- STACKING LOGIC ---
int nStacks = GetLocalInt(oSpellOrigin, "BITW_STACKS");
nStacks += 1;
SetLocalInt(oSpellOrigin, "BITW_STACKS", nStacks);
// Remove any old bonus effect
effect eOld = GetFirstEffect(oSpellOrigin);
while (GetIsEffectValid(eOld))
{
if (GetEffectTag(eOld) == "BITW_BUFF")
{
RemoveEffect(oSpellOrigin, eOld);
}
eOld = GetNextEffect(oSpellOrigin);
}
// Apply new combined attack/damage bonus
effect eBuff = EffectLinkEffects(
EffectAttackIncrease(nStacks),
EffectDamageIncrease(IPGetDamageBonusConstantFromNumber(nStacks), DAMAGE_TYPE_SLASHING));
eBuff = TagEffect(eBuff, "BITW_BUFF");
DelayCommand(0.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBuff, oSpellOrigin, TurnsToSeconds(1)));
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oSpellTarget);
}
}
*/
/* if(GetHasSpellEffect(MOVE_TC_BLOOD_WATER, oSpellOrigin) && GetBaseItemType(oItem) != BASE_ITEM_ARMOR && !GetIsImmune(oSpellTarget, IMMUNITY_TYPE_CRITICAL_HIT) )
{
// Fake critical hit check
if(d20() >= GetWeaponCriticalRange(oSpellOrigin, oItem))
{
effect eBuff = EffectLinkEffects(EffectAttackIncrease(1), EffectDamageIncrease(DAMAGE_BONUS_1, DAMAGE_TYPE_SLASHING));
effect eVFX = EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL);
effect eBuff = EffectLinkEffects(EffectAttackIncrease(1), EffectDamageIncrease(DAMAGE_BONUS_1, DAMAGE_TYPE_SLASHING));
string sBlood = GetCreatureBloodColor(oSpellTarget);
int bGhost = GetIsIncorporeal(oSpellTarget);
int nRace = MyPRCGetRacialType(oSpellTarget);
if (sBlood == "R") eVFX = EffectVisualEffect(VFX_COM_CHUNK_RED_SMALL);
if (sBlood == "Y") eVFX = EffectVisualEffect(VFX_COM_CHUNK_YELLOW_SMALL);
if (sBlood == "W") eVFX = EffectVisualEffect(VFX_COM_BLOOD_SPARK_SMALL);
if (sBlood == "G") eVFX = EffectVisualEffect(VFX_COM_CHUNK_GREEN_SMALL);
if (sBlood == "N")
{
if (nRace == RACIAL_TYPE_UNDEAD)
{
if(bGhost)
{
eVFX = EffectVisualEffect(VFX_COM_HIT_DIVINE);
}
else
{
eVFX = EffectVisualEffect(VFX_COM_CHUNK_BONE_MEDIUM);
}
}
else
{
eVFX = EffectVisualEffect(VFX_COM_CHUNK_STONE_SMALL);
}
}
DelayCommand(0.0, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBuff, oSpellOrigin, TurnsToSeconds(1)));
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_COM_BLOOD_CRT_YELLOW_HEAD), oSpellOrigin);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oSpellTarget);
}
}
// Fire Riposte
*/
// Fire Riposte
if(GetHasSpellEffect(MOVE_DW_FIRE_RIPOSTE, oSpellOrigin) && GetBaseItemType(oItem) == BASE_ITEM_ARMOR)
{
int nTouchAttack = PRCDoMeleeTouchAttack(oSpellTarget);

View File

@@ -716,6 +716,56 @@ void reqLionOfTalisid(object oPC)
}
}
void reqVerdantLord(object oPC)
{
//:: Get casting ability scores
int iWis = GetAbilityScore(oPC, ABILITY_WISDOM, TRUE);
int iInt = GetAbilityScore(oPC, ABILITY_INTELLIGENCE, TRUE);
//:: Check if the character Control Plants
int bKnowsCtrlPlants = PRCGetIsRealSpellKnown(SPELL_CONTROL_PLANTS, oPC);
int bHasPlantDomain = GetHasFeat(DOMAIN_PLANT) || GetHasFeat(FEAT_BONUS_DOMAIN_PLANT) || GetHasFeat(FEAT_PLANT_DOMAIN_POWER);
//:: Archivist (INT-based)
if(iInt >= 14 && GetLevelByClass(CLASS_TYPE_ARCHIVIST) >= 7 && bKnowsCtrlPlants)
{
SetLocalInt(oPC, "PRC_PrereqVerdantLord", 0);
return;
}
//:: Druid (WIS-based)
if(iWis >= 14 && GetLevelByClass(CLASS_TYPE_DRUID) >= 7 && bKnowsCtrlPlants)
{
SetLocalInt(oPC, "PRC_PrereqVerdantLord", 0);
return;
}
//:: Ranger (WIS-based) <20> Rangers get Plant Control at 3rd level
//:: Rangers get 3rd lvl spells at level 11 w/ a 16 WIS
if(iWis >= 16 && GetLevelByClass(CLASS_TYPE_RANGER) >= 11 && bKnowsCtrlPlants)
{
SetLocalInt(oPC, "PRC_PrereqVerdantLord", 0);
return;
}
//:: Ranger (WIS-based) <20> Rangers get Plant Control at 3rd level
if(iWis >= 13 && GetLevelByClass(CLASS_TYPE_RANGER) >= 12 && bKnowsCtrlPlants)
{
SetLocalInt(oPC, "PRC_PrereqVerdantLord", 0);
return;
}
//:: Shaman (WIS-based & must have plant domain to cast Control Plants)
if(iWis >= 14 && GetLevelByClass(CLASS_TYPE_SHAMAN) >= 7 && bHasPlantDomain)
{
SetLocalInt(oPC, "PRC_PrereqVerdantLord", 0);
return;
}
//:: Cleric (WIS-based & must have plant domain to cast Control Plants)
if(iWis >= 14 && GetLevelByClass(CLASS_TYPE_CLERIC) >= 7 && bHasPlantDomain)
{
SetLocalInt(oPC, "PRC_PrereqVerdantLord", 0);
return;
}
}
void RedWizard(object oPC)
{
@@ -1095,6 +1145,9 @@ void TomeOfBattle(object oPC = OBJECT_SELF)
int nCount = 0;
int nTotal = 0;
int nIron = _CheckPrereqsByDiscipline(oPC, DISCIPLINE_IRON_HEART); //:: Some dumbass forgot this was a discipline -Jaysyn
if (nIron > 0) nCount += 1;
int nDesert = _CheckPrereqsByDiscipline(oPC, DISCIPLINE_DESERT_WIND);
if (nDesert > 0) nCount += 1;
@@ -1119,8 +1172,9 @@ void TomeOfBattle(object oPC = OBJECT_SELF)
int nRaven = _CheckPrereqsByDiscipline(oPC, DISCIPLINE_WHITE_RAVEN);
if (nRaven > 0) nCount += 1;
nTotal = nDevoted + nDiamond + nTiger + nShadow + nStone + nSun + nRaven + nDesert;
nTotal = nDevoted + nDiamond + nTiger + nShadow + nStone + nSun + nRaven + nDesert +nIron;
if (DEBUG) DoDebug("You have "+IntToString(nIron)+" Iron Heart Maneuvers");
if (DEBUG) DoDebug("You have "+IntToString(nDevoted)+" Devoted Spirit Maneuvers");
if (DEBUG) DoDebug("You have "+IntToString(nDiamond)+" Diamond Mind Maneuvers");
if (DEBUG) DoDebug("You have "+IntToString(nTiger)+" Tiger Claw Maneuvers");

View File

@@ -31,6 +31,25 @@ void ResetLionSwiftness(object oPC)
}
}
void ClearBloodintheWater(object oPC = OBJECT_SELF)
{
// Remove all BITW effects
effect eOld = GetFirstEffect(oPC);
while (GetIsEffectValid(eOld))
{
if (GetEffectTag(eOld) == "BITW_BUFF")
{
RemoveEffect(oPC, eOld);
if(DEBUG) DoDebug("prc_rest >> ClearBloodintheWater: Clearing BitW EffectTag");
}
eOld = GetNextEffect(oPC);
}
// Reset stack counter
DeleteLocalInt(oPC, "BITW_STACKS");
if(DEBUG) DoDebug("prc_rest >> ClearBloodintheWater: Clearing BitW Variables");
}
void RemoveExtraImages(object oPC)
{
string sImage1 = "PC_IMAGE"+ObjectToString(oPC)+"mirror";
@@ -99,6 +118,7 @@ void RestFinished(object oPC)
DelayCommand(0.0, ClearMystLocalVars(oPC));
DelayCommand(0.0, ClearLegacyUses(oPC));
DelayCommand(0.1, ClearInvocationLocalVars(oPC));
DelayCommand(0.1, ClearBloodintheWater(oPC));
// To heal up enslaved creatures...
object oSlave = GetLocalObject(oPC, "EnslavedCreature");

View File

@@ -74,7 +74,7 @@ void EpicSwarmOfArrows(object oAttacker)
// Perform a single attack using full base attack bonus
int iBAB = GetBaseAttackBonus(oAttacker);
PerformAttack(
DelayCommand(0.0, PerformAttack(
oTarget,
oAttacker,
EffectVisualEffect(VFX_IMP_PDK_GENERIC_HEAD_HIT),
@@ -83,7 +83,7 @@ void EpicSwarmOfArrows(object oAttacker)
0,
DAMAGE_TYPE_PIERCING,
"*Swarm of Arrows Hit!*",
"*Swarm of Arrows Miss*");
"*Swarm of Arrows Miss*"));
}
}
oTarget = MyNextObjectInShape(SHAPE_SPHERE, fRadius, lAttacker, TRUE, OBJECT_TYPE_CREATURE);

View File

@@ -0,0 +1,55 @@
//::///////////////////////////////////////////////
//:: [Verdant Lord setup script]
//:: [prc_verdantlord.nss]
//:: [Jaysyn 2025-08-15 12:39:28]
//::///////////////////////////////////////////////
#include "prc_inc_spells"
void main()
{
//:: Declare major variables
object oPC = OBJECT_SELF;
object oSkin = GetPCSkin(oPC);
int nVerdant = GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oPC);
effect eEffect;
itemproperty ipIP;
//:: Setup Gaeas Embrace ///////////////////////////////////////////////////////////////
/* Gaeas Embrace: At 10th level, the verdant lord permanently becomes a plant
creature, though all forms of wild shape that the character could previously
use remain available to him. His type changes to plant, and as a result he
gains low-light vision, is immune to poison, sleep, paralysis, stunning, and
polymorphing, and is not subject to critical hits or mind-influencing effects
(charms, compulsions, phantasms, patterns, or morale effects). He no longer
suffers penalties for aging and cannot be magically aged. Any aging penalties
he may already have suffered, however, remain in place. Bonuses still accrue,
and the verdant lord still dies of old age when his time is up. */
//::///////////////////////////////////////////////////////////////////////////////
if (nVerdant >= 10)
{
effect eNoStun = EffectImmunity(IMMUNITY_TYPE_STUN);
eNoStun = SupernaturalEffect(eNoStun);
eNoStun = ExtraordinaryEffect(eNoStun);
DelayCommand(0.0f, ApplyEffectToObject(DURATION_TYPE_PERMANENT, eNoStun, oPC));
//:: Plant Immunities
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);
}
} //:: End

View File

@@ -0,0 +1,180 @@
//::////////////////////////////////////////////////////////
//:: ;-. ,-. ,-. ,-.
//:: | ) | ) / ( )
//:: |-' |-< | ;-:
//:: | | \ \ ( )
//:: ' ' ' `-' `-'
//::///////////////////////////////////////////////////////
//::
/*
Impactscript for Animate Tree.
Animate Tree (Sp): At 8th level, a verdant lord can
animate a tree within 180 feet of him once per day. It
takes a full round for a tree to uproot itself;
thereafter it has a speed of 30 feet and fights as a
treant with respect to attacks and damage. The animated
tree gains a number of bonus Hit Dice equal to the
number of verdant lord levels the character possesses.
Though its Intelligence score is only 2 while animated,
the tree automatically understands the verdant lord<72>s
commands. The character can return the animated tree to
its normal state at will, and it automatically returns
to its normal state if it dies or if the verdant lord
who animated it is incapacitated or moves out of range.
Once the tree returns to its normal state by any means,
the verdant lord cannot animate another tree for 24 hours.
*/
//::
//:://////////////////////////////////////////////
//:: Script: vl_animate_tree.nss
//:: Author: Jaysyn
//:: Created: 2025-08-14 11:06:57
//:://////////////////////////////////////////////
#include "prc_inc_json"
#include "prc_inc_spells"
const float TREE_RANGE_FEET = 180.0;
const string TREE_RESREF = "prc_anim_tree01";
// Checks if caster is outside & above ground
int GetIsOutsideAboveGround(object oPC)
{
if (GetIsAreaInterior(GetArea(oPC))) return FALSE;
if (GetIsAreaNatural(GetArea(oPC)) == FALSE) return FALSE;
return TRUE;
}
// Watch function: despawns tree if caster is dead or out of range
void AnimateTreeWatch(object oTree, object oPC)
{
if(DEBUG) DoDebug("vl_animate_tree >> AnimateTreeWatch: Starting function.");
if (!GetIsObjectValid(oTree) || !GetIsObjectValid(oPC)) return;
if (GetIsDead(oPC) ||
GetDistanceBetween(oTree, oPC) > FeetToMeters(TREE_RANGE_FEET))
{
DestroyObject(oTree);
return;
}
DelayCommand(1.0, AnimateTreeWatch(oTree, oPC));
}
void main()
{
object oPC = OBJECT_SELF;
int nVerdant = GetLevelByClass(CLASS_TYPE_VERDANT_LORD, oPC);
// Check outdoors & above ground
if (!GetIsOutsideAboveGround(oPC))
{
SendMessageToPC(oPC, "This ability can only be used outdoors and above ground.");
return;
}
// Target location
location lTarget = GetSpellTargetLocation();
// Distance check
if (GetDistanceBetweenLocations(GetLocation(oPC), lTarget) > FeetToMeters(TREE_RANGE_FEET))
{
SendMessageToPC(oPC, "That location is too far away.");
return;
}
// Load template
json jTree = TemplateToJson(TREE_RESREF, RESTYPE_UTC);
if (jTree == JSON_NULL)
{
SendMessageToPC(oPC, "TemplateToJson failed <20> bad resref or resource missing.");
return;
}
// Original HD
int nOriginalHD = json_GetCreatureHD(jTree);
if (nOriginalHD <= 0)
{
SendMessageToPC(oPC, "json_GetCreatureHD failed <20> template missing HD data.");
return;
}
//:: Stat boost calc
int nStatBoost = GetStatBoostsFromHD(nOriginalHD, nVerdant);
//:: Add Hit Dice
jTree = json_AddHitDice(jTree, nVerdant);
if (jTree == JSON_NULL)
{
SendMessageToPC(oPC, "json_AddHitDice failed - JSON became invalid.");
return;
}
//:: Update feats
jTree = json_AddFeatsFromCreatureVars(jTree, nOriginalHD);
if (jTree == JSON_NULL)
{
SendMessageToPC(oPC, "json_AddFeatsFromCreatureVars failed <20> JSON became invalid.");
return;
}
//:: Update stats
jTree = json_ApplyAbilityBoostFromHD(jTree, nOriginalHD, nVerdant);
if (jTree == JSON_NULL)
{
SendMessageToPC(oPC, "json_ApplyAbilityBoostFromHD failed <20> JSON became invalid.");
return;
}
// Size increase
if (nVerdant > 9)
{
jTree = json_AdjustCreatureSize(jTree, 1);
if (jTree == JSON_NULL)
{
SendMessageToPC(oPC, "vl_animate_tree: json_AdjustCreatureSize failed - JSON became invalid.");
return;
}
}
// Spawn Animated Tree from JSON
MultisummonPreSummon();
object oTree = JsonToObject(jTree, lTarget);
effect eSummon = ExtraordinaryEffect(EffectSummonCreature("", VFX_FNF_SUMMON_NATURES_ALLY_1, 0.0, 0, VFX_IMP_UNSUMMON, oTree));
ApplyEffectAtLocation(DURATION_TYPE_PERMANENT, eSummon, lTarget);
if (!GetIsObjectValid(oTree))
{
SendMessageToPC(oPC, "JsonToObject failed - could not create creature from edited template.");
return;
}
DelayCommand(0.5, AugmentSummonedCreature(GetResRef(oTree)));
// Set faction to caster<65>s
ChangeFaction(oTree, oPC);
SetLocalObject(oTree, "ANIMATOR", oPC);
SetCurrentHitPoints(oTree, GetMaxPossibleHP(oTree));
// Explosion effect during uprooting
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_NATURE), oTree, 6.0);
effect eBark = EffectVisualEffect(VFX_DUR_PROT_BARKSKIN);
eBark = UnyieldingEffect(eBark);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBark, oTree);
// Full round wait then move
AssignCommand(oTree, ClearAllActions());
AssignCommand(oTree, ActionWait(6.0));
AssignCommand(oTree, ActionMoveToObject(oPC));
// Start watch loop
DelayCommand(6.1, AnimateTreeWatch(oTree, oPC));
}