Author Topic: Dynamically Generated Areas Question  (Read 520 times)

Legacy_Dae-Glyth

  • Newbie
  • *
  • Posts: 28
  • Karma: +0/-0
Dynamically Generated Areas Question
« on: November 08, 2010, 05:30:29 pm »


                Hi again, I did some searching and I couldn’t find anything specific to the problem I want to address here.

Basically, I have an area created in a module and a script that teleports a player to this area. 
The script could be attached to an existing spell, or item, it doesn’t really matter.

Basically here’s the goal:

To have a caster use a spell/item to teleport to their own personal dimension.

 

Every dimension is identical.  However, I don’t want to create 50 (or so) such copies of the area (to accommodate every possible player online), but I don’t want all casters to teleport into the same room.  I want them all to have their own specific room.

So, this begs the question as to if there’s a method to create a copy of an area on the fly prior to porting the character and then delete it after the player exits?

If not then the only option is to create 50 (or so) copies of the area and then porting the player to the next available empty copy.
               
               

               


                     Modifié par Dae-Glyth, 08 novembre 2010 - 05:31 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #1 on: November 08, 2010, 05:54:52 pm »


               There's a NWNX plugin for instancing areas. It is otherwise impossible, though you CAN make some 20 or so identical areas and spawn in placeables on the fly, simply sorting the pc to the next empty area, and blocking them if all are full (unlikely).

Here's the link to the plugin:
http://www.nwnx.org/...opic.php?t=1244

[edit]
Just read the end of your post, sorry. 50 is way too many, as is 20 in all likelihood. You're not likely to have more than a couple in use at a time even on a busy server.

Funky
               
               

               


                     Modifié par FunkySwerve, 08 novembre 2010 - 05:57 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #2 on: November 08, 2010, 05:56:59 pm »


               Well, NWNX can handle this, but making 50 copies of areas isn't too bad idea. You then can cut them from module and add to your server's override so they won't bother you in toolset.

EDIT: Funky was faster '<img'>
               
               

               


                     Modifié par ShaDoOoW, 08 novembre 2010 - 05:57 .
                     
                  


            

Legacy_Dae-Glyth

  • Newbie
  • *
  • Posts: 28
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #3 on: November 08, 2010, 08:33:39 pm »


               If you're curious here's where I am going with this, these are some of the specifications -- I'm not claiming that these are wholy possible to implement or not, but it's the gist of the idea.  I know it's a long read, and anti-d20 so don't chastise me!



** Fort: Death magic itself has to be re-evaluated.  Instant death is not fun.  However, making death spells just deal damage is also not fun.  



Here's one proposal (though, pretty insane).  A death spell will always be gauranteed to hit.  When a death spell hits a player, a petrified clone of the player (or something representing the player's body) will spawn where the player is currently and the player's playable character will be ported out into a mage specific fugue-like dimension.  The petrified clone represents the physical body of the player, and for the duration of the death spell the body is immune to all forms of damage, abilities, and spells.  The "spirit" of the player is represented the playable character in the mage specific fugue-like realm.  



