Aller au contenu

Photo

"Reduce speed HASTE" Scripting


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

#1
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
Haste is really too fast to our .mod, it's necessary reduce your speed. We are thinking to make it slow using NWNX or scripting maybe using a penalty speed on haste.

if anyone can help i'll be grateful. 

#2
WhiZard

WhiZard
  • Members
  • 1 204 messages
Is it just haste or movement speed in general that is too fast?

If it is movement speed in general, you can decrease the values in creaturespeed.2da.

If the problem is haste, and you aren't using perma-haste gear you can add on a movement speed penalty to the abilities that produce it.

#3
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
In the general, haste itens is too fast. I want to reduce especially the itens speed.




I found it:

--------------------------------------------------------------------------------------
Message by Squatting Monk -

You can reduce the movement speed with EffectMovementSpeedDecrease().

Add this right under line 59 of nw_s0_haste:
Code:
// Slow it down a bit
eHaste = EffectLinkEffects(EffectMovementSpeedDecrease(30), eHaste);


Replace 30 with a smaller value if that slows it down too much.

Note: this will not help with hasted items, only the spell.
----------------------------------------------------------------------------------------------

Modifié par sandronejm, 26 janvier 2014 - 01:54 .


#4
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
A non-NWNX way to fix haste speed on items would be to modify your OnPlayer(Un)EquipItem scripts to look for haste properties on any equipped items and apply/remove a movement penalty there.

#5
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
We need do it for all itens hasted?

#6
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
You don't need a script for each individual item. Rather, you need something like the following in your OnPlayerEquipItem script (disclaimer: I'm doing this in a text editor since I'm at work and cannot guarantee it will compile):

   object oItem = GetPCItemLastEquipped();

    // If the equipped item has the Haste property...
    if (GetItemHasItemProperty(oItem, ITEM_PROPERTY_HASTE))
    {
        object oPC = GetPCItemLastEquippedBy();
        int nItems = GetLocalInt(oPC, "HastedItems");
	
        // Slow him if he hasn't been slowed by another hasted item.
        if (!nItems)
        {
            effect eSlow = EffectMovementSpeedDecrease(30);
            ApplyEffectToObject(DURATION_TYPE_PERMANENT, eSlow, oPC);
        }
	
        // Increment a counter for hasted items in case the PC has more than one
        SetLocalInt(oPC, "HastedItems", nItems + 1);
    }

...and something like this in your OnPlayerUnEquipItem script:

    object oItem = GetPCItemLastUnequipped();

    // If the unequipped item has the Haste property...
    if (GetItemHasItemProperty(oItem, ITEM_PROPERTY_HASTE))
    {
        object oPC = GetPCItemLastUnequippedBy();
        int nItems = GetLocalInt(oPC, "HastedItems");
	
        // Remove the slow effect if he has no more hasted items.
        if (nItems == 1)
        {
            object oCreator = GetModule();
            effect eEffect = GetFirstEffect(oPC);
		
            while (GetIsEffectValid(eEffect))
            {
                if (GetEffectType(eEffect) == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE &&
                    GetEffectCreator(eEffect) == oCreator) // Don't remove slow effects from other sources
                    RemoveEffect(oPC, eEffect);
			
                eEffect = GetNextEffect(oPC);
            }
        }
	
        // Decrement a counter for hasted items in case the PC has more than one
        SetLocalInt(oPC, "HastedItems", nItems - 1);
    }

Note: I'm not sure if equipped items that are destroyed with DestroyObject() are still valid when the OnPlayerUnequipItem event is fired. If not, you'd need to loop through all currently equipped items to see if a now-invalid item had the Haste property.

Modifié par Squatting Monk, 24 janvier 2014 - 02:54 .


#7
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
I'll test. Thanks

The ON_PLAYER_EQUIP has compiled with sucess but the

on_player_UNEQUIP don't compile (looks 8 line wrong)

#8
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
Ah. Yeah, the line that reads:

object oPC = GetPCItemLastUnquippedBy();

...should read:

object oPC = GetPCItemLastUnequippedBy();

Modifié par Squatting Monk, 24 janvier 2014 - 02:55 .


