Added Mine & Caverns raise & lower functionality

Added Mine & Caverns raise & lower functionality.  Revamped Level 1 events to match PnP.  Updated Level 1: Core.  Added new Footcatcher trap type. Added Underwater heartbeat to Glop Lake Underwater.  Full compile.
This commit is contained in:
Jaysyn904
2023-09-16 09:04:37 -04:00
parent 6b9ab12c25
commit 84d583b489
183 changed files with 62513 additions and 10133 deletions

View File

@@ -0,0 +1,13 @@
void main()
{
// Get the PC who is in this conversation.
object oPC = GetPCSpeaker();
object oSelf = OBJECT_SELF;
string sTag = GetTag(oSelf);
// Give 2-8 gold to the PC.
GiveGoldToCreature(oPC, d6(1)+2);
SetLocalInt(oPC, "FoundCoins"+sTag, 1);
}

View File

@@ -0,0 +1,24 @@
//:: at_spn_grn_slime
//::
//:: Spawns a small green slime
#include "nw_i0_generic"
void main()
{
object oSpawn;
effect eVFX;
object oSelf = OBJECT_SELF;
// Get the PC who is in this conversation.
object oPC = GetPCSpeaker();
string sName = GetName(oPC);
string sTag = GetTag(oSelf);
// Spawn Small Green Slime.
eVFX = EffectVisualEffect(VFX_COM_BLOOD_CRT_GREEN);
oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "zep_greenslimes", GetLocation(oSelf));
AssignCommand(oSpawn, DetermineCombatRound(oPC));
DelayCommand(0.3f, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVFX, oSpawn));
SetLocalInt(oPC, "FoundSlime"+sTag, 1);
}

View File

@@ -0,0 +1,51 @@
void main()
{
//:: Get the placeable object that triggers the script.
object oPlaceable = OBJECT_SELF;
string sTexture;
//:: Check if a custom local variable has been set to indicate that the script has run.
int bHasRun = GetLocalInt(oPlaceable, "HasRun");
if (!bHasRun)
{
switch (d4(1))
{
case 1:
{
sTexture = "tdm01_rock01";
break;
}
case 2:
{
sTexture = "tdm01_rock02";
break;
}
case 3:
{
sTexture = "tdm01_rock03";
break;
}
case 4:
{
sTexture = "tdm01_rock04";
break;
}
}
ReplaceObjectTexture(oPlaceable, "tx_rough_006g", sTexture);
//:: Set the local variable to indicate that the script has run.
SetLocalInt(oPlaceable, "HasRun", 1);
//:: Removed HB script from event to save resources
SetEventScript(oPlaceable, EVENT_SCRIPT_PLACEABLE_ON_HEARTBEAT, "");
}
}

View File

@@ -0,0 +1,27 @@
//:: Returns true if PC passes a DC 10 search check
int StartingConditional()
{
//:: Declare major variables
object oPC = GetPCSpeaker();
object oSelf = OBJECT_SELF;
string sTag =GetTag(oSelf);
if (GetLocalInt(oPC, "FoundCoins"+sTag) == TRUE)
{
return FALSE;
}
// Check if the player's search skill check is successful against DC 10
if (GetIsSkillSuccessful(oPC, SKILL_SEARCH, 10))
{
//:: Player passed the search check
return TRUE;
}
else
{
//:: Player failed the search check
return FALSE;
}
}

View File

@@ -0,0 +1,27 @@
//:: Returns true if PC passes a DC 5 search check
int StartingConditional()
{
//:: Declare major variables
object oPC = GetPCSpeaker();
object oSelf = OBJECT_SELF;
string sTag =GetTag(oSelf);
if (GetLocalInt(oPC, "FoundSlime"+sTag) == TRUE)
{
return FALSE;
}
// Check if the player's search skill check is successful against DC 5
if (GetIsSkillSuccessful(oPC, SKILL_SEARCH, 5))
{
//:: Player passed the search check
return TRUE;
}
else
{
//:: Player failed the search check
return FALSE;
}
}

View File