FORT score will reduce the total duration (number of rounds) that they must spend in this dimension.  The more time spent in this realm will have very bad side-effects (the player will suffer damage based on percentage of total health remaining).  The player will fall victim to sporadic negative energies in the realm itself causing severe damage, or perhaps have to face some nightmarish mobs using pillars to break line of sight with the stationary mobs so as to minimize damage taken.  There may be some strategy (such as moving out of AOE's) in order to minimize some of the damage suffered while in the dimension.  



All such realms will either have magic supression so that spells/abilities cannot be used, or some other system to make sure the player is not healing/buffing while in the realm.  Further, there will be many such possible realms to keep things interesting, you'll never know which one you'll get.  If the player dies inside the realm, then the petrified clone is removed and the player's corpse shows up there.  If the player survives the dimension, the petrified clone will be removed and the player will be ported back with the HP level they had in the realm, in most cases the player will have suffered great damage if they come out alive.  



If the player does manage to come out alive they will be granted a buff with a clear visual indicator to everyone that they have a stronger connection to the material plane now and cannot be subject to another death spell for X amount of time.  This keeps it from being spammed.  



Also, it may be possible that the player recieves a couple rounds of damage immunity to compensate for loading in issues.



Further, a mechanism must be set in place to deal with people dc'ing and/or logging out whilst in the dimensional room.


               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #4 on: November 08, 2010, 09:12:45 pm »


               I think saving throws are a much better (read: more granulated) way of ensuring that that effect isn't spammed, as opposed to a lockout timer. Further, giving something like that as an automatic one-off to mages is immensely powerful. Not that that has anything to do with your question. '<img'>



So long as you're sendning NPCs to their own separate room, you would only need 1 room for them. Given that it's a death magic mechanic, I would allow one player room per player slot on your server. That will definitely be overkill 99.9999% of the time, but you simply can't have that kind of failure in a combat/game balance scenario. I'm guessing that's part of the reason you shared your plan - makes sense.



Funky
               
               

               
            

Legacy_Dae-Glyth

  • Newbie
  • *
  • Posts: 28
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #5 on: November 08, 2010, 09:30:31 pm »


               Ah yea, no I agree about the saving throws gradient thing -- in theory.  I left out the reasoning that led to this design decision, though:

Consider the following:
* A caster with an ability score for their main caster attribute between 40 to 50.  This leaves an ability modifer of 15 to 20.  

* Spells may have spell levels 0 to 9.

* Spells have a base 10 DC.

* Spell DC can be further augmented by spell focus feats 0,2,4, or 6.

Total DC is then determined:

Ability modifier (15 to 20) + Base 10 + Spell Level (0 to 9) + Spell Focus feat ( 0, 2, 4, 6 )

At worst: 15+10+0+0 = 25
At best: 20+10+9+6 = 45


In General, d20 causes:

Fort: Negates necromatic spells

Reflex: Negates evocation spells

Will: Negates all mind spells



While RNG can be important, I believe this is hardly ideal for an on-line game for these reasons:

1. It can, in some cases, become too "easy" to become "immune" to a vast majority of spells.
2. It encourages casters to spam spells until they get "lucky" such as with auto-failure.  It is not skillful to spam a spell in hopes for a lucky failure.
3. Automatic death based on a roll is not fun in PVP, period.  Further, in PVE it can be too powerful as well requiring many mobs to be made immune to death magic thereby rendering death magic useless.


               
               

               


                     Modifié par Dae-Glyth, 08 novembre 2010 - 09:31 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #6 on: November 08, 2010, 09:34:28 pm »


               If you want to get rid of spamming spells, it may be easier to use nwnplayer.ini setting and set

Saving Throw Automatic Failure On 1

to 0

I never had problems with this btw, because Im using random immunities every time. As long as your critters are not static, you can't cheat them this way.
               
               

               


                     Modifié par ShaDoOoW, 08 novembre 2010 - 09:35 .
                     
                  


            

Legacy_Dae-Glyth

  • Newbie
  • *
  • Posts: 28
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #7 on: November 08, 2010, 09:36:39 pm »


               Yea, I know about the new INI feature to do that.  However, I think that is even worse than having it on.  Might as well just hand out an item that grants everyone immunity to every saving throw related thing.

What I mean is, it becomes way too easy for a player to become "immune" to basically everything saving throw related with the right build/items.  Unless you make players/critters have extremely high (tweaked) DC's.
               
               

               


                     Modifié par Dae-Glyth, 08 novembre 2010 - 09:40 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #8 on: November 08, 2010, 10:36:15 pm »


               Agreed (with Shadow) on the no-auto failure rationale - we don't have auto-fail for monsters, only players (mostly party PvM server setting with rare PvP, though it doesn't matter since monster saves are balanced). We answered the immunities things by making many, but not all attacks, ignore immunity from gear. You can do this by stripping and reapplying gear immunities without players ever noticing (though you don't need to for death effects) - which is of course far more granular. Here's our hg_attack_inc:


#include "ac_level_inc"
#include "hg_antiex_inc"

void AttackEffectCallback (int nCallback, object oSelf, object oTarget);
void AttackResistCallback (int nCallback, object oSelf, object oTarget);


int GetCombatActive (object oCreature, object oArea) {
    if (!GetIsInCombat(oCreature) || GetLocalInt(oCreature, "Friendly"))
        return FALSE;

    if (GetLocalInt(oArea, "TimeStop") && GetLocalObject(oArea, "TimeStopper") != oCreature)
        return FALSE;

    if (GetHasEffectOfType(EFFECT_TYPE_PETRIFY, oCreature))
        return FALSE;

    return TRUE;
}

int GetArmorACBase (object oTarget) {
    return GetItemACBase(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget));
}

int GetIsKnockedDown (object oTarget) {
    return GetHasEffectOfTrueType(EFFECT_TRUETYPE_KNOCKDOWN, oTarget);
}

int GetPCGrappleModifier (object oPC) {
    int nBaseItem = BASE_ITEM_GLOVES, nMod = GetTrueBaseAttackBonus(oPC);
    object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);

    if (GetHasFeat(FEAT_EPIC_PROWESS, oPC))
        nMod += 1;

    /* add weapon feats */
    if (GetIsObjectValid(oWeapon))
        nBaseItem = GetBaseItemType(oWeapon);
    if (GetHasFeat(GetWeaponFocusFeat(nBaseItem), oPC))
        nMod += 1;
    if (GetHasFeat(GetWeaponGreaterFocusFeat(nBaseItem), oPC))
        nMod += 1;
    if (GetHasFeat(GetWeaponEpicFocusFeat(nBaseItem), oPC))
        nMod += 2;
    if (GetHasFeat(GetWeaponLegendaryFocusFeat(nBaseItem), oPC))
        nMod += 3;

    /* add WM bonus AB */
    if (GetHasFeat(GetWeaponOfChoiceFeat(nBaseItem), oPC)) {
        int nWM = GetLevelByclass(class_TYPE_WEAPON_MASTER, oPC);

        if (nWM < 13)
            nMod++;
        else
            nMod += 1 + ((nWM - 10) / 3);
    } else {
        /* unarmed feats count double if no WM */
        if (GetHasFeat(FEAT_WEAPON_FOCUS_UNARMED_STRIKE, oPC))
            nMod += 1;
        if (GetHasFeat(HGFEAT_GREATER_WEAPON_FOCUS_UNARMED_STRIKE, oPC))
            nMod += 1;
        if (GetHasFeat(FEAT_EPIC_WEAPON_FOCUS_UNARMED, oPC))
            nMod += 2;
        if (GetHasFeat(HGFEAT_LEG_WEAPON_FOCUS_UNARMED_STRIKE, oPC))
            nMod += 3;
    }

    return nMod;
}