#9
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
It's works. Thanks

#10
WhiZard

WhiZard
  • Members
  • 1 204 messages
For OnEquip replace

effect eSlow = EffectMovementSpeedDecrease(30);

with

effect eSlow = EffectMovmentSpeedIncrease(-30);

and for OnUnEquip replace

if (GetEffectType(eEffect) == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE && GetEffectCreator(eEffect) == oCreator)

with

if (GetEffectType(eEffect) == EFFECT_TYPE_MOVEMENT_SPEED_INCREASE && GetEffectCreator(eEffect) == oCreator)


Can't have those with freedom getting an unnecessary advantage can we?

#11
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
Congratulations, WhiZard. You think for us. haha.

I spent some time testing the system and it's work fine. It's only works on equip. When player login with item already equiped the penalty speed isn't as we programmed ):

#12
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
WhiZard,
The code effect eSlow = EffectMovmentSpeedIncrease(-30); won't compile. You need replace Movment to Movement.

1. Rogues hide/unhide cancel the speed decrease.
2. Login in the game cancell the speed decrease.
3. I'm not sure but rest cancel the speed decrease.

#13
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages
You'll need to make the effect Supernatural to avoid it being removed by resting. Or you can simply reapply it when someone cancels or finished a rest.

#14
WhiZard

WhiZard
  • Members
  • 1 204 messages
1. Hiding should not cancel the decrease. The icon may disappear (because removing an effect triggers the removal of associated icons from the display) but the effect is there and the character sheet should show it.

2. Login may have the same issue as 1). Check to see if you are just missing the icon or the effect itself.

3. As MM described an effect needs to be supernatural to not be removed on rest.

Additionally there may be issues with dying and resurrection (so that a character, if resurrected with the haste item already equipped, will not incur a penalty). The number of scripts needed to get the behavior for the haste item property to work is significant, which may be a reason to stay away from perma-haste items.

Modifié par WhiZard, 24 janvier 2014 - 06:42 .


#15
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages
If you just fire a similar segment of code for the OnRespawn, Raise Dead, and Resurrection scripts that should fix the death issues removing the buffs.

#16
WhiZard

WhiZard
  • Members
  • 1 204 messages
Perhaps, but at that point it may just be easier to grant a bonus attack effect for equipping certain items rather than penalizing haste.

#17
MagicalMaster

MagicalMaster
  • Members
  • 2 000 messages
Bonus attack effect doesn't increase spellcasting speed, though, and something like giving Autoquicken III won't make feats cast faster.

#18
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
I found how do to hide the icon speed penalty...

Messabe by ShaDoOoW

"This function will hide just applied effect's icon. How it works? Simply, I apply this effect twice, first time normally, but second time with some other effect in link. And then I will strip the second linked effect, so effect icon will disappear but original effect will stay."

//nDurationType - only permanent and temporary, its logical
//may not work properly with linked effect, proper testing is recommended
void ApplyEffectToPCAndHideIcon(int nDurationType, effect eEffect, object oPC, float fDuration=0.0);
void ApplyEffectToPCAndHideIcon(int nDurationType, effect eEffect, object oPC, float fDuration=0.0)
{
ApplyEffectToObject(nDurationType,eEffect,oPC,fDuration);eEffect = EffectLinkEffects(eEffect,EffectTurnResistanceIncrease(1));
ApplyEffectToObject(nDurationType,eEffect,oPC,fDuration);
RemoveEffect(oPC,eEffect);
}


  to apply (on_player_equip) the speed decrease hide icon (already WhiZard and Squatting Monk way)

