Aller au contenu

Photo

Underwater script systems


  • Veuillez vous connecter pour répondre
37 réponses à ce sujet

#1
Invisig0th

Invisig0th
  • Members
  • 170 messages
I'm interested in compiling/borrowing/revising a robust set of scripts for underwater NWN adventuring which implement something approaching 3.0/3.5 rules. The main things I'm looking to address here are underwater spell casting and underwater weapon attack/damage penalties based on weapon type.

GOG's Underwater system from NWVault looks to be a good start regarding the basic implementation of drowning and the special items which prevent drowning, but it doesn't appear to address spellcasting at all. Olander's system may or may not address other issues -- the way it is packaged and documented makes this very difficult to determine.

I suspect that there are several full featured underwater script systems around, particularly on some of the more popular persistent worlds. There was a thread on the old Bioware boards about a system used by the Silver Marches PW, but their current build module on NWVault unfortunately doesn't seem to contain the underwater demo area mentioned.

I'm not asking for scripting 101 tips here -- I'm well aware how all the things I have mentioned can be done with NWN scripting, and what spellhooking is for. Rather, what I am looking for is any existing content or systems that folks have found useful, so that I am not forced to start from scratch when I put this system together. I'm sure I'll need to tweak any set of scripts due to various differences in requirements, but any system that at least prevents fireballs and other spells from being used underwater would get me a long way toward what i need here. And as far as I can see, nothing like that has been posted to NWVault.

Also, any tips from builders who have tried to script similar underwater PC effects would be very helpful, particularly regarding other things that I might want to alter for underwater areas. For example, the discussion of Silver Marches PW mentions the benefits of the using the Silent feat, something I had not considered.

Thanks in advance for any assistance.

Modifié par Invisig0th, 02 mai 2011 - 10:28 .


#2
Invisig0th

Invisig0th
  • Members
  • 170 messages
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.

Modifié par Invisig0th, 03 mai 2011 - 02:33 .


#3
Thayan

Thayan
  • Members
  • 244 messages
I too did some extensive searching back in teh day and there really wasn't a very robust Underwater system on the Vault (that I found anyway), so I used the scripts in the following post as a start for the Underwater system in Thay.
http://nwn.bioware.c...611153&forum=47

It also has a spellhook script in there which could be merged into your own if you already have one. You'll also want to consider things like Alchemist Fire and Fire Bombs too.

#4
TSMDude

TSMDude
  • Members
  • 865 messages

Invisig0th wrote...

I'm interested in compiling/borrowing/revising a robust set of scripts for underwater NWN adventuring which implement something approaching 3.0/3.5 rules. The main things I'm looking to address here are underwater spell casting and underwater weapon attack/damage penalties based on weapon type.

GOG's Underwater system from NWVault looks to be a good start regarding the basic implementation of drowning and the special items which prevent drowning, but it doesn't appear to address spellcasting at all. Olander's system may or may not address other issues -- the way it is packaged and documented makes this very difficult to determine.

I suspect that there are several full featured underwater script systems around, particularly on some of the more popular persistent worlds. There was a thread on the old Bioware boards about a system used by the Silver Marches PW, but their current build module on NWVault unfortunately doesn't seem to contain the underwater demo area mentioned.

I'm not asking for scripting 101 tips here -- I'm well aware how all the things I have mentioned can be done with NWN scripting, and what spellhooking is for. Rather, what I am looking for is any existing content or systems that folks have found useful, so that I am not forced to start from scratch when I put this system together. I'm sure I'll need to tweak any set of scripts due to various differences in requirements, but any system that at least prevents fireballs and other spells from being used underwater would get me a long way toward what i need here. And as far as I can see, nothing like that has been posted to NWVault.

Also, any tips from builders who have tried to script similar underwater PC effects would be very helpful, particularly regarding other things that I might want to alter for underwater areas. For example, the discussion of Silver Marches PW mentions the benefits of the using the Silent feat, something I had not considered.

Thanks in advance for any assistance.

Hey Invis, come by the module on rp and I will show you how it works plus send a email to our builder email at

tsm.builder@gmail.com

And I will erf out a mod for you with the proper code and what not.

#5
Invisig0th

Invisig0th
  • Members
  • 170 messages
E-mail sent.Thanks very much!

#6
TSMDude

TSMDude
  • Members
  • 865 messages

Invisig0th wrote...

E-mail sent.Thanks very much!


Will have to send it tomorrow as my wife is home on Weds and apprently we spend familiy time then...dang family getting in the way of my hobby! *shakes fist in mock anger*

#7
Invisig0th

Invisig0th
  • Members
  • 170 messages
No rush! Thank you!

#8
Invisig0th

Invisig0th
  • Members
  • 170 messages
Well, it's been several weeks and no word. I guess something must have come up. I hope to hear from TMSDude in the near future regarding the ERF that he offered to provide.

In the meantime, I'm a month into this and I still have no scripts to use as a starting point. Has anyone else out there dealt with scripting underwater rules? There have to be at least a few people in the NWN community who have been down this road in the past.

#9
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

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");
            }
        }
    }
}


  • Thayan aime ceci

#10
cmwise

cmwise
  • Members
  • 34 messages
I've used gog's underwater scripts. worked fine in my own PW..seems somewhat customizable. May not be what you're looking for though.

http://nwvault.ign.c....Detail&id=3685

#11
cmwise

cmwise
  • Members
  • 34 messages
I've used Gog's underwater scripts in my own PW. Somewhat customizable but maybe not robust enough for your purpose.

http://nwvault.ign.c....Detail&id=3685

#12
Invisig0th

Invisig0th
  • Members
  • 170 messages
Thank you, FunkySwerve. I'll need to take some time to go through all of that, and will contact you if I have any questions.

Like I said above, GOG's Underwater system from NWVault looks to be a good start regarding the basic implementation of drowning and the special items which prevent drowning, but it doesn't appear to address spellcasting at all. I need to implement something MUCH more complex. However, I will be reviewing all GOG's scripts in detail, as I will be doing with all the other systems, to see if there is anything I can use here.

Perhaps I should have done a better job of explaining exactlyw hat I am doing.  I am converting a set of three 2E TSR PNP modules  to NWN, and most of the action (particularly in the last module) occurs underwater. When complete, my system will include drowning rules, swimming, weapon restrictions, extensive spell changes, custom underwater items and spells, and lots and lots of other rule changes and underwater effects. I will also be implementing custom animations, footsteps, and combat sound effects.

Most modules (including POTSC) only have one or a few underwater areas, so they usually only need to implement some basic underwater rules to give a little bit of flavor. Since PCs will be spending so much time underwater in my modules, I'm going to need to do a lot more work to make things work properly and stay interesting. When this is all complete, it may very well be the most complicated underwater system for NWN to date (and certainly will be overkill compared to the needs of the typical module builder).

This is why I am reaching out to the folks in the community who have implemented their own complex underwater system for their own needs. I would like to learn about what they have done in the context of the NWN engine and how they have implemented it. Most of the individual things I am talking about here are actually pretty straightforward, and have no doubt already been done by somebody somewhere for their own modules/servers. So it makes sense to start by looking at existing scripts, combining them as needed, and then adding in all the missing stuff and custom stuff afterwards.