int GetAttackCheckResult (object oTarget, int nCheckType, int nCheckParam, object oSelf, float fDelay) {
    int nBonus = 0, nResult = 0, nCheckSubType = (nCheckType & 0xFFFFFF);

    if (nCheckType & 0x40000000) {
        if (GetHasSpellEffect(HGSPELL_EPIC_UNBOWED_UNBENT_UNBROKEN, oTarget))
            nBonus = 8;

        if (nCheckType & 0x3F000000 == CHECK_TYPE_SKILL)
            nBonus *= 2;
    }

    switch (nCheckType & 0x3F000000) {
        case CHECK_TYPE_SAVE:
            if (nCheckSubType == 4)
                nResult = MindSave(oTarget, nCheckParam, oSelf);
            else
                nResult = GetSaveCheckResult(nCheckSubType & 0xF, oTarget, nCheckParam, (nCheckSubType >> 4), TRUE, TRUE, oSelf, fDelay);
            break;

        case CHECK_TYPE_SKILL:
            nResult = GetSkillCheckResult(nCheckSubType, oTarget, nCheckParam, TRUE, (nBonus ? 2 : 0), oSelf, fDelay, 0, nBonus);
            break;

        case CHECK_TYPE_ABILITY:
            nResult = GetAbilityCheckResult(nCheckSubType, oTarget, nCheckParam, TRUE, (nBonus ? 2 : 0), oSelf, fDelay, nBonus);
            break;

        default: return 1;
    }

    return nResult;
}


object FindCasterTarget (object oSelf=OBJECT_SELF) {
    int i, nclass;
    object oPC;

    for (i = 1; i < 11; i++) {
        oPC    = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oSelf, i,
                                    CREATURE_TYPE_IS_ALIVE, TRUE, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
        nclass = GetHighestCasterclass(oPC);

        if (GetLevelByclass(nclass, oPC) > 10             &&
            !GetHasEffectOfType(EFFECT_TYPE_SILENCE, oPC) &&
            !GetHasSpellEffect(SPELL_ETHEREALNESS, oPC))
            return oPC;
    }
    return OBJECT_INVALID;
}

object FindDistancedTarget (object oSelf, int nNearest, int nFurthest, float fMaxDistance=100.0, float fMinDistance=0.0) {
    float fDist;
    int i = nNearest + Random(nFurthest - nNearest);
    object oPC = OBJECT_INVALID;

    while (i >= nNearest && !GetIsObjectValid(oPC)) {
        oPC = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, oSelf, i--,
                                 CREATURE_TYPE_IS_ALIVE, TRUE, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);

        if (GetHasSpellEffect(SPELL_ETHEREALNESS, oPC))
            oPC = OBJECT_INVALID;
        else if ((fDist = GetDistanceBetween(oPC, oSelf)) > fMaxDistance)
            oPC = OBJECT_INVALID;
        else if (fDist < fMinDistance)
            oPC = OBJECT_INVALID;
    }

    if (i < nNearest - 1)
        return OBJECT_INVALID;

    return oPC;
}


void ApplyBlindness (object oTarget, int nRounds, string sMessage, int bNoStack=TRUE, int bEx=TRUE, int nSpellId=-1, int bSpells=FALSE) {
    if ((bNoStack && GetHasEffectOfType(EFFECT_TYPE_BLINDNESS, oTarget)) || GetPlotFlag(oTarget))
        return;

    effect eLink = EffectLinkEffects(EffectBlindness(), EffectSkillDecrease(SKILL_SPOT, 30));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_BLINDVISION));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    if (bSpells)
        RemoveEffectsFromSpell(SPELL_UNDEATHS_ETERNAL_FOE, oTarget);

    ApplyVisualToObject(VFX_IMP_BLIND_DEAF_M, oTarget);
    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyConfuse (object oTarget, int nRounds=10, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if (!GetIsPC(oTarget) || GetPlotFlag(oTarget))
        return;

    effect eLink = EffectConfused();
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DOMINATED));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_CONFUSION_S, oTarget);

    if (bIgnore)
        ApplyEffectIgnoringMindImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
    else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyDaze (object oTarget, int nRounds=4, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if (GetPlotFlag(oTarget))
        return;

    effect eLink = EffectDazed();
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DISABLED));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_DAZED_S, oTarget);

    if (bIgnore)
        ApplyEffectIgnoringMindImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
    else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyDeafness (object oTarget, int nRounds, string sMessage, int bSilenceBlocks=TRUE, int bNoStack=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if ((bNoStack && GetHasEffectOfType(EFFECT_TYPE_DEAF, oTarget)) || GetPlotFlag(oTarget))
        return;

    if (bSilenceBlocks &&!GetHasEffectOfType(EFFECT_TYPE_SILENCE, oTarget))
        return;

    effect eLink = EffectLinkEffects(EffectDeaf(), EffectSkillDecrease(SKILL_LISTEN, 30));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_BLIND_DEAF_M, oTarget);
    ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyDisarm (object oTarget, string sMessage, int bIgnore=FALSE) {
    if (GetPlotFlag(oTarget) || (bIgnore < 2 && GetHasSpellImmunity(HGSPELL_IRON_GRASP, oTarget)))
        return;

    object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
    if (!GetIsObjectValid(oWeapon) || GetLocalInt(oWeapon, "REPLACED"))
        return;
    if (GetLocalInt(oWeapon, "NoDisarm") > bIgnore)
        return;

    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    if (GetIsPC(oTarget)) {
        object oReplace = DestroyLostItem(oTarget, oWeapon, "DISEQUIP", "Disarmed Weapon");
        if (GetResRef(oReplace) == "moltenslag") {
            SetDescription(oReplace, "This is a disarmed weapon.\\\\\\\\n-----\\\\\\\\nUse the Unique Power on this item to restore it.\\\\\\\\n");
            SetItemAppearance(oReplace, -3, 83);
        }
    } else {
        effect eEff = EffectDisarm(0);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eEff, oTarget);
    }
}