@@ -0,0 +1,67 @@
/*
OnDisarm
Awards XP for disarming traps
For nonmagical harmless traps set the trap type to "minor entangle trap"
For magical traps, use the local int TRAP_DAMAGE_OVERRIDE
*/
void main()
{
object oPC = GetLastDisarmed();
if (GetIsPC(oPC) )
{
int nTrapCR = 0;
int nDisarmDC = GetTrapDisarmDC(OBJECT_SELF);
int nDetectDC = GetTrapDetectDC(OBJECT_SELF);
int nRow, nDam, nDamOvr;
// Modify the CR based on the Disarm DC
if (nDisarmDC > 30)
{
nTrapCR = nTrapCR + 2;
}
else if (nDisarmDC > 15)
{
nTrapCR = nTrapCR + 1;
}
// Modify the CR based on the Detect DC
if (nDetectDC > 30)
{
nTrapCR = nTrapCR + 2;
}
else if (nDetectDC > 15)
{
nTrapCR = nTrapCR + 1;
}
// Determine the average amount of damage to use in the final CR calculation
nDamOvr = GetLocalInt(OBJECT_SELF, "TRAP_DAMAGE_OVERRIDE");
if (!nDamOvr)
{
nRow = GetTrapBaseType(OBJECT_SELF);
nDam = StringToInt(Get2DAString("traps.2da", "MedianDamage", nRow));
}
else
{
nDam = nDamOvr;
}
// Calculate the total Trap CR
nTrapCR = nTrapCR + (nDam /7);
// Calculate the final award - PC only
int nLevel = GetHitDice(oPC);
int nXPScale = GetModuleXPScale();
float fAward = (300.00 / IntToFloat(nXPScale)) * IntToFloat(nLevel) * pow(2.0, (IntToFloat(nTrapCR)-IntToFloat(nLevel)) * 0.5);
// Award some XP
if (fAward > 0.0)
{
GiveXPToCreature(oPC, FloatToInt(fAward));
}
}
}

View File

@@ -1,43 +0,0 @@
/// ----------------------------------------------------------------------------
/// @file nui_i_library.nss
/// @author Ed Burke (tinygiant98) <af.hog.pilot@gmail.com>
/// @brief Boilerplate code for creating a library dispatcher. Should only be
/// included in library scripts as it implements main().
/// ----------------------------------------------------------------------------
#include "nui_i_main"
// -----------------------------------------------------------------------------
// Function Protoypes
// -----------------------------------------------------------------------------
void DefineForm();
void BindForm();
void HandleNUIEvents();
void HandleModuleEvents();
// -----------------------------------------------------------------------------
// Function Implementations
// -----------------------------------------------------------------------------
// These are dummy implementations to prevent nwnsc from complaining that they
// do not exist. If you want to compile in the toolset rather than using nwnsc,
// comment these lines out.
//#pragma default_function(DefineForm)
//#pragma default_function(BindForm)
//#pragma default_function(HandleNUIEvents)
//#pragma default_function(HandleModuleEvents)
// -----------------------------------------------------------------------------
// Library Dispatch
// -----------------------------------------------------------------------------
void main()
{
string sOperation = GetScriptParam("NUI_FUNCTION");
if (sOperation == NUI_DEFINE) DefineForm();
else if (sOperation == NUI_BIND) BindForm();
else if (sOperation == NUI_EVENT_NUI) HandleNUIEvents();
else HandleModuleEvents();
}

View File

@@ -0,0 +1,89 @@
//::////////////////////////////////////////////////////////////////////////////
/*//
Level 1: The Lair of the Dung Beast
onEnter script
ra_lvl01_onenter.nss
Wandering Monsters: Check once per 30 minutes on 1d20
Detections: Faint evil from the whole place; slightly more to the south east.
Continuous Effects: The stench of this level requires all characters to make
a Fortitude save (DC 10) upon entering the level and every 30 minutes
thereafter or all rolls are at 2 morale penalty due to the distraction
caused by the overpowering smell.
*///
//::////////////////////////////////////////////////////////////////////////////
#include "spawn_functions"
#include "tgdc_explore_inc"
void StenchMessage(object oPC = OBJECT_SELF)
{
//:: Only fire for (real) PCs.
if ( !GetIsPC(oPC) || GetIsDMPossessed(oPC) )
return;
//:: Have text appear over the PC's head.
FloatingTextStringOnCreature("As you enter this area, a terrible, overpowering stench assaults your senses.", oPC, FALSE);
}
void main()
{
//:: Get the entering object (usually a PC)
object oPC = GetEnteringObject();
//:: Only fire once per PC.
if (!GetLocalInt(oPC, "DO_ONCE__" + GetTag(OBJECT_SELF)) )
{
StenchMessage(oPC);
SetLocalInt(oPC, "DO_ONCE__" + GetTag(OBJECT_SELF), TRUE);
}
/*
Spawn_OnAreaEnter() can take three arguments - the name of the heartbeat
script to execute, the heartbeat duration, and a delay for the first
heartbeat. They default to spawn_sample_hb, 6.0, and 0.0 respectively; as
if it were called like: Spawn_OnAreaEnter( "spawn_sample_hb", 6.0, 0.0 );
*/
if ( GetIsAreaAboveGround( OBJECT_SELF ) && ! GetIsAreaNatural( OBJECT_SELF ) )
{
//:: Indoors - no delay on the first HB
Spawn_OnAreaEnter( "spawn_sample_hb", 6.0, 0.0 );
}
else
{
//:: Outdoors or underground - do a 3 second delay on the first HB
Spawn_OnAreaEnter( "spawn_sample_hb", 6.0, 3.0 );
}
//:: Records that the PC has entered Rappan Athuk at least once.
SetLocalInt(oPC, "bEnteredDungeon", 1);
SetLocalInt(oPC, "bEnteredLevelOne", 1);
}