Thanks to all who have offered assistance or suggestions. I very much appreciate it.

Modifié par Invisig0th, 03 juin 2011 - 01:20 .


#13
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Whups, got distracted and forgot the spellhook. Ours is pretty simple, really, without too much focus on altering individual spells. Here it is:

#include "hg_inc"
#include "x2_inc_switches"

void main () {
    /* Underwater effects on spells, by FunkySwerve */
    int nSpellId = GetSpellId();
    object oParty = GetFirstFactionMember(OBJECT_SELF, TRUE);

    switch (nSpellId) {
        case SPELL_BALL_LIGHTNING:
        case SPELL_CALL_LIGHTNING:
        case SPELL_CHAIN_LIGHTNING:
        case SPELL_ELECTRIC_JOLT:
        case SPELL_GEDLEES_ELECTRIC_LOOP:
        case SPELL_LIGHTNING_BOLT:
        case SPELL_SCINTILLATING_SPHERE:
        case HGSPELL_GREATER_ORB_OF_ELECTRICITY:
        case HGSPELL_LESSER_ORB_OF_ELECTRICITY:
        case HGSPELL_ORB_OF_ELECTRICITY: {
            /* electrical spells damage everyone in the casters party in the same
             * area, for 6 points dmg /casterlevel, and then the spell casts normally */

            int nDamage;
            float fDist, fDamage;
            effect eEffect, eVis;
            object oArea = GetArea(OBJECT_SELF);

            int nAmount = GetCasterLevel(OBJECT_SELF) * 6;

            while (GetIsObjectValid(oParty)) {
                if (GetArea(oParty) == oArea) {
                    fDist = GetDistanceBetween(OBJECT_SELF, oParty);
                    fDamage = (IntToFloat(nAmount) - fDist);
                    nDamage = FloatToInt(fDamage);
                    eEffect = EffectDamage(nDamage, DAMAGE_TYPE_ELECTRICAL);
                    fDist = fDist / 100;
                    DelayCommand(fDist, ApplyEffectToObject(DURATION_TYPE_INSTANT, eEffect, oParty));
                    DelayCommand(fDist + 0.1, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oParty));
                }

                oParty = GetNextFactionMember(OBJECT_SELF, TRUE);
            }

            FloatingTextStringOnCreature("The electricity arcs through the water, damaging your party.", OBJECT_SELF);
            break;
        }

        case SPELL_BURNING_HANDS:
        case SPELL_COMBUST:
        case SPELL_CONTINUAL_FLAME:
        case SPELL_DARKFIRE:
        case SPELL_DELAYED_BLAST_FIREBALL:
        case SPELL_ELEMENTAL_SHIELD:
        case SPELL_FIRE_STORM:
        case SPELL_FIREBALL:
        case SPELL_FIREBRAND:
        case SPELL_FLAME_ARROW:
        case SPELL_FLAME_LASH:
        case SPELL_FLAME_STRIKE:
        case SPELL_FLAME_WEAPON:
        case SPELL_FLARE:
        case SPELL_INCENDIARY_CLOUD:
        case SPELL_INFERNO:
        case SPELL_SHADES_FIREBALL:
        case HGSPELL_FIREBURST:
        case HGSPELL_GREATER_ORB_OF_FIRE:
        case HGSPELL_LESSER_ORB_OF_FIRE:
        case HGSPELL_ORB_OF_FIRE:
            FloatingTextStringOnCreature("The water quenches the flames of your spell.", OBJECT_SELF, FALSE);
            SetModuleOverrideSpellScriptFinished();
            break;

        case SPELL_DROWN:
        case SPELL_GUST_OF_WIND:
        case HGSPELL_DESERT_SIROCCO:
            FloatingTextStringOnCreature("This spell doesn't work underwater.", OBJECT_SELF, FALSE);
            SetModuleOverrideSpellScriptFinished();
            break;
    }
}

Some spells and abilities are handled in the individual scripts. Our Wall of Fire spell, for example, has a different effect above level 20, so is handled in the spellscript proper. Other scripts to check include shifter scripts (dragon breath, for example), and summons (fire elementals, and presumably water elementals), which are handled separately for various reasons, some of which may or may not apply to your situation :