//nDurationType - only permanent and temporary, its logical
//may not work properly with linked effect, proper testing is recommended
void ApplyEffectToPCAndHideIcon(int nDurationType, effect eEffect, object oPC, float fDuration=0.0);
void ApplyEffectToPCAndHideIcon(int nDurationType, effect eEffect, object oPC, float fDuration=0.0)
{
ApplyEffectToObject(nDurationType,eEffect,oPC,fDuration);
eEffect = EffectLinkEffects(eEffect,EffectTurnResistanceIncrease(1));
ApplyEffectToObject(nDurationType,eEffect,oPC,fDuration);
RemoveEffect(oPC,eEffect);
}
void main()
{

object oItem = GetPCItemLastEquipped();

    // If the equipped item has the Haste property... 
  if (GetItemHasItemProperty(oItem, ITEM_PROPERTY_HASTE)) 
  {   
    object oPC = GetPCItemLastEquippedBy(); 
      int nItems = GetLocalInt(oPC, "HastedItems");

        // Slow him if he hasn't been slowed by another hasted item.   
    if (!nItems)     
  {           

    effect eSlow = EffectMovementSpeedIncrease(-25);                    ApplyEffectToPCAndHideIcon(DURATION_TYPE_PERMANENT, eSlow, oPC); 
      }
        // Increment a counter for hasted items in case the PC has more than one   
    SetLocalInt(oPC, "HastedItems", nItems + 1); 
  }


}


Link code social.bioware.com/forum/1/topic/192/index/3174344#3180147 

Modifié par sandronejm, 24 janvier 2014 - 08:58 .


#19
WhiteTiger

WhiteTiger
  • Members
  • 479 messages

WhiZard says...

The number of scripts needed to get the behavior for the haste item property to work is significant, which may be a reason to stay away from perma-haste items.


What do you mean with this?

Unfortunately the speed penalty does not work when:
1.Player logs out and logs in the game.
2.On respawn (don`t work).
3.Hide/Unhide.
4.On rest (don`t work after finished).

Modifié par sandronejm, 24 janvier 2014 - 08:57 .


#20
WhiZard

WhiZard
  • Members
  • 1 204 messages

MagicalMaster wrote...

Bonus attack effect doesn't increase spellcasting speed, though, and something like giving Autoquicken III won't make feats cast faster.


I was not recommending getting away from the spells and abilities that grant haste.  For them, haste can be linked to the movement speed adjustment, so that one won't disappear without the other (although the slow effect will increase its potency against hasted characters).

Item properties of haste can be much more difficult to handle as effects can get lost while properties endure.  For them placing a bonus attack might be better as errors will not suddenly produce exploits.

#21
WhiZard

WhiZard
  • Members
  • 1 204 messages

sandronejm wrote...

WhiZard says...

The number of scripts needed to get the behavior for the haste item property to work is significant, which may be a reason to stay away from perma-haste items.


What do you mean with this?

Unfortunately the speed penalty does not work when:
1.Player logs out and logs in the game.
2.On respawn (don`t work).
3.Hide/Unhide.
4.On rest (don`t work after finished).


Read the above.  I have done in depth studies in movement speed application and removal. 

Point 3 should work regardless,  as hide/unhide effetively applies and removes a movement speed effect (multiplying speed by 0.5 then recalculating the speed without the 0.5 multiplication).  Haste and 30% movement penalty result in a speed of 1.05 times normal movent (1.5 times 0.7),  with the exception of monk and barbarian who have a bug regarding their bonus being added in every time a new movement speed effect is added.  The only other avenue that could reasonably cause problems is the recursive nature of applying movement speed effects to rebound off the minimum speed cap (of 0.125 base speed) or the maximum cap (of 1.5 base speed) (e.g. 50% increase with a 50% increase and then a 30% deacrease will still net 1.05 base speed as the first increase already reached the cap).  In short I am betting the reason you are saying point three doesn't work, is because the icon for the movement speed disappears, rather than the character failing to be slowed.

Points 2 and 4 were already addressed by MM above.

For point 1 are you looking at a character that has logged off and logged back into the same server session? Or has the server reset in between loggings?

#22
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
Yes, when login/logout - in the same session - the penalty speed is removed. The character back to walk fast.

#23
Squatting Monk

Squatting Monk
  • Members
  • 445 messages
When a player logs in, the OnAcquireItem and OnPlayerEquipItem events run before even OnClientEnter. It's possible that effects aren't getting applied because the PC is not a fully valid object yet. Try DelayCommand()-ing the application of the slow effect and see if that helps any. If that doesn't work, you can loop through the PC's equipped items OnClientEnter to determine if they have any hasted items equipped and apply the slow effect there.

