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:
@@ -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);
|
||||
|
@@ -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()
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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");
|
||||
|
@@ -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");
|
||||
|
@@ -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);
|
||||
|
55
nwn/nwnprc/trunk/scripts/prc_verdantlord.nss
Normal file
55
nwn/nwnprc/trunk/scripts/prc_verdantlord.nss
Normal 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 Gaea’s Embrace ///////////////////////////////////////////////////////////////
|
||||
/* Gaea’s 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
|
180
nwn/nwnprc/trunk/scripts/vl_animate_tree.nss
Normal file
180
nwn/nwnprc/trunk/scripts/vl_animate_tree.nss
Normal 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));
|
||||
}
|
Reference in New Issue
Block a user