hgs_gen_shifter (98):         if (GetLocalString(GetArea(si.caster), "Area_SpellHook") == "zwund_spellhook") {
hgs_gen_shifter (149):         if (GetLocalString(GetArea(si.caster), "Area_SpellHook") == "zwund_spellhook") {
hgs_gen_shifter (588):     if (GetLocalString(GetArea(si.caster), "Area_SpellHook") == "zwund_spellhook") {
nw_s0_newsummon (103):     if (GetStringLeft(GetTag(GetArea(si.caster)), 5) == "zwund")
nw_s0_wallfire (59):     if (si.clevel < 20 && GetLocalString(oArea, "Area_SpellHook") == "zwund_spellhook") {
x2_s2_discbreath (110):         GetLocalString(GetArea(OBJECT_SELF), "Area_SpellHook") == "zwund_spellhook") {

Area-specific spellhooks like this underwater one are checked for at the end of the spellhook proper, with this bit of code:

    string sScript = GetLocalString(oArea, "Area_SpellHook");
    if (sScript != "")
        ExecuteScript(sScript, oCaster);

They follow checks for dead magic zones, amnesia, artifacts, an autocaster, and the like.

Funky

Modifié par FunkySwerve, 03 juin 2011 - 03:22 .


#14
Invisig0th

Invisig0th
  • Members
  • 170 messages
Awesome -- thanks.

If anyone simply has player/DM documentation regarding how the rules do and do not change in underwater areas in their module, that would also be very helpful to me as I decide exactly which special underwater rules will add the most value (and fun) to the module. Lists of the NWN spells which don't work or have altered effects would be particularly helpful, since some of the NWN spells deviate significantly from the PNP versions.

Also, any thoughts specifically about the differences between the PNP underwater rules and a computer game would be useful here as I compile these scripts. For example, I've already determined that there probably is no benefit to requiring NWN players to apply "varnish" to every one of their spell scrolls to prevent them from being destroyed underwater. In PNP this adds a touch of 'realism' while causing no serious inconvenience. (The player simply says to DM "I apply the varnish I bought to all my scrolls". Done. ) However, in the context of a computer game it would be a colossal pain for spellcasters to have to use a unique power to dozens or hundreds of scrolls, while adding no real value to the overall gaming experience. So this is an example of something that just doesn't translate well to a computer game like NWN, and I'm sure I'll come across more things like this.

Modifié par Invisig0th, 03 juin 2011 - 04:39 .


#15
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
We skimped on a lot of the movement and weapons rules, as they were either too difficult to implement correctly, or weren't of much significance in the larger picture (basically, things where the development time / benefit ratio was too high). If your setting doesn't have prevalent Freedom of Movement effects, you may well want to consider movement rate modifications. I added the rules for electrical spells myself, as they just seemed like common sense.

From the 3.5 DMG1 p 95:

-ranged weapons are at -2 to hit per 5 feet (blocking use altogether might be more practical, as a gid square in NWN is 10 meters, or -12 to hit, making attacks against well-matched enemies nigh pointless) : skipped this, as ranged combat in our mod is already underpowered (purely a balance, rather than a RP/immersion concern, though I imagine hacking the engine would be required to implement properly, as well)
-creatures in water get cover when attacked from land (+8 ac, +4 reflex, if at least chest deep): we skipped this because pretty much all of our areas are either all underwater or all above water (in betweens are possible, with extensive triggers to demarcate depth zones, but a real pain); note that the rules say that the surface also blocks 'line of effect' for fire spells - good luck making that happen
-fire spells fizzle unless the caster makes a dc 20 plus spell level spellcraft check, if passed, spell creates a bubble of hot steam, functioning with described effects (skipped because this check doesn't scale well for us (though we could have modified it), and more importantly, we lacked the vfx to do anything close to this)

Swimming movement rules rom the 3.5 Rules Compendium p 90:
-move at 1/4 speed (there's more, for full-round moves, that doesn't translate well at all to NWN, especially given the lack of a Swim skill in vanilla): we skipped this, because of the prevalence of FoM on HG, and because this would likely get old VERY fast)

Additional rules rom the 3.5 Rules Compendium p 149:

-creatures with no natural swim speed in chest deep or higher water are at -2 to hit and deal half damage, unless weilding a piercing weapon, freedom of movement allows you to ignore this (again skipped because of FoM's prevalence on HG)
-swimming vs firm footing, and failed swim checks: we disregarded this, as there are few options for making pcs appear to be swimming, aside from switching to swimming (flying) phenos, which aren't available for all of our subrace appearances (nevermind NPCs); instead, we assume all characters are weighted down enough to have firm footing (almost always true, in any event)

Funky

#16
ShadowM

ShadowM
  • Members
  • 768 messages
Very interesting stuff. I am going to start work on my underwater system (from scratch, well might take some of the functions funky gave. Thanks for them :) ) for HR pretty soon and this thread has given me some interesting things to think over and work out. I see some things I could work out and like funky says some just do not work well. I do have the swim skill in and I can think of some nice uses for it in my system.

#17
Invisig0th

Invisig0th
  • Members
  • 170 messages
Yep, those are the biggest items in the 3.5 underwater rules, which are unfortunately little more than an afterthought by WOTC. Even with just those few paragraphs, I will probably have to implement a lot of various small things like disabling thrown weapons and alchemist's fire.

As a bit of an aside, there are a lot of other sources for 3.0 and 3.5 underwater rules -- Stormwrack, classic Play Book of the Sea, The Deep, Seafarer's Handbook, Dead Man's Chest and Seas of Blood being the main ones (and I have read all of them at this point). I also have to consider 2E Monstrous Arcana: The Sea Devils, since that book details the main antagonists for this module series. Between all of these sources, there is a lot of very detailed coverage of all the above subjects and various other rules, some of which just make sense once you think of them. One example of these is the need to use Silent Magic feat to cast spells underwater. It sounds like TSMDude has done exactly that on his server, and I will probably include that in my modules as well. Assuming DM/PCs are notified up front about this and then given an opportunity to revise their spells early on to account for this, it seems to me like a reasonable restriction which adds value by stressing the uniqueness and danger of the underwater setting. (Since one major NPC and likely henchman is a water elementalist (Fathomer), there is ample opportunity to educate the PCs about exactly which things are different in the underwater environment. Personally, I think requiring some special preparations before venturing underwater would actually make it more fun for the players.)

There are also various other things that are not technically "rules", but which are part of the underwater setting and would add depth. For instance,I'll be implementing clouds of blood which appear when a PC is injured in combat, and the blood can attract sharks. (This is good example of something that can actually work better in a computer game than for PNP, because the clues are visual.) These sort of things will be much more important for my modules than they would be for a standard PW where underwater areas are rare. So I will no doubt go a bit deeper into all this than most folks have in the past.

However, getting a basic and intuitive scripted underwater rule framework in place is necessary before moving on to these things.

Modifié par Invisig0th, 03 juin 2011 - 10:31 .


#18
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
I'm familiar with the other 3.5 sources, but they're generally setting- or area-specific. Stormwrack, for example, gives rules for pressure and cold damage, and vision (p 11). Those we handle via our enviroment scripts, but damages are going to be highly relative to the module and its power level. Vision issues are generally best solved with fog levels, though I could see maybe throwing in some conceal in extreme cases (as well as area skill mods, perhaps). Probably the most generalizable of Stormwrack's rules are a finer look at currents, but I didn't bother going into any of that, since currents are basically impossible to do correctly and unexploitably in an area where combat can happen (forcing movement requires clearing actions, which causes huge problems when trying to fight, cue actions/spells, etc). In short, I'm not aware of any other 3.5 sources readily implementable into NWN.

We considered blocking spells with verbal components underwater, but didn't - initially because of the amount of work involved, and later, because it didn't jive particularly well with the waterbreathing that was a general requirement for our underwater areas. Should you want to do something of the kind, you'll want to use OldManWhistlers spell functions, which basically set up a number of hashes storing all kinds of info that bioware's spell scripts don't provide access to without 2da reads, indexing on spell id:

Spell Functions
by OldManWhistler

This one script provides fast access to a lot of the information stored in
SPELLS.2DA without having to use slow Get2DAString lookups.

Instead it uses local variables for storing the data at module load time so that
it can be accessed quickly at run time. It uses packed integers to store the
following spell information with only four local variables per spell:
- spell name
- spell script
- hostile setting
- feat or spell
- innate spell level
- innate spell level by class
- range
- metamagic types
- spell school
- projectile type
- immunity type
- target type
- vocal component
- somatic component
- breachable (added by Acaos)

Here are some snippets from the initialization:

void SFInitSpellNames (object oSelf) {
    HashSetSetLocalString(oSelf, "SF", "SFNames_0",     "Acid Fog");
    HashSetSetLocalString(oSelf, "SF", "SFNames_1",     "Aid");
    HashSetSetLocalString(oSelf, "SF", "SFNames_2",     "Animate Dead");
    HashSetSetLocalString(oSelf, "SF", "SFNames_3",     "Barkskin");
    HashSetSetLocalString(oSelf, "SF", "SFNames_4",     "Bestow Curse");
    HashSetSetLocalString(oSelf, "SF", "SFNames_5",     "Blade Barrier");
    HashSetSetLocalString(oSelf, "SF", "SFNames_6",     "Bless");
...

void SFInitSpellPack1 (object oSelf) {
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_0",    0x1cbe23f2); // Acid Fog
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_1",    0x0cab0ff4); // Aid
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_2",    0x0cac0bf7); // Animate Dead
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_3",    0x0cab0ff8); // Barkskin
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_4",    0x1caa0ff8); // Bestow Curse
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_5",    0x1cae87f2); // Blade Barrier
    HashSetSetLocalInt(oSelf, "SF", "SFPacked1_6",    0x0caf83f4); // Bless
...

void SFInitSpellPack2 (object oSelf) {
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_0",    0x07000007); // Acid Fog
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_1",    0x00430303); // Aid
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_2",    0x06000404); // Animate Dead
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_3",    0x00303003); // Barkskin
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_4",    0x05000444); // Bestow Curse
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_5",    0x00000707); // Blade Barrier
    HashSetSetLocalInt(oSelf, "SF", "SFPacked2_6",    0x00020202); // Bless
...

void SFInitSpellPack3 (object oSelf) {
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_0",    0x0fff0800); // Acid Fog
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_1",    0x0fff0004); // Aid
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_2",    0x0fff0000); // Animate Dead
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_3",    0x0fff0000); // Barkskin
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_4",    0x0fff0801); // Bestow Curse
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_5",    0x0fff0000); // Blade Barrier
    HashSetSetLocalInt(oSelf, "SF", "SFPacked3_6",    0x0fff0004); // Bless
...

void SFInitSpellPack4 (object oSelf) {
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_0",   0x00100401);  //    1    1    1
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_1",   0x00200802);  //    2    2    2
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_2",   0x00400005);  //    4    0    5
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_3",   0x00500c08);  //    5    3    8
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_4",   0x00001009);  //    0    4    9
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_5",   0x0000000b);  //    0    0   11
    HashSetSetLocalInt(oSelf, "SF", "SFPacked4_6",   0x0090000c);  //    9    0   12
 ...

Those values are manipulated with shifts and masks like these:


// What is the range of the spell? (How far away can it be cast?)
const int SPELLFUNC_RANGE_LARGE        = 0;
const int SPELLFUNC_RANGE_MEDIUM       = 1;
const int SPELLFUNC_RANGE_SMALL        = 2;
const int SPELLFUNC_RANGE_TOUCH        = 3;
const int SPELLFUNC_RANGE_PERSONAL     = 4; // Self-only
const int SPELLFUNC_RANGE_FOCUS        = 5; // focus-based

// What can the spell be targeted against? Might be of use to some AI scripting.
const int SPELLFUNC_TARGET_SELF        = 0x00000001;
const int SPELLFUNC_TARGET_CREATURE    = 0x00000002;
const int SPELLFUNC_TARGET_GROUND      = 0x00000004;
const int SPELLFUNC_TARGET_ITEM        = 0x00000008;
const int SPELLFUNC_TARGET_DOOR        = 0x00000010;
const int SPELLFUNC_TARGET_PLACEABLE   = 0x00000020;
const int SPELLFUNC_TARGET_TRIGGER     = 0x00000040;

// Does the spell require spoken words or gestures to cast?
const int SPELLFUNC_LOREBOOST          = 0x40000000;
const int SPELLFUNC_BREACHABLE         = 0x20000000;
const int SPELLFUNC_HOSTILE            = 0x10000000;
const int SPELLFUNC_VERBAL             = 0x08000000;
const int SPELLFUNC_SOMATIC            = 0x04000000;

const int SPELLFUNC_FEAT_MASK          = 0x00000001;
const int SPELLFUNC_SCHOOL_MASK        = 0x0000000f;
const int SPELLFUNC_SCHOOL_SHIFT       = 0x00000000;
const int SPELLFUNC_METAMAGIC_MASK     = 0x0000003f;
const int SPELLFUNC_METAMAGIC_SHIFT    = 0x00000004;
const int SPELLFUNC_RANGE_MASK         = 0x00000007;
const int SPELLFUNC_RANGE_SHIFT        = 0x0000000a;
const int SPELLFUNC_PROJECTILE_MASK    = 0x00000007;
const int SPELLFUNC_PROJECTILE_SHIFT   = 0x0000000d;
const int SPELLFUNC_TARGET_MASK        = 0x0000007f;
const int SPELLFUNC_TARGET_SHIFT       = 0x00000010;
const int SPELLFUNC_TYPE_MASK          = 0x00000007;
const int SPELLFUNC_TYPE_SHIFT         = 0x00000017;

const int SPELLFUNC_INNATE_SHIFT       = 0x00000000;
const int SPELLFUNC_INNATE_MASK        = 0x0000000f;
const int SPELLFUNC_INNATE_BD_SHIFT    = 0x00000004;
const int SPELLFUNC_INNATE_BD_MASK     = 0x0000000f;
const int SPELLFUNC_INNATE_CL_SHIFT    = 0x00000008;
const int SPELLFUNC_INNATE_CL_MASK     = 0x0000000f;
const int SPELLFUNC_INNATE_DR_SHIFT    = 0x0000000c;
const int SPELLFUNC_INNATE_DR_MASK     = 0x0000000f;
const int SPELLFUNC_INNATE_PA_SHIFT    = 0x00000010;
const int SPELLFUNC_INNATE_PA_MASK     = 0x0000000f;
const int SPELLFUNC_INNATE_RA_SHIFT    = 0x00000014;
const int SPELLFUNC_INNATE_RA_MASK     = 0x0000000f;
const int SPELLFUNC_INNATE_WZ_SHIFT    = 0x00000018;
const int SPELLFUNC_INNATE_WZ_MASK     = 0x0000000f;

const int SPELLFUNC_IDTOCAST_MASK      = 0x000003ff;
const int SPELLFUNC_IDTOCAST_SHIFT     = 0x00000000;
const int SPELLFUNC_IDTOIMM_MASK       = 0x000003ff;
const int SPELLFUNC_IDTOIMM_SHIFT      = 0x0000000a;
const int SPELLFUNC_IMMTOID_MASK       = 0x000003ff;
const int SPELLFUNC_IMMTOID_SHIFT      = 0x00000014;

This provides us with functions like SFGetIsSpellVerbal:


int SFGetIsSpellVerbal (int nSpell) {
    return !!(HashSetGetLocalInt(GetModule(), "SF", "SFPacked1_" + IntToString(nSpell)) & SPELLFUNC_VERBAL);
}

Providing a relatively easy way to block verbal-component spells underwater, should you wish to do that, as opposed to simply blocking all spells. Nowadays, with the new 2da caching, you might well just use a 2da read, if the only thing you want to do is check for a verbal component, and you aren't worried about using too many 2da reads elsewhere (default is to store 10 in the cache, if memory serves):

Funky

Modifié par FunkySwerve, 04 juin 2011 - 01:32 .


#19
Karvon

Karvon
  • Members
  • 243 messages
I would certainly like to see an updated and more fleshed out UW script set including more of the factors from PNP rules. Have used some of the various systems mentioned in the past, but never really been completely satisfied with the results. Perhaps some elements could be configurable so folks could more easily tailor the implementation to suit their tastes and situation.

Regards

Karvon

#20
Karvon

Karvon
  • Members
  • 243 messages
Also, The Grimore spell hak added UW limits and effects for spells, if someone wanted to delve into that to see what and how they implemented stuff.

Regards

Karvon

#21
Invisig0th

Invisig0th
  • Members
  • 170 messages
Interesting - thanks. I'll check it out.

And yes, configuration options would be extremely important for a system like this. As a programmer by day, that is something I always plan for. The Silent Spell thing mentioned above is a great example of an optional thing that a builder might want to disable, and making it easy to do that is important.

#22
Artistmonk

Artistmonk
  • Members
  • 30 messages
I just saw this, will have questions when I get home, if that's okay with Funky.

#23
Baaleos

Baaleos
  • Members
  • 1 322 messages
@ Funky

Hey Funky,
I see an interesting function up above.

ApplyDirectDamage(oPC, (GetMaxHitPoints(oPC) * 2) / 5, DAMAGE_TYPE_INTERNAL,
DAMAGE_POWER_ENERGY, "Elemental Fire", "You burn to a crisp!");


What does this do?

It looks interesting, I was wondering does it do any custom messages or such via nwnx or is it just plain simple SendMessageToPC?

I like the Constant INT values you have added for it, I might try to make something similar to that, it would be good to have something like this,

#24
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Baaleos wrote...

@ Funky

Hey Funky,
I see an interesting function up above.

ApplyDirectDamage(oPC, (GetMaxHitPoints(oPC) * 2) / 5, DAMAGE_TYPE_INTERNAL,
DAMAGE_POWER_ENERGY, "Elemental Fire", "You burn to a crisp!");


What does this do?

It looks interesting, I was wondering does it do any custom messages or such via nwnx or is it just plain simple SendMessageToPC?

I like the Constant INT values you have added for it, I might try to make something similar to that, it would be good to have something like this,


It's a wrapper function from our central include, hg_inc. We use it to apply custom damage types via script:

void ApplyDirectDamage (object oTarget, int nDamage, int nDamType, int nDamPower=DAMAGE_POWER_NORMAL, string sName="", string sDeathMessage="") {
    int nDamImm = GetTrueDamageImmunity(oTarget, nDamType);
    int nDamRes = GetEffectDamageResistance(oTarget, nDamType);
    object oArea = GetArea(oTarget);

    if (nDamImm >= 100 || !GetIsObjectValid(oArea) || GetIsDead(oTarget) || GetPlotFlag(oTarget))
        return;
    else if (nDamImm < -100)
        nDamImm = -100;

    nDamage -= (nDamage * nDamImm) / 100;
    nDamage -= nDamRes;

    if (nDamage < 0)
        nDamage = 0;

    if (sName == "")
        sName = GetName(OBJECT_SELF);

    if (GetIsPC(OBJECT_SELF) || GetIsPC(GetMaster(OBJECT_SELF))) {
        string sMessage;
        object oMessage;

        sMessage = sName + C_DAMAGE_PHYSICAL + " damages " +
            GetName(oTarget) + ": " + IntToString(nDamage) + " (" + GetDamageTypeColor(nDamType) +
            IntToString(nDamage) + " " + GetDamageTypeName(nDamType) + C_DAMAGE_PHYSICAL + ")";

        SendMessageToPC(OBJECT_SELF, C_DAMAGE_SELF + sMessage);

        sMessage = C_DAMAGE_OTHER + sMessage;

        for (oMessage = GetFirstFactionMember(OBJECT_SELF);
             GetIsObjectValid(oMessage);
             oMessage = GetNextFactionMember(OBJECT_SELF)) {

            if (oMessage != OBJECT_SELF && GetIsPC(oMessage) && GetArea(oMessage) == oArea)
                SendMessageToPC(oMessage, sMessage);
        }
    }

    if ((GetIsPC(oTarget) || GetIsPC(GetMaster(oTarget))) && !GetFactionEqual(oTarget, OBJECT_SELF)) {
        string sMessage;
        object oMessage;

        sMessage = C_DAMAGE_OTHER + sName + C_DAMAGE_PHYSICAL + " damages " +
            GetName(oTarget) + ": " + IntToString(nDamage) + " (" + GetDamageTypeColor(nDamType) +
            IntToString(nDamage) + " " + GetDamageTypeName(nDamType) + C_DAMAGE_PHYSICAL + ")";

        for (oMessage = GetFirstFactionMember(oTarget);
             GetIsObjectValid(oMessage);
             oMessage = GetNextFactionMember(oTarget)) {

            if (GetIsPC(oMessage) && GetArea(oMessage) == oArea)
                SendMessageToPC(oMessage, sMessage);
        }
    }

    if (nDamage <= 0)
        return;

    effect eTemp;
    for (eTemp = GetFirstEffect(oTarget);
         GetIsEffectValid(eTemp);
         eTemp = GetNextEffect(oTarget)) {

        if (GetEffectType(eTemp) == EFFECT_TYPE_TEMPORARY_HITPOINTS) {
            int nTemp = GetEffectInteger(eTemp, 0);

            if (nDamage >= nTemp) {
                RemoveEffect(oTarget, eTemp);
                nDamage -= nTemp;

                if (nDamage == 0)
                    return;
            } else {
                object oCreator = GetEffectCreator(eTemp);
                int nDurType = GetEffectDurationType(eTemp);
                float fDur = (nDurType == DURATION_TYPE_TEMPORARY ? GetEffectDurationRemaining(eTemp) : 0.0);

                if (!GetIsObjectValid(oCreator))
                    oCreator = oTarget;

                SetEffectInteger(eTemp, 0, nTemp - nDamage);
                RemoveEffect(oTarget, eTemp);

                AssignCommand(oCreator, DelayCommand(0.01, ApplyEffectToObject(nDurType, eTemp, oTarget, fDur)));
                return;
            }
        }
    }

    if (ModifyCurrentHitPoints(oTarget, -nDamage) <= 1) {
        if (sDeathMessage != "")
            FloatingTextStringOnCreature(sDeathMessage, oTarget, FALSE);

        effect eDeath = SupernaturalEffect(EffectDeath());
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eDeath, oTarget);

        SetLocalInt(oTarget, "DirectDamageDeath", 1);
    } else
        DeleteLocalInt(oTarget, "DirectDamageDeath");
}

We added ectoplasmic, psionic, sacred, vile, and internal damage types, along wtih gear properties for immunity and resistance and the like. Here's some sample uses:

abo_eldest_ebt (56):             DelayCommand(fDelay + 0.1, ApplyDirectDamage(oTarget, nDamage, DAMAGE_TYPE_VILE, DAMAGE_POWER_ENERGY));
abo_endcombat (182):         DelayCommand(fDelay, ApplyDirectDamage(oTarget, nDamage, DAMAGE_TYPE_PSIONIC, DAMAGE_POWER_ENERGY));
abo_endcombat (1201):                         DelayCommand(fDelay, ApplyDirectDamage(oTarget, d20(12), DAMAGE_TYPE_ECTOPLASMIC,
aby_endcombat (653):         DelayCommand(fDelay, ApplyDirectDamage(oTarget, nDamage, DAMAGE_TYPE_PSIONIC, DAMAGE_POWER_ENERGY));
aby_endcombat (2851):                     DelayCommand(fDelay + 0.1, ApplyDirectDamage(oTarget, nResult, DAMAGE_TYPE_INTERNAL,
aby_endcombat (2867):                     DelayCommand(0.1, ApplyDirectDamage(oTarget, d100(2 + nParagon), DAMAGE_TYPE_INTERNAL,
aby_endcombat (3783):                                     DelayCommand(fDelay, ApplyDirectDamage(oTarget, d20(5 + nParagon), DAMAGE_TYPE_VILE,
aby_endcombat (4312):                             DelayCommand(fDelay + 0.1, ApplyDirectDamage(oTarget, nResult, DAMAGE_TYPE_VILE, DAMAGE_POWER_ENERGY,
aby_endcombat (4532):                                 DelayCommand(fDelay + 0.1, ApplyDirectDamage(oTarget, nResult, DAMAGE_TYPE_VILE,
aby_endcombat (5071):                                     DelayCommand(fDelay, ApplyDirectDamage(oTarget, d20(8 + nParagon), DAMAGE_TYPE_VILE,


And, since you're probably going to ask anway...yes, it requires engine hacks. Nwnx_structs to alter effect intergers to drop temp hit point effects the proper amount, for one (as well as to reapply the effect at the properly reduced duration). ModifyCurrentHitPoints is done with...nwnx_funcs, iirc, and just checks current hit point level more cleanly than Get. Nwnx_defenses for GetTrueDamageImmunity, which fixes issues with immunity stacking, and accounts for additional RDD-style complete immunity feats (ditto GetEffectDamageResistance). I don't believe it ends there, either, though I could be mistaken. This is all acaos' code, persuant to his custom hacks for us.

Funky

#25
TSMDude

TSMDude
  • Members
  • 865 messages
Ours was mentioned and nowhere near as clean as Funkys I will wager but here is our old one as the new one is dependant on haks and more ingrained systems we use.



//This function is called from the onenter event of an underwater area
//
//Set a local int on the underwater creatures that will be spawned in the underwater
//area. If you do not the creatures will also drown.
//    Name: Aquatic
//    Type: int
//    Value: 1
// -----------------------------------------------------------------------------
// CONSTANTS
// -----------------------------------------------------------------------------
const float BREATH_CHECK = 6.0;                            // << Breath check delay in seconds
const string WATER_BREATHING_ITEM = "waterb_item";           // << Tag of water breathing items
const string WATER_SWIMMING_ITEM = "swim_item";            // << Tag of water swimming items
// -----------------------------------------------------------------------------
// PROTOTYPES
// -----------------------------------------------------------------------------
// Check player is wearing an underwater breathing item
int WearingWaterItemCheck(object oPC);
//Do the underwater check
void UnderWaterCheck(object oPC);
// -----------------------------------------------------------------------------
// FUNCTIONS
// -----------------------------------------------------------------------------
int WearingWaterBreathingItem(object oPC)
{
   if(GetTag(GetItemInSlot(INVENTORY_SLOT_ARMS, oPC))       == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_BELT, oPC))       == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC))      == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC))    == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC))      == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC))      == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_HEAD, oPC))       == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC))   == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_NECK, oPC))       == WATER_BREATHING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC)) == WATER_BREATHING_ITEM ||
      GetLocalInt(oPC, "WBPOTION"))
      return TRUE;
      return FALSE;
}
int WearingWaterSwimmingItem(object oPC)
{
   if(GetTag(GetItemInSlot(INVENTORY_SLOT_ARMS, oPC))       == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_BELT, oPC))       == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_BOOTS, oPC))      == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC))    == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_CHEST, oPC))      == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_CLOAK, oPC))      == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_HEAD, oPC))       == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_LEFTRING, oPC))   == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_NECK, oPC))       == WATER_SWIMMING_ITEM ||
      GetTag(GetItemInSlot(INVENTORY_SLOT_RIGHTRING, oPC)) == WATER_SWIMMING_ITEM ||
      GetLocalInt(oPC, "WSPOTION"))
      return TRUE;
      return FALSE;
}
void UnderWaterCheck(object oPC)
{
int iCON = GetAbilityModifier(ABILITY_CONSTITUTION,oPC);    // << Constitution Modifier of oPC
int iCONSave = d20(1) + iCON;                               // << CON Save if failed to hold breath
int iHOLD = 2 + (iCON*2);                                   // << Let them hold their breath in seconds
 
if(GetLocalInt(oPC, "UNDERWATER") == 0) return;
 
if(WearingWaterBreathingItem(oPC) == FALSE)
    {
    if(WearingWaterSwimmingItem(oPC) == FALSE)
        {
        // Reduce the entering objects movement speed and mark them as being under water
        ApplyEffectToObject(2, EffectMovementSpeedDecrease(75), oPC, 0.0);
        }
    int iROUND = GetLocalInt(oPC,"ROUND");                  // << Determines number of rounds
    if (iROUND <= iHOLD)
        {
        iROUND=iROUND+1;
        SetLocalInt(oPC,"ROUND",iROUND);
        SendMessageToPC(oPC,"** You are holding your breath **");
 
        // DEBUG
        if (GetLocalInt(GetModule(),"Debug")==1)
            {
            string sROUND = IntToString(iROUND);
            SendMessageToPC(oPC,"DEBUG: This is round number "+sROUND);
            }
        }
    if (iROUND > iHOLD)
        {
        int iDC = GetLocalInt(oPC,"DC");                    // << DC to keep holding breath
        if (iCONSave>=iDC)
            {
            iDC=iDC+1;
            SetLocalInt(oPC,"DC",iDC);
            SendMessageToPC(oPC,"** You are barely holding your breath **");
            FloatingTextStringOnCreature("** blub blub **", oPC);
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_DUR_BUBBLES), oPC);
 
            // DEBUG
            if (GetLocalInt(GetModule(),"Debug")==1)
                {
                string sDC = IntToString(iDC);
                SendMessageToPC(oPC,"DEBUG: The DC is now "+sDC);
                }
            }
        if (iCONSave < iDC)
            {
            int iHPs=GetCurrentHitPoints(oPC);
            int iDROWN=GetLocalInt(oPC,"DROWN");
            FloatingTextStringOnCreature("** Drowning: blub blub **", oPC, FALSE);
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_DUR_BUBBLES), oPC);
            if(iDROWN == 2)
                {
                effect eDamage=EffectDamage(iHPs,DAMAGE_TYPE_DIVINE,DAMAGE_POWER_PLUS_TWENTY);
                //effect eDamage=EffectDamage(10,DAMAGE_TYPE_DIVINE,DAMAGE_POWER_PLUS_TWENTY);
                ApplyEffectToObject(DURATION_TYPE_INSTANT,eDamage,oPC,0.0);
                SetLocalInt(oPC,"DROWN",0);
                }
            if(iDROWN == 1)
                {
                effect eDamage=EffectDamage(iHPs/2,DAMAGE_TYPE_DIVINE,DAMAGE_POWER_PLUS_TWENTY);
                //effect eDamage=EffectDamage(1,DAMAGE_TYPE_DIVINE,DAMAGE_POWER_PLUS_TWENTY);
                ApplyEffectToObject(DURATION_TYPE_INSTANT,eDamage,oPC,0.0);
                SetLocalInt(oPC,"DROWN",2);
                }
            if(iDROWN == 0)
                {
                effect eDamage=EffectDamage(iHPs/4,DAMAGE_TYPE_DIVINE,DAMAGE_POWER_PLUS_TWENTY);
                //effect eDamage=EffectDamage(iHPs,DAMAGE_TYPE_DIVINE,DAMAGE_POWER_PLUS_TWENTY);
                ApplyEffectToObject(DURATION_TYPE_INSTANT,eDamage,oPC,0.0);
                SetLocalInt(oPC,"DROWN",1);
                }
            }
        iROUND=iROUND+1;
        SetLocalInt(oPC,"ROUND",iROUND);
        }
    }
