Walking & Running

Any way to tell which one the PC is doing and control their speed? I know there are very few functions for this and don’t hold out much hope, but figured I’d ask the experts here to get a final answer. Thanks!

Assuming NWN1, there is no NWScript function to tell if a creature is walking or running. GetCurrentAction is not that helpful since moving with WSAD keeps action queue empty. And GetMovementRate is useless for this.

It is however possible to gauge the speed by, well, measuring it in a low-latency loop.

This is CPU-intensive so apply only to few targets (i.e. PCs only).

The general idea:

  1. get previous location of PC
  2. get current location of PC
  3. GetDistanceBetweenLocations and divide it by a time delta
  4. save the calculated speed in a local float variable
  5. wait time delta, loop back to step 1

The code:

const float DELAY = 0.1; // measurement delay [s]
const string VAR_SPEED = "speedometer_speed";
const string VAR_STATUS = "speedometer_status";
const string VAR_LOCATION = "speedometer_location";

void speedometer(object oCreature=OBJECT_SELF)
{
    location lLocation;
    float fDistance;

    // if status variable is OFF - return
    // (this sentinel allows to turn off
    // the speedometer - very important)
    if(!GetLocalInt(oCreature, VAR_STATUS))
    {
        return;
    }

    // get current location
    lLocation = GetLocation(oCreature);

    // get distance between current and last location
    fDistance = GetDistanceBetweenLocations(lLocation,
        GetLocalLocation(oCreature, VAR_LOCATION));

    // save current location as last location
    SetLocalLocation(oCreature, VAR_LOCATION, lLocation);

    // save current speed (distance / delay)
    SetLocalFloat(oCreature, VAR_SPEED, fDistance / DELAY);

    // run this function again after the same delay
    DelayCommand(DELAY, speedometer(oCreature));
}

To start the measurement, execute this somewhere (once):

// allow the speedometer to run
SetLocalInt(oPC, VAR_STATUS, TRUE);
// initialize previous position
SetLocalLocation(oPC, VAR_LOCATION, GetLocation(oPC));
// start the loop
speedometer(oPC);

Now you can tell the speed (in m/s) from any other script:

float fSpeed = GetLocalFloat(oPC, VAR_SPEED);

Determine which value range means walking empirically (use creaturespeed.2da).

To turn off the speedometer, execute:

// signal loop break
DeleteLocalInt(oPC, VAR_STATUS);
// cleanup
SetLocalFloat(oPC, VAR_SPEED, 0.0);

EDIT: made the code a bit more readable.

1 Like

Thanks for the pointers! Yet another example to put into my library. I know the module owner wants to be able to track all the PCs, so this is likely a non-starter, but I like the idea.

One other question in case you know … I know the movement speeds are sourced from a 2da file. Is there anyway to create a custom entry there with desired walk/run speeds for our PCs without creating a bunch of unintended consequences?

Thanks!

All PCs should be alright. Just don’t apply this to all creatures indiscriminately. And don’t forget to turn it off when not needed. A delay of 0.1 is manageable by the engine. Lag appears when you go below 0.01 (see here - may be quite useful), unless something high-AI is happening, like a large battle.

About 2da, you can do this:

  1. add a new row in creaturespeed.2da:
    9 XFast 6893 XFAST 5.00 10.00
    Here I make an extra fast entry (walk 5, run 10)
  2. duplicate appearance.2da rows for playable race (i.e. human) and change the string in MOVERATE column to XFAST
  3. in game, call SetCreatureAppearanceType(oPC, <new 2da row>)
  4. wait for models to re-load (this will annoy the players and is really visible)
  5. profit:

As you can see, the actual speed may vary from the 2da value(s). At least for PCs.

I can verify that. In my module’s creaturespeed.2da, I have “flying” creatures that move 5 times faster than the PC (10 20) with no problems at all.

Awesome, thanks for the help! I’ll get to work on this in the next couple of days and see what I can make happen. Haven’t delved into Heartlib yet, but it looks like it’ll work for a lot of things the module owners are requesting. Thanks again!