Author Topic: Underwater script systems  (Read 1047 times)

Legacy_ShadowM

  • Hero Member
  • *****
  • Posts: 1373
  • Karma: +0/-0
Underwater script systems
« Reply #15 on: June 03, 2011, 10:21:18 pm »


               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 '<img'> ) 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.
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #16 on: June 03, 2011, 10:39:12 pm »


               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 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Underwater script systems
« Reply #17 on: June 04, 2011, 02:30:44 am »


               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 .
                     
                  


            

Legacy_Karvon

  • Sr. Member
  • ****
  • Posts: 430
  • Karma: +0/-0
Underwater script systems
« Reply #18 on: June 04, 2011, 01:31:04 pm »


               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
               
               

               
            

Legacy_Karvon

  • Sr. Member
  • ****
  • Posts: 430
  • Karma: +0/-0
Underwater script systems
« Reply #19 on: June 04, 2011, 01:32:49 pm »


               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
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #20 on: June 04, 2011, 05:43:36 pm »


               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.
               
               

               
            

Legacy_Artistmonk

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Underwater script systems
« Reply #21 on: June 05, 2011, 02:54:27 am »


               I just saw this, will have questions when I get home, if that's okay with Funky.
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Underwater script systems
« Reply #22 on: June 06, 2011, 05:02:02 pm »


               @ 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,
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Underwater script systems
« Reply #23 on: June 06, 2011, 06:50:17 pm »


               

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
               
               

               
            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Underwater script systems
« Reply #24 on: June 06, 2011, 08:56:29 pm »


               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;
 
        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();&a
               
               

               
            

Legacy_Invisig0th

  • Sr. Member
  • ****
  • Posts: 279
  • Karma: +0/-0
Underwater script systems
« Reply #25 on: June 06, 2011, 09:03:36 pm »


               Thanks very much, TMSDude. Much appreciated.
               
               

               
            

Legacy_Artistmonk

  • Jr. Member
  • **
  • Posts: 54
  • Karma: +0/-0
Underwater script systems
« Reply #26 on: June 07, 2011, 02:37:46 am »


               TMSdude, yours is hack free but doesn't affect spells correct?

Funky, your code affects spells, does it require anything outside of the NWN engine  and script functions?
               
               

               
            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Underwater script systems
« Reply #27 on: June 07, 2011, 03:15:00 am »


               It does effect spells. hence the spell hook.

And funkys requires nwnx i would suspect at the least.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Underwater script systems
« Reply #28 on: June 07, 2011, 04:09:21 am »


               Ours does indeed require nwnx, and linux-only plugin nwnx_structs, but only trivially, not for the core functionality. The bubbles, for example, we set a unique effect id on, to check for it and only apply if not present already (and to strip when leaving watewr). You don't really need the bubble vfx at all - things like this we just use, well, because we can. '<img'>

Funky
               
               

               
            

Legacy_TSMDude

  • Hero Member
  • *****
  • Posts: 1515
  • Karma: +0/-0
Underwater script systems
« Reply #29 on: June 07, 2011, 04:34:06 am »


               

FunkySwerve wrote...

Ours does indeed require nwnx, and linux-only plugin nwnx_structs, but only trivially, not for the core functionality. The bubbles, for example, we set a unique effect id on, to check for it and only apply if not present already (and to strip when leaving watewr). You don't really need the bubble vfx at all - things like this we just use, well, because we can. '<img'>

Funky

You can add it as well as we did this way;


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

While not as purdy as scripts of real scripters we tried, lol....it fires off every 6 seconds flying off the PC making the sound and all that.