As others have noted, there's a lot to consider when trying to do this stuff, and it's going to be difficult to make a fool-proof system that isn't full of ugly hacks. A far simpler solution would be to remove perma-haste items from the module.

#24
WhiteTiger

WhiteTiger
  • Members
  • 479 messages
DelayCommand()-ing what's this?

1.The idea is to create a script to check on login if the player is using a Hasted item and apply the penalty speed.
2.On respawn is the same thing - checks on respawn if the player are using hasted item.
3.Hide/Unhide are OK - working.
4.On rest - checks on rest if the player are using hasted item and apply penalty.

I was testing the script with penalty Increase -75% (really slow moviment). I was fighting with a Red Dragon and a Mage... When I receive a spell damage from enemy I back to run fast (with hasted normal).
This script are very bugged. ):

Squatting Monk, I can't remove all perma-hasted items from the module. Don't we have other way to reduce speed hasted?

Modifié par sandronejm, 25 janvier 2014 - 10:24 .


#25
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
There are ways to do what you want, but they're fairly involved. Here's our sample code for mud that slows players while they're in it:

                /* Shedaklah - Gasping Crater
                 *   Slowing Mud (height 11.5)
                 */
                case 31: {
                    vector vPos = GetPosition(oTarget);
                    if (vPos.z < 11.5 && !GetCanPasswall(oTarget)) {
                        effect eEff = EffectSpellImmunity(HGSPELL_UNUSED);
                        eEff = SupernaturalEffect(eEff);
                        SetEffectSpellId(eEff, HGEFFECT_LETHARGY);

                        if (!GetHasSpellEffect(HGEFFECT_LETHARGY, oTarget))
                            FloatingTextStringOnCreature("The mud sticks to you and slows you down!", oTarget, FALSE);

                        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEff, oTarget, 12.0);
                    }
                }


Basically, we set a spell id on an otherwise unused/non-existant spell immunity effect, creating a new spell id effect. That, in itself, requires nwnx_structs, though you can get around that by using a custom effect creator object and checking something unique, like tag or resref, of GetEffectCreator. 

That's just the start, because, by itself, that effect does nothing, other than serve as a marker.

We have the following scripts in our central include:

int GetAdjustedMovementRate (int nRate, int nAdjust) {
    if (nRate == MOVEMENT_RATE_PC)
        nRate = MOVEMENT_RATE_NORMAL;

    if (nRate == MOVEMENT_RATE_DM_FAST    ||
        nRate == MOVEMENT_RATE_IMMOBILE   ||
        nRate == MOVEMENT_RATE_EXTRA_SLOW ||
        nRate == MOVEMENT_RATE_SUPER_SLOW ||
        nRate == MOVEMENT_RATE_GLACIAL)
        return nRate;

    switch (nRate) {
        case MOVEMENT_RATE_VERY_SLOW:
            nRate = 0;
            break;

        case MOVEMENT_RATE_SLOW:
            nRate = 1;
            break;

        case MOVEMENT_RATE_RELAXED:
            nRate = 2;
            break;

        case MOVEMENT_RATE_FAST:
            nRate = 4;
            break;

        case MOVEMENT_RATE_FASTER:
            nRate = 5;
            break;

        case MOVEMENT_RATE_VERY_FAST:
            nRate = 6;
            break;

        case MOVEMENT_RATE_EXTRA_FAST:
        case MOVEMENT_RATE_SUPER_FAST:
        case MOVEMENT_RATE_HYPER_FAST:
        case MOVEMENT_RATE_LIGHTSPEED:
        case MOVEMENT_RATE_RIDICULOUS:
        case MOVEMENT_RATE_LUDICROUS:
            nRate = 7 + (nRate - MOVEMENT_RATE_EXTRA_FAST);
            break;

        default:
            nRate = 3;
            break;
    }

    nRate += nAdjust;

    if (nRate > 12)
        nRate = 12;
    else if (nRate < 0)
        nRate = 0;

    switch (nRate) {
        case 0:
            nRate = MOVEMENT_RATE_VERY_SLOW;
            break;

        case 1:
            nRate = MOVEMENT_RATE_SLOW;
            break;

        case 2:
            nRate = MOVEMENT_RATE_RELAXED;
            break;

        case 4:
            nRate = MOVEMENT_RATE_FAST;
            break;

        case 5:
            nRate = MOVEMENT_RATE_FASTER;
            break;

        case 6:
            nRate = MOVEMENT_RATE_VERY_FAST;
            break;

        case 7:  /* MOVEMENT_RATE_EXTRA_FAST */
        case 8:  /* MOVEMENT_RATE_SUPER_FAST */
        case 9:  /* MOVEMENT_RATE_HYPER_FAST */
        case 10: /* MOVEMENT_RATE_LIGHTSPEED */
        case 11: /* MOVEMENT_RATE_RIDICULOUS */
        case 12: /* MOVEMENT_RATE_LUDICROUS  */
            nRate = MOVEMENT_RATE_EXTRA_FAST + (nRate - 7);
            break;

        default:
            nRate = MOVEMENT_RATE_NORMAL;
            break;
    }

    return nRate;
}