void ApplyFear (object oTarget, int nRounds=10, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if (GetPlotFlag(oTarget))
        return;

    effect eLink = EffectFrightened();
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_FEAR_S, oTarget);

    if (bIgnore > 1)
        ApplyEffectIgnoringImmunities(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds), IGNORE_IMM_MIND|IGNORE_IMM_FEAR);
    else if (bIgnore)
        ApplyEffectIgnoringMindImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds), FALSE);
    else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyKnockdown (object oTarget, int nRounds=3, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1, int nTumbleDC=120) {
    if (GetPlotFlag(oTarget) || GetIsKnockedDown(oTarget))
        return;

    effect eLink = EffectLinkEffects(EffectKnockdown(), EffectIcon(EFFECT_ICON_KNOCKDOWN));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    float fDur = RoundsToSeconds(nRounds);

    if (nTumbleDC > 0 && GetSkillCheckResult(SKILL_TUMBLE, oTarget, nTumbleDC) > 0)
        fDur -= 6.0;

    /* the Orb of Dragonkind halves knockdown duration */
    if (GetLocalInt(oTarget, "StatArtifactUsed") == HGARTIFACT_ORB_OF_DRAGONKIND)
        fDur /= 2.0;

    if (fDur < 6.0)
        fDur = 6.0;

    if (bIgnore) {
        ApplyEffectIgnoringKnockdownImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
        RemoveEffectsFromSpell(HGEFFECT_UNTOUCHABLE_CURSE, oTarget);
    } else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyLevelDrain (object oTarget, int nLevels=8, string sMessage="", int bIgnore=TRUE, int nSpellId=-1) {
    if (GetPlotFlag(oTarget))
        return;

    effect eLink = EffectNegativeLevel(nLevels);
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = SupernaturalEffect(eLink);

    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_REDUCE_ABILITY_SCORE, oTarget);

    if (bIgnore)
        ApplyEffectIgnoringDrainImmunity(DURATION_TYPE_PERMANENT, eLink, oTarget);
    else
        ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget);
}


void ApplyParalyze (object oTarget, int nRounds=4, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if (GetPlotFlag(oTarget))
        return;

    effect eLink = EffectParalyze();
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_PARALYZE_HOLD));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    if (bIgnore)
        ApplyEffectIgnoringMindImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
    else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplySleep (object oTarget, int nRounds=4, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if (GetPlotFlag(oTarget))
        return;

    effect eLink = EffectSleep();
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_SLEEP, oTarget);

    if (bIgnore)
        ApplyEffectIgnoringMindImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
    else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


void ApplyStun (object oTarget, int nRounds=4, string sMessage="", int bIgnore=TRUE, int bEx=TRUE, int nSpellId=-1) {
    if (GetPlotFlag(oTarget))
        return;

    effect eLink = EffectStunned();
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
    eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DISABLED));

    if (bEx)
        eLink = ExtraordinaryEffect(eLink);
    if (nSpellId >= 0)
        SetEffectSpellId(eLink, nSpellId);
    if (sMessage != "")
        DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));

    ApplyVisualToObject(VFX_IMP_STUN, oTarget);

    if (bIgnore)
        ApplyEffectIgnoringMindImmunity(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
    else
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nRounds));
}


int DoBardSong (object oSelf, int nSongLevel, int nCurseLevel, int bCurseFirst=FALSE) {
    int nSongCount = 0, nCurseCount = 0;
    location lSelf = GetLocation(oSelf);
    object oTarget;

    if (GetHasDisablingEffect(oSelf) ||
        GetHasEffectOfType(EFFECT_TYPE_SILENCE, oSelf))
        return -1;

    for (oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, lSelf);
         GetIsObjectValid(oTarget);
         oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, lSelf)) {

        if (GetIsDead(oTarget) || GetPlotFlag(oTarget))
            continue;

        if (nSongLevel > 0 && GetFactionEqual(oTarget, oSelf) && !GetHasSpellEffect(SPELLABILITY_BARD_SONG, oTarget))
            nSongCount++;
        else if (nCurseLevel > 0 && GetIsPC(oTarget) && !GetHasSpellEffect(SPELLABILITY_EPIC_CURSE_SONG, oTarget))
            nCurseCount++;
    }

    if (nSongCount > 0 && !(bCurseFirst && nCurseCount > 0)) {
        SetLocalInt(oSelf, "TempSongLevel", nSongLevel);
        AssignCommand(oSelf, ClearAllActions(TRUE));
        AssignCommand(oSelf, ActionCastSpellAtLocation(SPELLABILITY_BARD_SONG, lSelf, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));

        return 1;
    } else if (nCurseCount > 0) {
        SetLocalInt(oSelf, "TempSongLevel", nCurseLevel);
        AssignCommand(oSelf, ClearAllActions(TRUE));
        AssignCommand(oSelf, ActionCastSpellAtLocation(SPELLABILITY_EPIC_CURSE_SONG, lSelf, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));

        return 2;
    }

    return 0;
}


object DoGrapple (object oSelf, object oTarget, string sMessage, int nDC) {
    int nTargetMod, nSelfRoll, nTargetRoll;
    float fDelay = 0.0;
    string sSign = " + ";
    object oGrapple = GetLocalObject(oSelf, "GrappleTarget");

    if (!GetIsObjectValid(oGrapple) || !GetHasEffectOfType(EFFECT_TYPE_CUTSCENE_PARALYZE, oGrapple)) {
        DeleteLocalObject(oSelf, "GrappleTarget");
        RemoveEffectsOfType(EFFECT_TYPE_CUTSCENEIMMOBILIZE, oSelf, oSelf);

        oGrapple = OBJECT_INVALID;
    }


