Author Topic: Underwater script systems  (Read 1131 times)

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« on: May 02, 2011, 11:21:20 pm »


               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 .
                     
                  


            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #1 on: May 03, 2011, 03:30:12 am »


               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 .
                     
                  


            

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Underwater script systems
« Reply #2 on: May 03, 2011, 03:47:14 pm »


               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.
               
               

               
            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Underwater script systems
« Reply #3 on: May 03, 2011, 08:12:06 pm »


               

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.
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #4 on: May 04, 2011, 08:21:38 pm »


               E-mail sent.Thanks very much!
               
               

               
            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Underwater script systems
« Reply #5 on: May 04, 2011, 10:49:20 pm »


               

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*
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #6 on: May 05, 2011, 12:52:25 am »


               No rush! Thank you!
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #7 on: June 03, 2011, 01:18:39 am »


               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.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Underwater script systems
« Reply #8 on: June 03, 2011, 01:51:53 am »


               

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


               
               

               
            

Legacy_cmwise

  • Jr. Member
  • **
  • Posts: 62
  • Karma: +0/-0
Underwater script systems
« Reply #9 on: June 03, 2011, 04:21:41 am »


               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
               
               

               
            

Legacy_cmwise

  • Jr. Member
  • **
  • Posts: 62
  • Karma: +0/-0
Underwater script systems
« Reply #10 on: June 03, 2011, 04:23:48 am »


               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
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #11 on: June 03, 2011, 01:56:00 pm »


               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 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Underwater script systems
« Reply #12 on: June 03, 2011, 04:12:12 pm »


               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 .
                     
                  


            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #13 on: June 03, 2011, 05:31:15 pm »


               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 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Underwater script systems
« Reply #14 on: June 03, 2011, 09:33:53 pm »


               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