2025-04-30 12:23:15 -04:00

202 lines
7.1 KiB
Plaintext

//::///////////////////////////////////////////////
//:: Katino (Bad Air)
//:: katino.nss
//:: Copyright (c) 2025 WizardryEE Project
//:://////////////////////////////////////////////
/*
Attempts to put to sleep a monster group.
Level 1 Mage spell.
In Wizardry: Proving Grounds of the Mad Overlord,
this is a Level 1 Mage disable spell that targets
a monster group.
*/
//:://////////////////////////////////////////////
//:: Created By: WizardryEE Project
//:: Created On: April 26, 2025
//:://////////////////////////////////////////////
#include "prc_inc_spells"
#include "prc_add_spell_dc"
void main()
{
// Spell implementation for WizardryEE
// Core functionality: Put monster group to sleep
// Spellcast Hook Code
if (!X2PreSpellCastCode()) return;
// Set spell school for proper record keeping
PRCSetSchool(SPELL_SCHOOL_ENCHANTMENT);
// Declare major variables
object oCaster = OBJECT_SELF;
int nCasterLevel = PRCGetCasterLevel(oCaster);
int nMetaMagic = PRCGetMetaMagicFeat();
int nPenetr = nCasterLevel + SPGetPenetr();
// Calculate HD affected (4 + d4 in standard sleep spell)
int nHD = 4 + d4();
// Apply metamagic
if (CheckMetaMagic(nMetaMagic, METAMAGIC_MAXIMIZE))
{
nHD = 8; // Maximum HD affected
}
if (CheckMetaMagic(nMetaMagic, METAMAGIC_EMPOWER))
{
nHD = nHD + (nHD / 2); // +50% HD affected
}
// Calculate duration
int nDuration = 3 + nCasterLevel;
if (CheckMetaMagic(nMetaMagic, METAMAGIC_EXTEND))
{
nDuration *= 2; // Duration is +100%
}
// Create the visual effect
effect eImpact = EffectVisualEffect(VFX_FNF_SLEEP);
// Apply the visual effect at the target location
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eImpact, PRCGetSpellTargetLocation());
// Create the sleep effects
effect eSleep = EffectSleep();
effect eMind = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE);
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
// Link the effects
effect eLink = EffectLinkEffects(eSleep, eMind);
eLink = EffectLinkEffects(eLink, eDur);
// Create a unique local variable name for this casting
string sSpellLocal = "WIZARDRY_SPELL_KATINO_" + ObjectToString(oCaster);
// Get the first target in the spell area
object oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, PRCGetSpellTargetLocation(), TRUE, OBJECT_TYPE_CREATURE);
// Variables for the HD tracking loop
object oLowest;
int bContinueLoop = TRUE;
int nLow;
int nMax = 9; // Maximum HD creature affected
int nCurrentHD;
int bAlreadyAffected;
int nAffected = 0;
// Process targets in order of lowest HD first until we run out of HD to affect
while ((nHD > 0) && (bContinueLoop))
{
nLow = nMax;
bContinueLoop = FALSE;
// Get the first creature in the spell area
oTarget = MyFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, PRCGetSpellTargetLocation(), TRUE, OBJECT_TYPE_CREATURE);
while (GetIsObjectValid(oTarget))
{
// Check if target is valid (hostile, not construct, not undead)
if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster) &&
MyPRCGetRacialType(oTarget) != RACIAL_TYPE_CONSTRUCT &&
MyPRCGetRacialType(oTarget) != RACIAL_TYPE_UNDEAD)
{
// Check if already affected by this casting
bAlreadyAffected = GetLocalInt(oTarget, sSpellLocal);
if (!bAlreadyAffected)
{
// Get the current HD of the target
nCurrentHD = GetHitDice(oTarget);
// Find the lowest HD creature that can be affected
if (nCurrentHD < nLow && nCurrentHD <= nHD && nCurrentHD < 5)
{
nLow = nCurrentHD;
oLowest = oTarget;
bContinueLoop = TRUE;
}
}
}
// Get the next target in the shape
oTarget = MyNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, PRCGetSpellTargetLocation(), TRUE, OBJECT_TYPE_CREATURE);
}
// Process the lowest HD creature found
if (GetIsObjectValid(oLowest))
{
// Fire cast spell at event for the target
SignalEvent(oLowest, EventSpellCastAt(oCaster, GetSpellId()));
// Make SR check
if (!PRCDoResistSpell(oCaster, oLowest, nPenetr))
{
// Make Will save
if (!PRCMySavingThrow(SAVING_THROW_WILL, oLowest, PRCGetSaveDC(oLowest, oCaster), SAVING_THROW_TYPE_MIND_SPELLS))
{
// Check for sleep immunity
if (GetIsImmune(oLowest, IMMUNITY_TYPE_SLEEP) == FALSE)
{
// Apply the sleep effect with visual
effect eLink2 = EffectLinkEffects(eLink, eVis);
int nScaledDuration = PRCGetScaledDuration(nDuration, oLowest);
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink2, oLowest, RoundsToSeconds(nScaledDuration), TRUE, -1, nCasterLevel);
// Set sleep condition flag
SetLocalInt(oLowest, "CONDITION_SLEEPING", 1);
// Increment affected count
nAffected++;
// Notify success
FloatingTextStringOnCreature(GetName(oLowest) + " falls asleep!", oCaster, TRUE);
}
else
{
// Apply just the sleep effect for the immunity message
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eSleep, oLowest, RoundsToSeconds(nDuration), TRUE, -1, nCasterLevel);
// Notify immunity
FloatingTextStringOnCreature(GetName(oLowest) + " is immune to sleep!", oCaster, TRUE);
}
}
else
{
// Notify save
FloatingTextStringOnCreature(GetName(oLowest) + " resisted sleep!", oCaster, TRUE);
}
}
else
{
// Notify SR
FloatingTextStringOnCreature(GetName(oLowest) + " has spell resistance!", oCaster, TRUE);
}
// Mark this creature as processed for this casting
SetLocalInt(oLowest, sSpellLocal, TRUE);
DelayCommand(0.5, DeleteLocalInt(oLowest, sSpellLocal));
// Remove the HD of the creature from the total
nHD = nHD - GetHitDice(oLowest);
oLowest = OBJECT_INVALID;
}
}
// Final notification
if (nAffected > 0)
{
FloatingTextStringOnCreature("KATINO: Put " + IntToString(nAffected) + " enemies to sleep!", oCaster, TRUE);
}
else
{
FloatingTextStringOnCreature("KATINO: No enemies affected", oCaster, TRUE);
}
// Clean up spell school variable
PRCSetSchool();
}