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

@@ -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);