    if (GetIsObjectValid(oGrapple)) {
        if (GetPlotFlag(oGrapple) || GetDistanceBetween(oGrapple, oSelf) > 5.0 || GetHasSpellImmunity(SPELL_FREEDOM_OF_MOVEMENT, oGrapple)) {
            DeleteLocalObject(oSelf, "GrappleTarget");
            RemoveEffectsOfType(EFFECT_TYPE_CUTSCENEIMMOBILIZE, oSelf, oSelf);
            RemoveEffectsOfType(EFFECT_TYPE_CUTSCENE_PARALYZE, oGrapple, oSelf);

            return OBJECT_INVALID;
        }

        if ((nTargetMod = GetPCGrappleModifier(oGrapple)) < 0)
            sSign = " - ";

        nSelfRoll   = 5;        /* currently static */
        nTargetRoll = d20();

        if (nDC + nSelfRoll > nTargetMod + nTargetRoll) {
            string sCheck = C_PALE_BLUE_PURPLE + GetName(oGrapple) + C_BLUE_PURPLE +
                " : " + "Grapple vs. " + GetName(oSelf) + " : *grappled* : (" + IntToString(nTargetRoll) +
                sSign + IntToString(abs(nTargetMod)) + " = " + IntToString(nTargetRoll + nTargetMod) +
                " vs. DC: " + IntToString(nSelfRoll + nDC) + ")" + C_END;

            switch (GetPCFilter(oGrapple, PCFILTER_CHECK)) {
                case 0: DelayCommand(fDelay, SendMessageToPC(oGrapple, sCheck));                     break;
                case 1: DelayCommand(fDelay, SendSystemMessage(oGrapple, sCheck));                   break;
                case 2: DelayCommand(fDelay, FloatingTextStringOnCreature(sCheck, oGrapple, FALSE)); break;
            }

            effect eGrap = EffectLinkEffects(EffectCutsceneParalyze(), EffectIcon(EFFECT_ICON_DOMINATED));
            eGrap = ExtraordinaryEffect(eGrap);

            RemoveEffectsOfType(EFFECT_TYPE_CUTSCENE_PARALYZE, oGrapple, oSelf);
            DelayCommand(0.01, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eGrap, oGrapple, 30.0));

            return oGrapple;
        } else {
            string sCheck = C_PALE_BLUE_PURPLE + GetName(oGrapple) + C_BLUE_PURPLE +
                " : " + "Grapple vs. " + GetName(oSelf) + " : *escaped* : (" + IntToString(nTargetRoll) +
                sSign + IntToString(abs(nTargetMod)) + " = " + IntToString(nTargetRoll + nTargetMod) +
                " vs. DC: " + IntToString(nSelfRoll + nDC) + ")" + C_END;

            switch (GetPCFilter(oGrapple, PCFILTER_CHECK)) {
                case 0: DelayCommand(fDelay, SendMessageToPC(oGrapple, sCheck));                     break;
                case 1: DelayCommand(fDelay, SendSystemMessage(oGrapple, sCheck));                   break;
                case 2: DelayCommand(fDelay, FloatingTextStringOnCreature(sCheck, oGrapple, FALSE)); break;
            }

            DeleteLocalObject(oSelf, "GrappleTarget");
            RemoveEffectsOfType(EFFECT_TYPE_CUTSCENEIMMOBILIZE, oSelf, oSelf);
            RemoveEffectsOfType(EFFECT_TYPE_CUTSCENE_PARALYZE, oGrapple, oSelf);
        }
    } else if (GetIsPC(oTarget) && !GetPlotFlag(oTarget)) {
        if (GetDistanceBetween(oTarget, oSelf) > 2.5 || GetHasSpellEffect(SPELL_FREEDOM_OF_MOVEMENT, oTarget))
            return OBJECT_INVALID;

        if ((nTargetMod = GetPCGrappleModifier(oTarget)) < 0)
            sSign = " - ";

        nSelfRoll   = d20();
        nTargetRoll = d20();


        if (nDC + nSelfRoll > nTargetMod + nTargetRoll) {
            string sCheck = C_PALE_BLUE_PURPLE + GetName(oTarget) + C_BLUE_PURPLE +
                " : " + "Grapple vs. " + GetName(oSelf) + " : *grappled* : (" + IntToString(nTargetRoll) +
                sSign + IntToString(abs(nTargetMod)) + " = " + IntToString(nTargetRoll + nTargetMod) +
                " vs. DC: " + IntToString(nSelfRoll + nDC) + ")" + C_END;

            switch (GetPCFilter(oTarget, PCFILTER_CHECK)) {
                case 0: DelayCommand(fDelay, SendMessageToPC(oTarget, sCheck));                     break;
                case 1: DelayCommand(fDelay, SendSystemMessage(oTarget, sCheck));                   break;
                case 2: DelayCommand(fDelay, FloatingTextStringOnCreature(sCheck, oTarget, FALSE)); break;
            }

            effect eSelf = ExtraordinaryEffect(EffectCutsceneImmobilize());
            effect eGrap = EffectLinkEffects(EffectCutsceneParalyze(), EffectIcon(EFFECT_ICON_DOMINATED));
            eGrap = ExtraordinaryEffect(eGrap);

            SetLocalObject(oSelf, "GrappleTarget", oTarget);
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eGrap, oTarget, 30.0);
            ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSelf, oSelf);

            if (sMessage != "")
                DelayCommand(0.2, FloatingTextStringOnCreature(sMessage, oTarget, FALSE));
        } else {
            string sCheck = C_PALE_BLUE_PURPLE + GetName(oTarget) + C_BLUE_PURPLE +
                " : " + "Grapple vs. " + GetName(oSelf) + " : *evaded* : (" + IntToString(nTargetRoll) +
                sSign + IntToString(abs(nTargetMod)) + " = " + IntToString(nTargetRoll + nTargetMod) +
                " vs. DC: " + IntToString(nSelfRoll + nDC) + ")" + C_END;

            switch (GetPCFilter(oTarget, PCFILTER_CHECK)) {
                case 0: DelayCommand(fDelay, SendMessageToPC(oTarget, sCheck));                     break;
                case 1: DelayCommand(fDelay, SendSystemMessage(oTarget, sCheck));                   break;
                case 2: DelayCommand(fDelay, FloatingTextStringOnCreature(sCheck, oTarget, FALSE)); break;
            }
        }
    }

    return OBJECT_INVALID;
}