DelayCommand(BREATH_CHECK, UnderWaterCheck(oPC));
}
void Water_Enter(object oPC)
{
// Skip creatures set to breathe underwater
if(GetLocalInt(oPC, "Aquatic") == TRUE) return;
// Skip DM's
if(GetIsDM(oPC) || GetIsDMPossessed(oPC)) return;
// Skip Undead, Constructs & Elementals
if(GetRacialType(oPC) == RACIAL_TYPE_UNDEAD ||
 GetRacialType(oPC) == RACIAL_TYPE_CONSTRUCT ||
 GetRacialType(oPC) == RACIAL_TYPE_ELEMENTAL) return;
// Torch goes out underwater
object oItem = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
if(GetBaseItemType(oItem) == BASE_ITEM_TORCH)
    {
    DelayCommand(1.0, FloatingTextStringOnCreature("Your torch fizzles out.", oPC));
    DelayCommand(1.0, AssignCommand(oPC, ActionUnequipItem(oItem)));
    }
if(WearingWaterSwimmingItem(oPC) == FALSE)
    {
    // Reduce the entering objects movement speed and mark them as being under water
    ApplyEffectToObject(2, EffectMovementSpeedDecrease(75), oPC, 0.0);
    }
// Set up drowning control Ints
SetLocalInt(oPC, "UNDERWATER", 1);
SetLocalInt(oPC,"ROUND",0);
SetLocalInt(oPC,"DC",10);
SetLocalInt(oPC,"DROWN",0);
DelayCommand(0.1, FloatingTextStringOnCreature("** This water is deep! **", oPC));
// Start checking for holding of breath and damage
DelayCommand(0.5, UnderWaterCheck(oPC));
}
 
 
 
 
 
 
 
 
 
 
 
 
//This function is called from the onexit event of an underwater area
 