int GetBaseMovementRate (object oPC) {
    int nRate = GetLocalInt(oPC, "SubraceMovementRate");

    if (GetQuasiclass(oPC) == QUASICLASS_ACCURSED_PARIAH)
        nRate = MOVEMENT_RATE_NORMAL;
    else if (GetLocalInt(oPC, "RDDLevel") >= 30)
        nRate = MOVEMENT_RATE_FAST;

    if (nRate <= 0)
        nRate = MOVEMENT_RATE_NORMAL;

    if (GetLocalInt(oPC, "StatArtifactUsed") == HGARTIFACT_TEAR_OF_SELUNE)
        nRate = GetAdjustedMovementRate(nRate, 2);

    if (GetHasFeat(FEAT_EPIC_BLINDING_SPEED, oPC))
        nRate = GetAdjustedMovementRate(nRate, 1);
    if (GetHasFeat(HGFEAT_Y_QUICKNESS, oPC))
        nRate = GetAdjustedMovementRate(nRate, 1);

    return nRate;
}

void RecalculateMovementRate (object oPC) {
    if (!GetIsPC(oPC) || GetIsDM(oPC))
        return;

    int nRate;

    if (GetHasSpellImmunity(SPELLABILITY_DW_DEFENSIVE_STANCE, oPC)  ||
        GetHasSpellEffect(HGSPELL_VISCID_GLOB, oPC)) {
        nRate = MOVEMENT_RATE_GLACIAL;
    } else if ((GetHasSpellEffect(HGEFFECT_LETHARGY, oPC)  ||
                GetHasSpellEffect(HGEFFECT_GNOMISH_INVENTOR_KD_IMMUNITY, oPC)) &&
                GetLocalInt(oPC, "StatArtifactUsed") != HGARTIFACT_TEAR_OF_SELUNE) {
        nRate = MOVEMENT_RATE_SLOW;
    } else {
        nRate = GetBaseMovementRate(oPC);

        if (GetHasSpellEffect(SPELL_EXPEDITIOUS_RETREAT, oPC) || GetHasSpellEffect(SPELL_FREEDOM_OF_MOVEMENT, oPC))
            nRate = GetAdjustedMovementRate(nRate, GetLocalInt(oPC, "MovementRateBonus"));
    }

    if (nRate == MOVEMENT_RATE_NORMAL)
        nRate = MOVEMENT_RATE_PC;
    SetMovementRate(oPC, nRate);
}


These moverate consts are in nwnx_functions:

const int MOVEMENT_RATE_PC          = 0;
const int MOVEMENT_RATE_IMMOBILE    = 1;
const int MOVEMENT_RATE_VERY_SLOW   = 2;
const int MOVEMENT_RATE_SLOW        = 3;
const int MOVEMENT_RATE_NORMAL      = 4;
const int MOVEMENT_RATE_FAST        = 5;
const int MOVEMENT_RATE_VERY_FAST   = 6;
const int MOVEMENT_RATE_DEFAULT     = 7;
const int MOVEMENT_RATE_DM_FAST     = 8;
const int MOVEMENT_RATE_FASTER      = 9;
const int MOVEMENT_RATE_EXTRA_FAST  = 10;
const int MOVEMENT_RATE_SUPER_FAST  = 11;
const int MOVEMENT_RATE_HYPER_FAST  = 12;
const int MOVEMENT_RATE_LIGHTSPEED  = 13;
const int MOVEMENT_RATE_RIDICULOUS  = 14;
const int MOVEMENT_RATE_LUDICROUS   = 15;
const int MOVEMENT_RATE_RELAXED     = 16;
const int MOVEMENT_RATE_EXTRA_SLOW  = 17;
const int MOVEMENT_RATE_SUPER_SLOW  = 18;
const int MOVEMENT_RATE_GLACIAL     = 19;


We simply recalc the movement rate for the pc at certain places in the mod:

fky_deathprocess (56):     RecalculateMovementRate(oPC);
fky_restprocess (264):     RecalculateMovementRate(oPC);
hgs_viscidglob (64):                 RecalculateMovementRate(si.target);
hg_inc (3661): void RecalculateMovementRate (object oPC) {
hg_mod_heartbeat (36):             RecalculateMovementRate(oPC);
nw_s0_freemove (107):         DelayCommand(0.5, RecalculateMovementRate(si.target));
qc_gi_inc (642):                 DelayCommand(0.1, RecalculateMovementRate(oTarget));
x0_s0_shield (47):     DelayCommand(0.1, RecalculateMovementRate(si.caster));


fky_deathprocess is called whenever a pc is brought back to life. It's essentially a 'rez event', and you'll need one to do this. fky_restprocess is the same, though that's a convenience script, because we sometimes call it other than when a pc rests (ForceRests, among other things):

ag_wandering_mon (92):         ExecuteScript("fky_restprocess", oPC);
asmoset001 (26):             ExecuteScript("fky_restprocess", oPC);
biorejuvenator (20):         ExecuteScript("fky_restprocess", oPC);
fky_chat_dm_comm (1140):                             if ((!VerifyDMKey(oDMTarget)) && (!VerifyAdminKey(oDMTarget)) || (oDMTarget == oDMPC)) {ForceRest(oDMTarget); ExecuteScript("fky_restprocess", oDMTarget);}
hellhiduse (105):     ExecuteScript("fky_restprocess", oPC);
hgll_start_dlg (9): - old altar had forced rest and fky_restprocess onenter, omitted from this setup,
hg_inc (4302):     ExecuteScript("fky_restprocess", oPC);
legendaltarlimit (14):         ExecuteScript("fky_restprocess", oPC);
paragon_spell (1477):     /* counter spell, recalc done each rest in fky_restprocess */


Firing RecalculateMovementRate in the module heartbeat, by the way, also prevents a known speed exp%lo@it. The loop is straightfoward enough:

void main() {
    object oMod = GetModule();
    object oMes = GetMessenger();
    int nUptime = GetLocalInt(oMod, "uptime");
    int nMemory = GetProcessMemoryUsage();
    int nMessages = 0, nPlayers = 0;
    string sServer = GetLocalString(oMod, "ServerNumber");
    string sBootTime = IntToString(GetLocalInt(oMod, "boottime"));

    {
        object oPC;

        for (oPC = GetFirstPC(); GetIsObjectValid(oPC); oPC = GetNextPC()) {
            nPlayers++;
            RecalculateMovementRate(oPC);
            RecalculateDexModifier(oPC);

            int nAlarm = GetLocalInt(oPC, "AlarmUptime");
            if (nAlarm > 0 && nAlarm <= nUptime) {
                DeleteLocalInt(oPC, "AlarmUptime");
                SendChatLogMessage(oPC, C_PINK + "[Alarm] " + GetLocalString(oPC, "AlarmMessage") + C_END, oMes, 4);
            }
        }

        SetLocalInt(oMod, "ServerPlayers", nPlayers);
    }


All of this relies on SetMovementRate, which can be found in both nwnx_funcs and nwnx_functions (pretty sure the functinos one is deprecated, though).

Like I said, involved, but there's no simpler way that actually works in all cases.

Funky