View File

@@ -0,0 +1,51 @@
//::////////////////////////////////////////////////////////////////////////////
/*//
Level 1: The Lair of the Dung Beast
onExit script
ra_lvl01_onexit.nss
Wandering Monsters: Check once per 30 minutes on 1d20
Detections: Faint evil from the whole place; slightly more to the south east.
Continuous Effects: The stench of this level requires all characters to make
a Fortitude save (DC 10) upon entering the level and every 30 minutes
thereafter or all rolls are at 2 morale penalty due to the distraction
caused by the overpowering smell.
*///
//::////////////////////////////////////////////////////////////////////////////
void main()
{
//:: Get the entering object (usually a PC)
object oPC = GetExitingObject();
//:: Clears Level 1 stench message int var,
//:: so it will show up again next time they enter
DelayCommand(0.0f,DeleteLocalInt(oPC, "DO_ONCE__" + GetTag(OBJECT_SELF)));
}

View File

@@ -0,0 +1,107 @@
//::////////////////////////////////////////////////////////////////////////////
/*//
Level 1: The Lair of the Dung Beast
onHeartbeat script
ra_lvl01_onhb.nss
Wandering Monsters: Check once per 30 minutes on 1d20.
Detections: Faint evil from the whole place; slightly more to the south east.
Continuous Effects: The stench of this level requires all characters to make
a Fortitude save (DC 10) upon entering the level and every 30 minutes
thereafter or all rolls are at 2 morale penalty due to the distraction
caused by the overpowering smell.
*///
//::////////////////////////////////////////////////////////////////////////////
//:: Function to process the stench penalty
void ApplyStench(object oPC)
{
int RA_DEBUG = 0;
//:: Check for exising variable on player
int oldTime = GetLocalInt(oPC, "StenchFortSaveTime");
// Get the current system time in seconds
int newTime = (GetTimeHour()*60*60)+(GetTimeMinute()*60)+GetTimeSecond();
// Calculate the time difference in seconds
int timeDifference = newTime - oldTime;
if (RA_DEBUG)
{
SendMessageToPC(oPC, "oldTime = " + IntToString(oldTime));
SendMessageToPC(oPC, "newTime = " + IntToString(newTime));
SendMessageToPC(oPC, "timeDifference = " + IntToString(timeDifference));
}
//:: Check if the character hasn't made a Fortitude save in the last 3 minutes
if (oldTime == 0 || timeDifference >= 180) // 180 seconds = 3 real-time minutes
{
//:: Check if the character failed the save
if (!FortitudeSave(oPC, 10))
{
//:: Apply a -2 morale penalty to all rolls
effect eMoralePenalty = EffectAttackDecrease(2);
effect eVFX = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
effect eLink = EffectLinkEffects(eMoralePenalty, eVFX);
eLink = SupernaturalEffect(eLink);
eLink = TagEffect(eLink, "LvlOneStench");
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oPC, 180.0f); // 180 seconds = 3 real-time minutes & .5 hours in-game.
//:: Store the current time on player
SetLocalInt(oPC, "StenchFortSaveTime", newTime);
if (RA_DEBUG)
{
SendMessageToPC(oPC, "Failed StenchFortSave");
SendMessageToPC(oPC, "Setting StenchFortSaveTime as " + IntToString(newTime));
}
// Send a message to the player
SendMessageToPC(oPC, "The overpowering stench is distracting you.");
}
//:: Store the current time on player
SetLocalInt(oPC, "StenchFortSaveTime", newTime);
if (RA_DEBUG)
{
SendMessageToPC(oPC, "Passed StenchFortSave");
SendMessageToPC(oPC, "Setting StenchFortSaveTime as " + IntToString(newTime));
}
}
}
void main()
{
int RA_DEBUG = 0;
//:: Declare major variables
object oArea = OBJECT_SELF;
object oPC = GetFirstObjectInArea(oArea, OBJECT_TYPE_CREATURE);
while (GetIsObjectValid(oPC))
{
if (GetIsPC(oPC) || (GetMaster(oPC) != OBJECT_INVALID && GetIsPC(GetMaster(oPC))))
{
if (RA_DEBUG)
{
SendMessageToPC(oPC, "Running Level 1 Area HB");
}
//:: Apply the stench
ApplyStench(oPC);
}
// Get the next object in the area
oPC = GetNextObjectInArea(oArea);
}
}