void Water_Exit(object oPC)
{
// Loop through effects on PC and remove movement rate decreases
effect eSlow = GetFirstEffect(oPC);
while(GetIsEffectValid(eSlow))
    {
    if(GetEffectType(eSlow) == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE)
        {
        RemoveEffect(oPC, eSlow);
        }
    eSlow = GetNextEffect(oPC);
    }
// Mark the PC as not being under water now
SetLocalInt(oPC, "UNDERWATER", 0);
// The following will reset all counters, DCs, etc in case they fall in the trap again
// Set the Drowning Round to zero
SetLocalInt(oPC, "ROUND", 0);
// Reset the DC of holding their breath
SetLocalInt(oPC, "DC", 0);
// Reset the Drown counter
SetLocalInt(oPC, "DROWN", 0);
}
 
 
 
 
 
 
/* Add this line after the Void Main () in your OnEquipItem script of your module
   ExecuteScript("notorch_water", OBJECT_SELF);
so torches cannot be used Underwater

*/
 
void main()
{
 object oItem = GetPCItemLastEquipped();
 object oUser = GetPCItemLastEquippedBy();
 
 // Torches go out underwater
 if (GetLocalInt(GetArea(oUser), "AREA_UNDERWATER") && GetBaseItemType(oItem) == BASE_ITEM_TORCH)
 {
    DelayCommand(1.0, FloatingTextStringOnCreature("Your torch fizzles out.", oUser));
    DelayCommand(1.0, AssignCommand(oUser, ActionUnequipItem(oItem)));
 }
}
 
 
 
 
 
 
//Create a item that has the same tag as this script and give it a unique power self only (single use)
#include "x2_inc_switches"
const float fBreathTime = 480.0;//<--how long the potion will last
void main()
{
    int nEvent = GetUserDefinedItemEventNumber();    //Which event triggered this
    object oPC;
    object oItem;
    object oSpellOrigin;
    object oSpellTarget;
    location lSpellLocation;
    int iSpell;
    int nRand;
    int nResult = X2_EXECUTE_SCRIPT_END;
    effect eEffect;
 
   switch (nEvent)
    {
        /*
        case X2_ITEM_EVENT_ONHITCAST:
            // * This code runs when the item has the 'OnHitCastSpell: Unique power' property
            // * and it hits a target(if it is a weapon) or is being hit (if it is a piece of armor)
            // * Note that this event fires for non PC creatures as well.
 
            oItem = GetSpellCastItem();           // The item triggering this spellscript
            oPC = OBJECT_SELF;                      // The player triggering it
            oSpellOrigin = OBJECT_SELF ;            // Where the spell came from
            oSpellTarget = GetSpellTargetObject(); // What the spell is aimed at
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
        */
        case X2_ITEM_EVENT_ACTIVATE:
            // * This code runs when the Unique Power property of the item is used
            // * Note that this event fires for PCs only
 
            oPC   = GetItemActivator();             // The player who activated the item
            oItem = GetItemActivated();             // The item that was activated
            lSpellLocation = GetItemActivatedTargetLocation();// The target creature
 
            SetLocalInt(oPC, "WBPOTION",TRUE);
 
            if(GetLocalInt(oPC, "UNDERWATER") == 1)
                {
                SendMessageToPC(oPC,"** You begin to breath easily under water **");
                FloatingTextStringOnCreature("** blub blub **", oPC);
                }
 
            SetLocalInt(oPC,"ROUND",0);
            SetLocalInt(oPC,"DC",10);
            SetLocalInt(oPC,"DROWN",0);
 
            DelayCommand(fBreathTime-10,SendMessageToPC(oPC,"Your breathing potion is about to wear off."));
            DelayCommand(fBreathTime,AssignCommand(oPC, DeleteLocalInt(oPC, "WBPOTION")));
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        case X2_ITEM_EVENT_EQUIP:
            // * This code runs when the item is equipped
            // * Note that this event fires PCs only
 
            oPC = GetPCItemLastEquippedBy();        // The player who equipped the item
            oItem = GetPCItemLastEquipped();        // The item that was equipped
 
            if(GetLocalInt(oPC, "UNDERWATER") == 1)
                {
                SendMessageToPC(oPC,"** You begin to breath easily under water **");
                FloatingTextStringOnCreature("** blub blub **", oPC);
                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_DUR_BUBBLES), oPC);
                }
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        case X2_ITEM_EVENT_UNEQUIP:
            // * This code runs when the item is unequipped
            // * Note that this event fires PCs only
 
            oPC    = GetPCItemLastUnequippedBy();   // The player who unequipped the item
            oItem = GetPCItemLastUnequipped();     // The item that was unequipped
 
            if(GetLocalInt(oPC, "UNDERWATER") == 1)
                {
                // Set up drowning control Ints
                SetLocalInt(oPC,"ROUND",0);
                SetLocalInt(oPC,"DC",10);
                SetLocalInt(oPC,"DROWN",0);
                }
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        /*
        case X2_ITEM_EVENT_ACQUIRE:
            // * This code runs when the item is acquired
            // * Note that this event fires PCs only
 
            oPC = GetModuleItemAcquiredBy();        // The player who acquired the item
            oItem = GetModuleItemAcquired();       // The item that was acquired
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        case X2_ITEM_EVENT_UNACQUIRE:
 
            // * This code runs when the item is unacquired
            // * Note that this event fires PCs only
 
            oPC = GetModuleItemLostBy();            // The player who dropped the item
            oItem = GetModuleItemLost();           // The item that was dropped
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
       case X2_ITEM_EVENT_SPELLCAST_AT:
            //* This code runs when a PC or DM casts a spell from one of the
            //* standard spellbooks on the item
 
            oPC = OBJECT_SELF;                      // The player who cast the spell
            oItem = GetSpellTargetObject();        // The item targeted by the spell
            iSpell = GetSpellId();                  // The id of the spell that was cast
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the spell that was
            //cast on the item from taking effect
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
        */
    }
    // * X2_EXECUTE_SCRIPT_CONTINUE - continue calling script after executed script is done
    // * X2_EXECUTE_SCRIPT_END - end calling script after executed script is done
   SetExecutedScriptReturnValue(nResult);
}
 
 
 
 
 
 
 
 
//Create a item that has the same tag as this script and give it a unique power self only (single use)
#include "x2_inc_switches"
const float fSwimTime = 480.0;//<--how long the potion will last
void main()
{
    int nEvent = GetUserDefinedItemEventNumber();    //Which event triggered this
    object oPC;
    object oItem;
    object oSpellOrigin;
    object oSpellTarget;
    location lSpellLocation;
    int iSpell;
    int nRand;
    int nResult = X2_EXECUTE_SCRIPT_END;
    effect eEffect;
 
    switch (nEvent)
    {
        /*
        case X2_ITEM_EVENT_ONHITCAST:
            // * This code runs when the item has the 'OnHitCastSpell: Unique power' property
            // * and it hits a target(if it is a weapon) or is being hit (if it is a piece of armor)
            // * Note that this event fires for non PC creatures as well.
 
            oItem = GetSpellCastItem();           // The item triggering this spellscript
            oPC = OBJECT_SELF;                      // The player triggering it
            oSpellOrigin = OBJECT_SELF ;            // Where the spell came from
            oSpellTarget = GetSpellTargetObject(); // What the spell is aimed at
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        */
        case X2_ITEM_EVENT_ACTIVATE:
            // * This code runs when the Unique Power property of the item is used
            // * Note that this event fires for PCs only
 
            oPC   = GetItemActivator();             // The player who activated the item
            oItem = GetItemActivated();             // The item that was activated
            lSpellLocation = GetItemActivatedTargetLocation();// The target creature
 
            SetLocalInt(oPC, "WSPOTION",TRUE);      // Tells the vg_water_enter script the PC has used a potion
            SendMessageToPC(oPC,"** Your have grown fin-like features that will help you swim faster. **");
 
            if(GetLocalInt(oPC, "UNDERWATER") == 1)
                {
                // Remove movement rate decreases
                eEffect = GetFirstEffect(oPC);
                while(GetIsEffectValid(eEffect))
                    {
                    if(GetEffectType(eEffect) == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE)
                        {
                        RemoveEffect(oPC, eEffect);
                        }
                    eEffect = GetNextEffect(oPC);
                    }
                }
 
            DelayCommand(fSwimTime-10,SendMessageToPC(oPC,"Your swimming potion is about to wear off."));
            DelayCommand(fSwimTime,AssignCommand(oPC, DeleteLocalInt(oPC, "WSPOTION")));
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        case X2_ITEM_EVENT_EQUIP:
            // * This code runs when the item is equipped
            // * Note that this event fires PCs only
 
            oPC = GetPCItemLastEquippedBy();        // The player who equipped the item
            oItem = GetPCItemLastEquipped();        // The item that was equipped
 
 
            if((GetLocalInt(oPC, "UNDERWATER") == 1)&&(GetLocalInt(oPC, "WSPOTION") == 0))
                {
                // Remove movement rate decreases
                eEffect = GetFirstEffect(oPC);
                while(GetIsEffectValid(eEffect))
                    {
                    if(GetEffectType(eEffect) == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE)
                        {
                        RemoveEffect(oPC, eEffect);
                        }
                    eEffect = GetNextEffect(oPC);
                    }
                }
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        case X2_ITEM_EVENT_UNEQUIP:
            // * This code runs when the item is unequipped
            // * Note that this event fires PCs only
 
            oPC    = GetPCItemLastUnequippedBy();   // The player who unequipped the item
            oItem = GetPCItemLastUnequipped();     // The item that was unequipped
 
            if((GetLocalInt(oPC, "UNDERWATER") == 1)&&(GetLocalInt(oPC, "WSPOTION") == 0))
                {
                // Reduce the entering objects movement speed
                ApplyEffectToObject(2, EffectMovementSpeedDecrease(75), oPC);
                }
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
 
        /*
        case X2_ITEM_EVENT_ACQUIRE:
            // * This code runs when the item is acquired
            // * Note that this event fires PCs only
 
            oPC = GetModuleItemAcquiredBy();        // The player who acquired the item
            oItem = GetModuleItemAcquired();       // The item that was acquired
 
            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to
            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the
            //code after the execute is done
            nResult = X2_EXECUTE_SCRIPT_CONTINUE;
            break;
[b] [/b]
[b]        case X2_ITEM_EVENT_UNACQUIRE:[/b]
[b] [/b]
[b]            // * This code runs when the item is unacquired[/b]
[b]            // * Note that this event fires PCs only[/b]
[b] [/b]
[b]            oPC = GetModuleItemLostBy();            // The player who dropped the item[/b]
[b]            oItem = GetModuleItemLost();           // The item that was dropped[/b]
[b] [/b]
[b]            //Change the following line from X2_EXECUTE_SCRIPT_CONTINUE to[/b]
[b]            //X2_EXECUTE_SCRIPT_END if you want to prevent the calling of the[/b]
[b]            //code after the execute is done[/b]
[b]            nResult = X2_EXECUTE_SCRIPT_CONTINUE;[/b]
[b]            break;[/b]
[b] [/b]
[b]       case X2_ITEM_EVENT_SPELLCAST_AT:[/b]
[b]            //* This code runs when a PC or DM casts a spell from one of the[/b]
[b]            //* standard spellbooks on the item[/b]
[b] [/b]
[b]            oPC = OBJECT_SELF;                      // The player who cast the spell[/b]
[b]            oItem = GetSpellTargetObject();        // The item targeted by the spell[/b]
[b]            iSpell = GetSpellId();&a