//:://///////////////////////////////////////////// //:: 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(); }