View File

@@ -0,0 +1,83 @@
#include "x0_i0_partywide"
//:: Returns true if PC passes a DC 10 search check.
//:: Only allows one chance to search
int StartingConditional()
{
object oPC = GetPCSpeaker();
string sPlacableTag = GetTag(OBJECT_SELF);
int nPartySearched = GetLocalInt(oPC, "PartySearched_" + sPlacableTag); // Unique flag for each placable
// Check if the party has already successfully searched at this placable
if (nPartySearched == 1)
{
return FALSE;
}
// Check if the player's search skill check is successful against DC 10
if (GetIsSkillSuccessful(oPC, SKILL_SEARCH, 10))
{
// Set the flag on every member of the party to ensure it's only run once per party
SetLocalIntOnAll(oPC, "PartySearched_" + sPlacableTag, 1);
return TRUE; // Condition met, continue with the conversation
}
else
{
return FALSE;
}
}
//:: Returns true if PC passes a DC 10 search check
/*int StartingConditional()
{
//:: Declare major variables
object oPC = GetPCSpeaker();
// Check if the player's search skill check is successful against DC 10
if (GetIsSkillSuccessful(oPC, SKILL_SEARCH, 10))
{
//:: Player passed the search check
return TRUE;
}
else
{
//:: Player failed the search check
return FALSE;
}
} */

View File

@@ -0,0 +1,19 @@
//:: Returns true if PC passes a DC 5 search check
int StartingConditional()
{
//:: Declare major variables
object oPC = GetPCSpeaker();
// Check if the player's search skill check is successful against DC 5
if (GetIsSkillSuccessful(oPC, SKILL_SEARCH, 5))
{
//:: Player passed the search check
return TRUE;
}
else
{
//:: Player failed the search check
return FALSE;
}
}

View File

@@ -0,0 +1,44 @@
// Stench Area OnEnter Script - stench_onenter.nss
// Function to apply the stench penalty
void ApplyStenchPenalty(object oPC)
{
// Calculate the current time in minutes
int currentTime = GetTimeHour() * 60 + GetTimeMinute();
// Check if the character hasn't made a Fortitude save in the last 30 minutes
if (!GetLocalInt(oPC, "FortitudeSaveTime") || currentTime >= GetLocalInt(oPC, "FortitudeSaveTime") + 30)
{
// Roll a Fortitude save (DC 10)
int fortitudeSaveResult = FortitudeSave(oPC, 10, SAVING_THROW_TYPE_NONE, OBJECT_SELF);
// Check if the character failed the save
if (fortitudeSaveResult < 1)
{
// Apply a -2 morale penalty to all rolls
effect eMoralePenalty = EffectAttackDecrease(2, ATTACK_BONUS_MISC);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eMoralePenalty, oPC, 90.0f); // 90 seconds = 3 real time minutes & .5 hours in game.
// Store the current time as the last Fortitude save time
SetLocalInt(oPC, "FortitudeSaveTime", currentTime);
// Send a message to the player
SendMessageToPC(oPC, "The overpowering stench distracts you, imposing a -2 morale penalty to your rolls for the next 30 minutes.");
}
}
}
void main()
{
// Get the entering object (usually a PC)
object oPC = GetEnteringObject();
// Check if the entering object is a valid creature and is a player character
if (GetIsObjectValid(oPC) && GetObjectType(oPC) == OBJECT_TYPE_CREATURE && GetIsPC(oPC))
{
// Start the periodic script to apply the stench penalty
ApplyStenchPenalty(oPC);
}
DelayCommand(90.0f, ExecuteScript("stench_onenter"));
}

View File

