Invisig0th wrote...
Further research: FunkySwerve posted a script in this thread which might make a good starting point: http://nwn.bioware.c...433566&forum=63
I'll also be looking at the scripts used in Oraweb's U1-2-3 series
modules, although they almost certainly won't include all the SOU and
HOTU spells.
Those are ancient. Acaos redid them, and the lag from drawn-out drowning was a non-issue. Here are some of our current 'environment' scripts, which aren't limited to underwater, courtesy of both acaos and myself:
fky_environ_inc
#include "hg_inc"
#include "subrace_inc"
const int ENVIRONMENT_TYPE_WATER = 1;
const int ENVIRONMENT_TYPE_AIR = 2;
const int ENVIRONMENT_TYPE_EARTH = 3;
const int ENVIRONMENT_TYPE_FIRE = 4;
int GetCanLevitate (object oPC) {
if (GetIsDM(oPC) ||
GetPlotFlag(oPC) ||
GetIsEncounterCreature(oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_SANDWALK, oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_TREEWALK, oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_LEVITATION, oPC))
return TRUE;
switch (GetLocalInt(oPC, "SubraceID")) {
case SUBRACE_ATOMIE:
case SUBRACE_AVARIEL:
case SUBRACE_DRIDER:
case SUBRACE_ERINYES:
case SUBRACE_FALLEN_ANGEL:
case SUBRACE_GENIE:
case SUBRACE_HALF_CELESTIAL:
case SUBRACE_HALF_FIEND:
case SUBRACE_PHARLAN:
case SUBRACE_PIXIE:
case SUBRACE_PLANEWALKER:
case SUBRACE_PT_AIR_GENASI:
case SUBRACE_STINGER:
case SUBRACE_THRIKREEN:
return 2;
}
object oRRing = GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC);
object oLRing = GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC);
object oAmulet = GetItemInSlot(INVENTORY_SLOT_NECK, oPC);
object oBoots = GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC);
object oBelt = GetItemInSlot(INVENTORY_SLOT_BELT, oPC);
object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC);
if (GetLocalInt(oRRing, "Levitate") ||
GetLocalInt(oLRing, "Levitate") ||
GetLocalInt(oAmulet, "Levitate") ||
GetLocalInt(oBoots, "Levitate") ||
GetLocalInt(oBelt, "Levitate") ||
GetLocalInt(oArmor, "Levitate"))
return TRUE;
return FALSE;
}
int GetCanBreatheWater (object oPC, int nBubbles=FALSE) {
int nReturn = FALSE;
if (GetIsDM(oPC) ||
GetPlotFlag(oPC) ||
GetIsEncounterCreature(oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_BREATHE_WATER, oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_BREATHE_VACUUM, oPC))
nReturn = TRUE;
int nRace = GetRacialType(oPC);
if (nRace == RACIAL_TYPE_UNDEAD ||
nRace == RACIAL_TYPE_CONSTRUCT ||
GetLevelByclass(class_TYPE_UNDEAD, oPC) > 0 ||
GetLevelByclass(class_TYPE_CONSTRUCT, oPC) > 0) {
nReturn = 2;
nBubbles = FALSE;
}
switch (GetLocalInt(oPC, "SubraceID")) {
case SUBRACE_JUGGERNAUT:
nBubbles = FALSE;
case SUBRACE_ELF_AQUATIC:
case SUBRACE_HALFELF_NEREIDKIN:
case SUBRACE_LIZARDFOLK:
case SUBRACE_PLANEWALKER:
case SUBRACE_PT_WATER_GENASI:
case SUBRACE_SIREN:
nReturn = 2;
break;
}
string sTag = GetTag(oPC);
if (sTag == "NW_WATER" || sTag == "NW_WATERHUGE" || sTag == "NW_WATERGREAT" || sTag == "NW_WATELDER") {
nReturn = 2;
nBubbles = FALSE;
}
/* liches and demiliches don't drown */
int nApp = GetAppearanceType(oPC);
if (nApp == APPEARANCE_TYPE_LICH || nApp == 1769) {
nReturn = 2;
nBubbles = FALSE;
}
if (!nReturn) {
object oRRing = GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC);
object oLRing = GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC);
object oAmulet = GetItemInSlot(INVENTORY_SLOT_NECK, oPC);
object oHelmet = GetItemInSlot(INVENTORY_SLOT_HEAD, oPC);
object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oPC);
if (GetLocalInt(oRRing, "Breath") ||
GetLocalInt(oLRing, "Breath") ||
GetLocalInt(oAmulet, "Breath") ||
GetLocalInt(oHelmet, "Breath") ||
GetLocalInt(oShield, "Breath") ||
GetLocalInt(oArmor, "Breath"))
nReturn = TRUE;
}
if (nReturn && nBubbles) {
if (!GetHasSpellEffect(HGEFFECT_UNDERWATER_BUBBLES, oPC)) {
effect eBubbles = EffectVisualEffect(HGVFX_DUR_BUBBLES_SILENT);
eBubbles = SupernaturalEffect(eBubbles);
SetEffectSpellId(eBubbles, HGEFFECT_UNDERWATER_BUBBLES);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eBubbles, oPC);
}
}
return nReturn;
}
int GetCanPasswall (object oPC) {
if (GetIsDM(oPC) ||
GetPlotFlag(oPC) ||
GetIsEncounterCreature(oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_PASSWALL, oPC))
return TRUE;
switch (GetLocalInt(oPC, "SubraceID")) {
case SUBRACE_PLANEWALKER:
case SUBRACE_PT_EARTH_GENASI:
return 2;
}
return FALSE;
}
int GetCanFirewalk (object oPC) {
if (GetIsDM(oPC) ||
GetPlotFlag(oPC) ||
GetIsEncounterCreature(oPC) ||
GetHasSpellImmunity(HGEFFECT_SURVIVAL_FIREWALK, oPC))
return TRUE;
switch (GetLocalInt(oPC, "SubraceID")) {
case SUBRACE_DWARF_AZER:
case SUBRACE_GNOME_FIRE:
case SUBRACE_PLANEWALKER:
case SUBRACE_PT_FIRE_GENASI:
case SUBRACE_SALAMANDER:
return 2;
}
return FALSE;
}
int GetCanEnterEnvironment (int nEnv, object oPC) {
switch (nEnv) {
case ENVIRONMENT_TYPE_WATER: return GetCanBreatheWater(oPC, FALSE);
case ENVIRONMENT_TYPE_AIR: return GetCanLevitate(oPC);
case ENVIRONMENT_TYPE_EARTH: return GetCanPasswall(oPC);
case ENVIRONMENT_TYPE_FIRE: return GetCanFirewalk(oPC);
}
return TRUE;
}
====================================================================
hg_area_enter
#include "hg_inc"
#include "hg_area_inc"
#include "hg_antiex_inc"
#include "fky_environ_inc"
#include "ac_qstatus_inc"
void DoAreaEffects (object oArea, object oPC, int nDamAmount, int nDamType, int nDamVis, string sMessage) {
if (GetArea(oPC) != oArea)
return;
if (!GetIsDead(oPC) && !GetPlotFlag(oPC)) {
if (nDamType < 0) {
if (nDamType == -1) {
int nBreath = GetCanBreatheWater(oPC, TRUE);
if (nBreath == 2)
return;
if (!nBreath) {
if (GetLocalInt(oPC, "Area_WaterBreath_Warning") < 3) {
FloatingTextStringOnCreature("You cannot hold your breath much longer!", oPC, FALSE);
AddLocalInt(oPC, "Area_WaterBreath_Warning", 1);
} else {
FloatingTextStringOnCreature("You can no longer hold your breath!", oPC, FALSE);
DeleteLocalInt(oPC, "Area_WaterBreath_Warning");
ApplyEffectToObject(DURATION_TYPE_INSTANT, SupernaturalEffect(EffectDeath()), oPC);
}
} else
DeleteLocalInt(oPC, "Area_WaterBreath_Warning");
} else if (nDamType == -2) {
int nLev = GetCanLevitate(oPC);
if (nLev == 2)
return;
if (!nLev) {
if (!GetIsResting(oPC))
FloatingTextStringOnCreature("You are unable to control your motion in the air!", oPC, FALSE);
RemoveEffectsOfType(EFFECT_TYPE_CUTSCENEIMMOBILIZE, oPC, oArea);
effect eEff = SupernaturalEffect(EffectCutsceneImmobilize());
DelayCommand(0.01, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oPC, 9.0));
}
} else if (nDamType == -3) {
/* passwall; do nothing */
return;
} else if (nDamType == -4) {
int nFirewalk = GetCanFirewalk(oPC);
if (nFirewalk == 2)
return;
if (!nFirewalk) {
FloatingTextStringOnCreature("You are unable to breathe elemental fire!", oPC, FALSE);
ApplyDirectDamage(oPC, (GetMaxHitPoints(oPC) * 2) / 5, DAMAGE_TYPE_INTERNAL,
DAMAGE_POWER_ENERGY, "Elemental Fire", "You burn to a crisp!");
ApplyVisualToObject(VFX_IMP_FLAME_M, oPC);
}
}
} else {
effect eDam = EffectDamage(d10(nDamAmount), nDamType);
effect eVis = EffectVisualEffect(nDamVis);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oPC);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC);
if (sMessage != "")
FloatingTextStringOnCreature(sMessage, oPC, FALSE);
}
}
DelayCommand(6.0, DoAreaEffects(oArea, oPC, nDamAmount, nDamType, nDamVis, sMessage));
}
void DoDust (object oPC, object oItem) {
SetPlotFlag(oItem, FALSE);
DestroyObject(oItem);
CreateItemOnObject("drowdust", oPC);
}
object GetVFXLoopTarget (object oSource, string sTarget) {
int nCount = 1;
struct SubString ss = GetFirstSubString(sTarget, "#");
if (ss.rest != "") {
}
return GetNearestObjectByTag(ss.first, oSource, nCount);
}
float GetVFXLoopVaryingFloat (float fRand) {
int nVary = FloatToInt(fRand * 100.0);
nVary = Random((nVary * 2) + 1) - nVary;
return (nVary * 0.01);
}
void VoidBroadcastProjectileToObject (object oSource, object oTarget, int nSpellId, int nDelay) {
BroadcastProjectileToObject(oSource, oTarget, nSpellId, nDelay);
}
void DoVFXLoop (object oVFX, int nVis, float fCyc, float fDur, string sTarget, int bBeam,
float fCycRand, float fDurRand, int nMulti, float fMultiInt, float fMultiRand) {
if (GetLocalInt(OBJECT_SELF, "Area_Clear")) {
AddLocalInt(OBJECT_SELF, "Area_VFX_Active", -1);
return;
}
if (nMulti > 1) {
/* ugly to have a second copy, but removes unnecessary delaycommands */
int i;
float fDelay = 0.0;
for (i = 0; i < nMulti; i++) {
float fDurVary = GetVFXLoopVaryingFloat(fDurRand);
if (sTarget != "") {
object oTarget = GetVFXLoopTarget(oVFX, sTarget);
if (bBeam) {
effect eBeam = EffectBeam(nVis, oVFX, BODY_NODE_CHEST, (bBeam == 2));
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, fDur + fDurVary));
} else if (nVis < 0) {
DelayCommand(fDelay, VoidBroadcastProjectileToObject(oVFX, oTarget, -nVis, FloatToInt((fDur + fDurVary) * 1000.0)));
} else if (fDur > 0.0) {
effect eVis = EffectVisualEffect(nVis);
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget, fDur + fDurVary));
} else {
DelayCommand(fDelay, ApplyVisualToObject(nVis, oTarget));
}
} else {
if (fDur > 0.0) {
effect eVis = EffectVisualEffect(nVis);
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oVFX, fDur + fDurVary));
} else {
DelayCommand(fDelay, ApplyVisualToObject(nVis, oVFX));
}
}
fDelay += fMultiInt + GetVFXLoopVaryingFloat(fMultiRand);
}
} else {
float fDurVary = GetVFXLoopVaryingFloat(fDurRand);
if (sTarget != "") {
object oTarget = GetVFXLoopTarget(oVFX, sTarget);
if (bBeam) {
effect eBeam = EffectBeam(nVis, oVFX, BODY_NODE_CHEST, (bBeam == 2));
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, fDur + fDurVary);
} else if (nVis < 0) {
BroadcastProjectileToObject(oVFX, oTarget, -nVis, FloatToInt((fDur + fDurVary) * 1000.0));
} else if (fDur > 0.0) {
effect eVis = EffectVisualEffect(nVis);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget, fDur + fDurVary);
} else {
ApplyVisualToObject(nVis, oTarget);
}
} else {
if (fDur > 0.0) {
effect eVis = EffectVisualEffect(nVis);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oVFX, fDur + fDurVary);
} else {
ApplyVisualToObject(nVis, oVFX);
}
}
}
float fCycVary = GetVFXLoopVaryingFloat(fCycRand);
DelayCommand(fCyc + fCycVary, DoVFXLoop(oVFX, nVis, fCyc, fDur, sTarget, bBeam, fCycRand, fDurRand, nMulti, fMultiInt, fMultiRand));
}
void main () {
object oPC = GetEnteringObject();
object oArea = GetArea(oPC);
if (GetIsPC(oPC)) {
DeleteLocalInt(oArea, "Area_Clear");
int bReveal = GetLocalInt(oArea, "Area_Reveal");
if (bReveal > 0)
ExploreAreaForPlayer(oArea, oPC, TRUE);
}
if (GetLocalString(oPC, "FKY_CHAT_PASSWORD_IP") == "CHECK")
AssignCommand(GenCreator(), ApplyPasswordHold(oPC));
/* execute specific onenter script if specified */
string sScript = GetLocalString(oArea, "Area_OnEnter");
if (sScript != "")
ExecuteScript(sScript, oArea);
/* PC-only stuff below here */
if (!GetIsPC(oPC))
return;
if (GetLocalInt(oArea, "Area_VFX_Objects") > 0 &&
GetLocalInt(oArea, "Area_VFX_Active") == 0) {
int i, nObjects = GetLocalInt(oArea, "Area_VFX_Objects");
object oVFX;
for (i = 0; i < nObjects; i++) {
oVFX = GetLocalObject(oArea, "Area_VFX_Object_" + IntToString(i));
if (GetIsObjectValid(oVFX)) {
int nVis = GetLocalInt(oVFX, "VFX_Loop");
int nMulti = GetLocalInt(oVFX, "VFX_Loop_Multi");
int bBeam = GetLocalInt(oVFX, "VFX_Loop_Beam");
float fCyc = GetLocalFloat(oVFX, "VFX_Loop_Cycle");
float fCycRand = GetLocalFloat(oVFX, "VFX_Loop_Cycle_Vary");
float fDur = GetLocalFloat(oVFX, "VFX_Loop_Dur");
float fDurRand = GetLocalFloat(oVFX, "VFX_Loop_Dur_Vary");
float fMultiInt = GetLocalFloat(oVFX, "VFX_Loop_Multi_Int");
float fMultiRand = GetLocalFloat(oVFX, "VFX_Loop_Multi_Int_Vary");
string sTarget = GetLocalString(oVFX, "VFX_Loop_Target");
AddLocalInt(oArea, "Area_VFX_Active", 1);
AssignCommand(oArea, DelayCommand(fCyc, DoVFXLoop(oVFX, nVis, fCyc, fDur, sTarget, bBeam, fCycRand, fDurRand, nMulti, fMultiInt, fMultiRand)));
}
}
}
/* if the PC is not allowed in the area, boot them and return */
if (!CheckAreaTagRequirement(oArea, oPC))
return;
string sQuest = GetLocalString(oArea, "Area_Quest");
if (sQuest != "")
QSAddQuestStatus(sQuest, 1);
/* for Hell and Limbo areas, set the Succor_ local */
if (GetLocalInt(oArea, "notele") > 0)
SetLocalInt(oPC, "Succor_" + GetResRef(oArea), 1);
/* area damage/underwater areas */
/* TODO: enhance this to be more generic, e.g. Hells penalties */
if (!GetIsDM(oPC)) {
int nDamType = GetLocalInt(oArea, "Area_Damage_Type");
if (GetLocalInt(oArea, "Area_Underwater"))
nDamType = -1;
else if (GetLocalInt(oArea, "Area_Aerial"))
nDamType = -2;
else if (GetLocalInt(oArea, "Area_Passwall"))
nDamType = -3;
else if (GetLocalInt(oArea, "Area_Firewalk"))
nDamType = -4;
if (nDamType) {
int nDamAmount = GetLocalInt(oArea, "Area_Damage_Amount");
int nDamVis = GetLocalInt(oArea, "Area_Damage_VFX");
string sMessage = GetLocalString(oArea, "Area_Damage_Message");
AssignCommand(oArea, DoAreaEffects(oArea, oPC, nDamAmount, nDamType, nDamVis, sMessage));
}
/* destroy items that cannot survive in daylight */
if (GetIsDay() &&
!GetIsAreaInterior(oArea) &&
GetIsAreaAboveGround(oArea)) {
int i;
object oItem;
for (i = 0; i < NUM_INVENTORY_SLOTS; i++) {
oItem = GetItemInSlot(i, oPC);
if (GetStringLeft(GetTag(oItem), 4) == "asdf")
DoDust(oPC, oItem);
}
while (GetIsObjectValid(oItem = GetItemPossessedBy(oPC, "sealofshadows")))
DestroyObject(oItem);
}
}
/* spawn anything specified in area locals */
int nSpawns = GetLocalInt(oArea, "Area_Spawns");
if (nSpawns > 0) {
int i, nType;
string sKey, sTag, sRes, sLoc, sLocType;
object oSpawn;
location lSpawn;
for (i = 1; i <= nSpawns; i++) {
sKey = "Area_Spawn_" + IntToString(i);
sTag = GetLocalString(oArea, sKey + "_Tag");
/* if the object has already been spawned, skip it */
if (sTag != "") {
if (GetIsObjectValid(GetNearestObjectByTag(sTag, oPC)))
continue;
} else {
if (GetIsObjectValid(GetLocalObject(oArea, sKey + "_Obj")))
continue;
}
sRes = GetLocalString(oArea, sKey + "_Res");
sLoc = GetLocalString(oArea, sKey + "_Loc");
nType = GetLocalInt(oArea, sKey + "_Type");
if (nType == 0)
nType = OBJECT_TYPE_CREATURE;
sLocType = GetStringLeft(sLoc, 1);
if (sLocType == "!")
lSpawn = GetLocation(GetNearestObjectByTag(GetSubString(sLoc, 1, 64), oPC));
else if (sLocType == "@")
lSpawn = GetLocation(GetWaypointByTag(GetSubString(sLoc, 1, 64)));
else
continue;
if (GetAreaFromLocation(lSpawn) == oArea) {
oSpawn = CreateObject(nType, sRes, lSpawn, FALSE, sTag);
SetLocalObject(oArea, sKey + "_Obj", oSpawn);
}
}
}
/* respawn loot or other other respawnables */
nSpawns = GetLocalInt(oArea, "Area_Respawns");
if (nSpawns > 0) {
int i, nType, nRespawnTime, nUptime = GetLocalInt(GetModule(), "uptime");
string sKey, sVar;
object oSpawn;
for (i = 1; i <= nSpawns; i++) {
sKey = IntToString(i);
nRespawnTime = GetLocalInt(oArea, "Area_Respawn_" + sKey);
if (nRespawnTime <= 0 ||
nRespawnTime > nUptime ||
GetIsObjectValid(GetLocalObject(oArea, "Area_Respawn_Obj_" + sKey)))
continue;
oSpawn = CreateObject(GetLocalInt(oArea, "Area_Respawn_Type_" + sKey),
GetLocalString(oArea, "Area_Respawn_Res_" + sKey),
GetLocalLocation(oArea, "Area_Respawn_Loc_" + sKey));
SetLocalObject(oArea, "Area_Respawn_Obj_" + sKey, oSpawn);
if ((sVar = GetLocalString(oArea, "Area_Respawn_Var_" + sKey)) != "")
AssignCommand(oArea, DelayCommand(0.0, RestoreLocals(oSpawn, sVar)));
}
}
/* clear despawn setting on area */
if (GetLocalInt(oArea, "DespawnTrack")) {
int nDespawnTime = GetLocalInt(oArea, "DespawnTime");
if (nDespawnTime) {
int nUptime = GetLocalInt(GetModule(), "uptime");
if ((nUptime - 3600) > nDespawnTime) {
DeleteLocalInt(oArea, "DespawnTime");
DeleteLocalInt(oArea, "DespawnCount");
}
}
}
}