void DoLeechHit (object oSelf, object oTarget, int nDice=35) {
    if (GetObjectType(oTarget) != OBJECT_TYPE_CREATURE  ||
        GetRacialType(oTarget) == RACIAL_TYPE_UNDEAD    ||
        GetRacialType(oTarget) == RACIAL_TYPE_CONSTRUCT ||
        GetLevelByclass(class_TYPE_PALEMASTER, oTarget) >= 20)
        return;

    int nMax = GetCurrentHitPoints(oTarget) + 10;
    int nImm = GetDamageImmunity(oTarget, DAMAGE_TYPE_NEGATIVE);

    if (nImm >= 100 || GetPlotFlag(oTarget))
        return;


    int nHeal, nDamage = d6(nDice);

    if ((nHeal = (nDamage * (100 - nImm)) / 100) > nMax)
        nHeal = nMax;


    effect eDam = EffectDamage(nDamage, DAMAGE_TYPE_NEGATIVE);

    ApplyVisualToObject(VFX_IMP_NEGATIVE_ENERGY, oTarget);
    ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget);


    if (nHeal > 0 && GetCurrentHitPoints(oSelf) < 9999) {
        ApplyVisualToObject(CEPVFX_IMP_HEALING_M_RED, oSelf);
        ApplyEffectToObject(DURATION_TYPE_PERMANENT, ExtraordinaryEffect(EffectTemporaryHitpoints(nHeal)), OBJECT_SELF);
    }
}


void DoMaul (object oTarget, int nDiscDC, int nStrDC, string sMessage, int nDamDice=6,
             int nDamType=DAMAGE_TYPE_SLASHING, int nDamPower=DAMAGE_POWER_NORMAL, int nDamVuln=0) {
    float fDelay = 0.1;
    effect eDam, eVuln = ExtraordinaryEffect(EffectDamageImmunityDecrease(nDamType, nDamVuln));

    if (TouchAttackMelee(oTarget, FALSE) > 0) {
        do {
            if (nDamVuln > 0)
                ApplyEffectToObject(DURATION_TYPE_PERMANENT, eVuln, oTarget);

            eDam = EffectDamage(d20(nDamDice), nDamType, nDamPower);
            DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));

            fDelay  += 0.01;
            nDiscDC -= 10;
        } while (nDiscDC > 50 && GetSkillCheckResult(SKILL_DISCIPLINE, oTarget, nDiscDC) <= 0);

        if (nStrDC > 0 && GetAbilityCheckResult(ABILITY_STRENGTH, oTarget, nStrDC) <= 0)
            ApplyKnockdown(oTarget, 4, sMessage);
    }
}


void DoSlag (object oSelf, object oTarget, string sMessage, string sDesc="", int nACLimit=0) {
    if (!GetIsObjectValid(oTarget) || !GetIsPC(oTarget))
        return;

    if (nACLimit)
        SetLocalInt(oSelf, "RuinACLimit", nACLimit);

    SetLocalInt(oSelf, "RuinSlag", -1);
    SetLocalObject(oSelf, "RuinTarget", oTarget);
    SetLocalString(oSelf, "RuinMessage", sMessage);
    SetLocalString(oSelf, "RuinDesc", sDesc);

    ExecuteScript("x2_s3_ruinarmor", oSelf);

    DeleteLocalObject(oSelf, "RuinTarget");
}


int DoRadiusKnockdown (object oSelf, float fRadius, int nCheckType, int nCheckParam,
                       int nRounds=3, string sMessage="", int bIgnore=TRUE, int bEx=TRUE,
                       int nSpellId=-1, int nTumbleDC=120, int nEffectCallback=0,
                       float fBaseDelay=0.0, int nTargetMask=OBJECT_TYPE_CREATURE,
                       int nArmorModifier=0, int nTouchAttack=FALSE) {
    int nCheckDC, nTouch, nTargets = 0;
    float fDelay;
    object oTarget;
    location lSelf = GetLocation(oSelf);

    nCheckType |= 0x40000000;

    for (oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lSelf, TRUE, nTargetMask);
         GetIsObjectValid(oTarget);
         oTarget = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lSelf, TRUE, nTargetMask)) {

        if (GetObjectType(oTarget) == OBJECT_TYPE_AREA_OF_EFFECT) {
            if (!GetLocalInt(oTarget, "NoAoEDispel"))
                DestroyObject(oTarget);
            continue;
        }

        if (nTouchAttack && (nTouch = TouchAttackMelee(oTarget, FALSE)) < 1)
            continue;

        if (fDelay < 0.0)
            fDelay = -fBaseDelay + (IntToFloat(Random(11) - 5) * 0.1);
        else
            fDelay = fBaseDelay + (GetDistanceBetween(oSelf, oTarget) / 20.0);

        if (nArmorModifier != 0)
            nCheckDC = nCheckParam + ((GetArmorACBase(oTarget) * nArmorModifier) / 10);
        else
            nCheckDC = nCheckParam;

        if (nTouch == 2)
            nCheckDC += nTouchAttack;

        if (GetIsTarget(oTarget, TARGET_TYPE_DEFAULT, oSelf) &&
            !GetPlotFlag(oTarget)                            &&
            !GetAttackCheckResult(oTarget, nCheckType, nCheckDC, oSelf, fDelay)) {

            DelayCommand(fDelay, ApplyKnockdown(oTarget, nRounds, sMessage, bIgnore, bEx, nSpellId, nTumbleDC));

            if (nEffectCallback > 0)
                DelayCommand(fDelay + 0.01, AttackEffectCallback(nEffectCallback, oSelf, oTarget));
        }

        nTargets++;
    }

    return nTargets;
}