@@ -0,0 +1,72 @@
//::///////////////////////////////////////////////
//:: Footcatcher Trap
//:: trap_footcatch
//:: Copyright (c) 2023 Project RATDOG
//:://////////////////////////////////////////////
/*
*/
//:://////////////////////////////////////////////
#include "prc_inc_spells"
#include "NW_I0_SPELLS"
void CreateObject2(int nObjectType, string sTemplate, location lLocation, int bUseAppearAnimation=FALSE);
void main()
{
//Declare major variables
int nDuration = 4;
int nDamage = d3(1);
object oTarget = GetEnteringObject();
location lTarget = GetLocation(oTarget);
effect eStopped = EffectCutsceneImmobilize();
eStopped = ExtraordinaryEffect(eStopped);
effect eDamage = EffectDamage(nDamage);
effect eVis1 = EffectVisualEffect(VFX_DUR_ENTANGLE);
effect eVis2 = EffectVisualEffect(VFX_IMP_POISON_S);
effect ePoison = EffectPoison(POISON_LARGE_SCORPION_VENOM);
ePoison = ExtraordinaryEffect(ePoison);
effect eLink1 = EffectLinkEffects(eVis1, eDamage);
eLink1 = ExtraordinaryEffect(eLink1);
//Find first target in the size
oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget);
//Cycle through the objects in the radius
while (GetIsObjectValid(oTarget))
{
if(!MySavingThrow(SAVING_THROW_REFLEX, oTarget, 20, SAVING_THROW_TYPE_NONE))
{
//:: Apply damage & hold effect
ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink1, oTarget);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eStopped, oTarget, RoundsToSeconds(nDuration));
SendMessageToPC(oTarget, "You've stepped thru a false floor, into a spiked trap.");
if(!MySavingThrow(SAVING_THROW_FORT, oTarget, 18, SAVING_THROW_TYPE_POISON))
{
//:: Apply poison
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis2, oTarget);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, ePoison, oTarget, RoundsToSeconds(nDuration));
}
}
//Get next target in the shape.
oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_SMALL, lTarget);
}
//respawn trap
DelayCommand(598.0, CreateObject2(GetObjectType(OBJECT_SELF), GetResRef(OBJECT_SELF), GetLocation(OBJECT_SELF)));
DelayCommand(600.0, DestroyObject(OBJECT_SELF));
}
void CreateObject2(int nObjectType, string sTemplate, location lLocation, int bUseAppearAnimation=FALSE)
{
CreateObject(nObjectType, sTemplate, lLocation, bUseAppearAnimation);
return;
}

View File

