202 lines
7.1 KiB
Plaintext
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();
|
|
} |