void DoSingleKnockdown (object oSelf, object oTarget, int nCheckType, int nCheckParam,
                        int nRounds=3, string sMessage="", int bIgnore=TRUE, int bEx=TRUE,
                        int nSpellId=-1, int nTumbleDC=120, int nEffectCallback=0) {
    location lSelf = GetLocation(oSelf);

    nCheckType |= 0x40000000;

    if (GetIsTarget(oTarget, TARGET_TYPE_DEFAULT, oSelf) &&
        !GetAttackCheckResult(oTarget, nCheckType, nCheckParam, oSelf, 0.0)) {

        ApplyKnockdown(oTarget, nRounds, sMessage, bIgnore, bEx, nSpellId, nTumbleDC);

        if (nEffectCallback > 0)
            AttackEffectCallback(nEffectCallback, oSelf, oTarget);
    }
}


void AttackNearest (object oSelf=OBJECT_SELF, float fDelay=1.0) {
    object oNearest = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oSelf, 1,
                                         CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);

    if (GetIsObjectValid(oNearest)) {
        DelayCommand(fDelay, ActionAttack(oNearest));
    } else {
        oNearest = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oSelf, 1,
                                      CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);

        if (GetIsObjectValid(oNearest))
            DelayCommand(fDelay, ActionAttack(oNearest));
    }
}


void DestroyBlades (location lTarget, float fRadius=RADIUS_SIZE_LARGE) {
    effect eVis = EffectVisualEffect(VFX_IMP_DISPEL);
    object oTarget;

    for (oTarget = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, lTarget);
         GetIsObjectValid(oTarget);
         oTarget = GetNextObjectInShape(SHAPE_SPHERE, fRadius, lTarget)) {

        if (GetResRef(oTarget) == "x2_s_bblade") {
            ApplyEffectAtLocation(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DISPEL), GetLocation(oTarget));
            DelayCommand(0.0, SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_MORDENKAINENS_DISJUNCTION)));
        }
    }
}


               
               

               


                     Modifié par FunkySwerve, 08 novembre 2010 - 10:38 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #9 on: November 08, 2010, 10:39:31 pm »


               Sigh. Go figure, forum cut off the rest of my reply. Edited, and appended:


And here are the Ignoring functions it relies on (part of a far larger include, so some may be missing):


void RestoreItemImmunities (object oTarget) {
    int i, j, nImm;
    object oItem;
    itemproperty ip;

    for (i = 0; i < 18; i++) {
        oItem = GetItemInSlot(i, oTarget);

        if (!GetIsObjectValid(oItem) || !(nImm = GetLocalInt(oItem, "ImmMissing")))
            continue;

        for (j = 0; j < 11; j++) {
            if (nImm & (1 << j)) {
                if (j == 10)
                    ip = ItemPropertyFreeAction();
                else
                    ip = ItemPropertyImmunityMisc(j);

                AddItemProperty(DURATION_TYPE_PERMANENT, ip, oItem);
            }
        }

        DeleteLocalInt(oItem, "ImmMissing");
    }
}

int IgnoreItemImmunities (object oTarget, int nIgnore) {
    int i, j, nType, nSubType, nMissing, nRemoved = 0;
    object oItem, oArea;
    itemproperty ip;

    for (i = 0; i < 18; i++) {
        oItem = GetItemInSlot(i, oTarget);

        if (!GetIsObjectValid(oItem))
            continue;

        nMissing = GetLocalInt(oItem, "ImmMissing");

        ip = GetFirstItemProperty(oItem);

        while (GetIsItemPropertyValid(ip)) {
            nType = GetItemPropertyType(ip);

            if (nType == ITEM_PROPERTY_IMMUNITY_MISCELLANEOUS) {
                int nSubType = 1 << GetItemPropertySubType(ip);

                if (nIgnore & nSubType) {
                    RemoveItemProperty(oItem, ip);
                    nMissing |= nSubType;

                    nRemoved++;
                }
            } else if (nType == ITEM_PROPERTY_FREEDOM_OF_MOVEMENT && (nIgnore & IGNORE_FREEDOM)) {
                RemoveItemProperty(oItem, ip);
                nMissing |= IGNORE_FREEDOM;

                nRemoved++;
            }

            ip = GetNextItemProperty(oItem);
        }

        if (nMissing)
            SetLocalInt(oItem, "ImmMissing", nMissing);
    }

    return nRemoved;
}