@@ -0,0 +1,174 @@
//::////////////////////////////////////////////////////////////////////////////
//::
// underwater_hb.nss
/*
Handles being underwater.
*/
//
//::
//::////////////////////////////////////////////////////////////////////////////
#include "prc_inc_spells"
#include "x0_i0_match"
//:: Checks if a creature is Aquatic for underwater related scripting
int GetIsAquatic(object oCreature)
{
//:: Check if the provided object is valid
if (!GetIsObjectValid(oCreature))
{
return FALSE;
}
//:: Get the subrace field of the creature or player, set to lowercase
string sSubrace = GetStringLowerCase(GetSubRace(oCreature));
if (FindSubString(sSubrace, "aquatic") != -1)
{
return TRUE;
}
//:: Check for aquatic or amphibious appearance
int nAppearance = GetAppearanceType(oCreature);
switch(nAppearance)
{
case APPEARANCE_TYPE_SHARK_GOBLIN:
case APPEARANCE_TYPE_SHARK_HAMMERHEAD:
case APPEARANCE_TYPE_SHARK_MAKO:
case APPEARANCE_TYPE_MEPHIT_OOZE:
case APPEARANCE_TYPE_MEPHIT_WATER:
case APPEARANCE_TYPE_SEA_HAG:
//:: CEP appearances follow
case 872: //:: Leviathan
case 990: //:: Troll, Scrag
case 1427: //:: Crab: Blue: Giant* (Eligio Sacateca)
case 1428: //:: Crab: Blue: Small* (Eligio Sacateca)
case 1429: //:: Crab: Red: Giant 1* (Eligio Sacateca)
case 1430: //:: Crab: Red: Small 1* (Eligio Sacateca)
case 3053: //:: Bullywug: Brown-Green* (baba yaga)
case 3054: //:: Bullywug: Green* (baba yaga)
case 3055: //:: Bullywug: Brown* (baba yaga)
case 3538: //:: Fish: Pike* (JFK)
case 3539: //:: Fish: Pike, Giant* (JFK)
case 3889: //:: Hag: Sea 2* (Hardpoints)
return TRUE;
}
//:: If you made it this far, return false.
return FALSE;
}
//:: Can't breathe in water
void ApplySuffocationEffect(object oCreature)
{
int iConstitution = GetAbilityScore(oCreature, ABILITY_CONSTITUTION);
int iBreathRounds = iConstitution * 2;
int iSuffocationDC = 10; // Starting suffocation DC
int iCurrentRound = GetLocalInt(oCreature, "SuffocationRound");
if (iCurrentRound <= iBreathRounds)
{
if (GetResRef(OBJECT_SELF) != "mepooze001" &&
GetResRef(OBJECT_SELF) != "tarmephit001" &&
!GetHasFeat(FEAT_WATER_BREATHING, oCreature) &&
!GetHasFeat(FEAT_BREATHLESS, oCreature) &&
!GetHasSpellEffect(SPELL_WATER_BREATHING, oCreature) &&
(MyPRCGetRacialType(oCreature) != RACIAL_TYPE_UNDEAD) &&
(MyPRCGetRacialType(oCreature) != RACIAL_TYPE_ELEMENTAL) &&
(MyPRCGetRacialType(oCreature) != RACIAL_TYPE_CONSTRUCT))
{
SendMessageToPC(oCreature, "You are struggling to breathe in the water!");
if (d20() + GetFortitudeSavingThrow(oCreature) >= iSuffocationDC)
{
SendMessageToPC(oCreature, "You manage to hold your breath and avoid suffocation for now.");
}
else
{
SendMessageToPC(oCreature, "You are drowning!");
if (iCurrentRound == 1)
{
AssignCommand(oCreature, ActionPlayAnimation(ANIMATION_LOOPING_SPASM, 0.5f, 6.0f));
SetCurrentHitPoints(oCreature, 1);
}
else if (iCurrentRound == 2)
{
SetCurrentHitPoints(oCreature, 0);
}
else if (iCurrentRound == 3)
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oCreature);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDeath(), oCreature);
}
iCurrentRound++;
}
}
// Store the updated round counter back in the creature's local variables
SetLocalInt(oCreature, "SuffocationRound", iCurrentRound);
}
}
//:: Water slows non-aquatic to 1/2 speed
//:: unless they are under the effect of Freedom of Movement
void ApplySlowEffect(object oCreature)
{
if (GetIsAquatic(oCreature) == FALSE &&
GetResRef(OBJECT_SELF) != "mepooze001" &&
GetResRef(OBJECT_SELF) != "tarmephit001" &&
!GetHasSpellEffect(SPELL_FREEDOM_OF_MOVEMENT, oCreature) ||
GetHasEffect(IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE, oCreature))
{
// Object has either Freedom of Movement spell effect or IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE effect
SendMessageToPC(GetFirstPC(), "This object has Freedom of Movement or immunity to movement speed decrease!");
}
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectMovementSpeedDecrease(50), oCreature, 6.0f);
}
void main()
{
object oArea = GetArea(OBJECT_SELF);
object oCreature = GetFirstObjectInArea(oArea, OBJECT_TYPE_CREATURE);
string sResref = GetResRef(oCreature);
while (GetIsObjectValid(oCreature))
{
//:: Calculate the armor penalty
int iArmorPenalty = 0;
object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oCreature);
if (GetIsObjectValid(oArmor))
{
iArmorPenalty = GetItemACValue(oArmor);
}
//:: Calculate the DC for the Strength check
int iStrengthDC = 15 + iArmorPenalty;
//:: Be an Ooze Mephit or roll a Strength check against the DC
if (GetIsAquatic(oCreature) == FALSE &&
GetResRef(OBJECT_SELF) != "mepooze001" &&
GetResRef(OBJECT_SELF) != "tarmephit001" ||
GetAppearanceType(oCreature) != APPEARANCE_TYPE_MEPHIT_OOZE ||
d20() + GetAbilityModifier(ABILITY_STRENGTH, oCreature) < iStrengthDC)
{
//:: Apply the other effects since the Strength check failed
SendMessageToPC(oCreature, "You are floundering in the water!");
ApplySuffocationEffect(oCreature);
}
//:: Tar is always hot & slowing
ApplySlowEffect(oCreature);
oCreature = GetNextObjectInArea(oArea, OBJECT_TYPE_CREATURE);
}
}