void ApplyEffectIgnoringImmunities (int nDurType, effect eEff, object oTarget, float fDur=0.0, int nIgnore=0) {
    int nRemoved = IgnoreItemImmunities(oTarget, nIgnore);

    if (nRemoved > 0) {
        AssignCommand(GetArea(oTarget), DelayCommand(0.1,
            ApplyEffectIgnoringImmunities(nDurType, eEff, oTarget, fDur, nIgnore)));
    } else {
        ApplyEffectToObject(nDurType, eEff, oTarget, fDur);
        AssignCommand(GetArea(oTarget), RestoreItemImmunities(oTarget));
    }
}

void ApplyEffectIgnoringDrainImmunity (int nDurType, effect eEff, object oTarget, float fDur=0.0, int bSpells=TRUE) {
    if (bSpells) {
        int nSpellId;
        effect eSpell = GetFirstEffect(oTarget);

        while (GetIsEffectValid(eSpell)) {
            nSpellId = GetEffectSpellId(eSpell);

            if (nSpellId == SPELL_NEGATIVE_ENERGY_PROTECTION ||
                nSpellId == SPELL_UNDEATHS_ETERNAL_FOE)
                RemoveEffect(oTarget, eSpell);

            eSpell = GetNextEffect(oTarget);
        }
    }

    ApplyEffectIgnoringImmunities(nDurType, eEff, oTarget, fDur, IGNORE_IMM_DRAIN);
}

void ApplyEffectIgnoringKnockdownImmunity (int nDurType, effect eEff, object oTarget, float fDur=0.0) {
    ApplyEffectIgnoringImmunities(nDurType, eEff, oTarget, fDur, IGNORE_IMM_KNOCKDOWN);
}

void ApplyEffectIgnoringMindImmunity (int nDurType, effect eEff, object oTarget, float fDur=0.0, int bSpells=TRUE) {
    int nSpellId;
    effect eSpell = GetFirstEffect(oTarget);

    while (GetIsEffectValid(eSpell)) {
        nSpellId = GetEffectSpellId(eSpell);

        if (bSpells) {
            if (nSpellId == SPELL_MIND_BLANK) {
                RemoveEffect(oTarget, eSpell);

                if (bSpells > 0)
                    return;
            } else if (nSpellId == SPELL_CLARITY || nSpellId == SPELL_LESSER_MIND_BLANK)
                RemoveEffect(oTarget, eSpell);
        }

        eSpell = GetNextEffect(oTarget);
    }

    ApplyEffectIgnoringImmunities(nDurType, eEff, oTarget, fDur, IGNORE_IMM_MIND);
}

void ApplyEffectIgnoringFreedom (int nDurType, effect eEff, object oTarget, float fDur=0.0, int bSpells=TRUE) {
    if (bSpells)
        RemoveEffectsFromSpell(SPELL_FREEDOM_OF_MOVEMENT, oTarget);

    ApplyEffectIgnoringImmunities(nDurType, eEff, oTarget, fDur, IGNORE_FREEDOM);
}

And yes, all our creatures' DCs are both high and carefully adjusted against what PCs can obtain.

This approach leaves gear still important, so long as you leave enough attacks that respect immunity, but allows you to make the important checks granular again, and prevents the 'immune to everything' syndrome. It also makes class/feat-based immunity like mind immunity all the better (though if we wanted to get REALLY nasty we could do the same there, using nwnx_exalt, stripping and reapplying the immunity feats).

Funky
               
               

               
            

Legacy_Dae-Glyth

  • Newbie
  • *
  • Posts: 28
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #10 on: November 09, 2010, 06:31:21 am »


               Yea, on any module I host I do not give my PC's items that grant immunities.  So I don't really worry about that so much.  Anyways, it's way easier to show my design in a 2minute youtube so here:



http://www.youtube.c...u/0/muA8TgqUWB4
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #11 on: November 09, 2010, 07:36:50 am »


               I'm afraid you lost me. Why include the issue as a part of the rationale for your design decision if it ISN'T an issue?:

Dae-Glyth wrote...

Ah yea, no I agree about the saving throws gradient thing -- in theory. I left out the reasoning that led to this design decision, though:

Consider the following:

While RNG can be important, I believe this is hardly ideal for an on-line game for these reasons:

1. It can, in some cases, become too "easy" to become "immune" to a vast majority of spells.


Dae-Glyth wrote...

Yea, I know about the new INI feature to do that. However, I think that is even worse than having it on. Might as well just hand out an item that grants everyone immunity to every saving throw related thing.

What I mean is, it becomes way too easy for a player to become "immune" to basically everything saving throw related with the right build/items.  Unless you make players/critters have extremely high (tweaked) DC's.


I guess I just wasted 10 minutes with those last two posts. I get the sense that you're speaking from a theoretical, rather than a practical, perspective.

Funky
               
               

               


                     Modifié par FunkySwerve, 09 novembre 2010 - 07:41 .
                     
                  


            

Legacy_CalSailX

  • Jr. Member
  • **
  • Posts: 93
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #12 on: November 09, 2010, 04:47:59 pm »


               

Not a waste Funky.... I generally learn a thing or two, looking at the guts of what's been done at HG  thanks for taking the 10 for those following the thread.




               
               

               
            

Legacy_420

  • Sr. Member
  • ****
  • Posts: 370
  • Karma: +0/-0
Dynamically Generated Areas Question
« Reply #13 on: November 09, 2010, 05:47:32 pm »


               I have an "extra-dimensional tent" item on my server that allows the PC to jump to a small 2x2 Tent area (City Interior). So I made one large 8x10 area with 20 2x2 Tent groups (max server capacity is 20). Then when a PC activates the item I send them to an unoccupied tent. Works like a charm. Just remember to wipe the minimap when the PC exits.

-420
               
               

               


                     Modifié par 420, 09 novembre 2010 - 05:48 .