Reversing Morrowind's formulas

Everything about development and the OpenMW source code.
User avatar
scrawl
Posts: 2152
Joined: 18 Feb 2012, 11:51

Re: Reversing Morrowind's formulas

Post by scrawl »

I just researched it recently for an MCP patch on creature voiceovers. It's still early research. Voice wiki page. Note that creatures can't use this in vanilla, their audios sources are SoundGens.
I think the x < roll part should be roll < x instead.

Is the roll 10000 an integer? That would mean if the frame is shorter than 0.1 seconds, idle voices never trigger. I think a proper implementation should use a roll[0..1]*10000 instead.
User avatar
Hrnchamd
Posts: 64
Joined: 11 Aug 2012, 00:48

Re: Reversing Morrowind's formulas

Post by Hrnchamd »

scrawl wrote: I think the x < roll part should be roll < x instead.

Is the roll 10000 an integer? That would mean if the frame is shorter than 0.1 seconds, idle voices never trigger. I think a proper implementation should use a roll[0..1]*10000 instead.
Yes, it should be roll < x. Your second part is not quite on target though.

roll x returns in the interval [0, x), and in this case a roll of 0 will always trigger. It is not equivalent to a floating point roll due to quantization, e.g. at 120 fps an integer roll triggers at p = 1/10000, but a floating point roll triggers at p = 1/120000. Instead it should be done in a framerate independent cycle, maybe every 0.1 sec, with an adjusted roll calibrated on the quantized behaviour of vanilla at a standard fps, maybe roll float (10000 * 60fps * 0.1s).
User avatar
scrawl
Posts: 2152
Joined: 18 Feb 2012, 11:51

Re: Reversing Morrowind's formulas

Post by scrawl »

roll x returns in the interval [0, x), and in this case a roll of 0 will always trigger. It is not equivalent to a floating point roll due to quantization, e.g. at 120 fps an integer roll triggers at p = 1/10000, but a floating point roll triggers at p = 1/120000. Instead it should be done in a framerate independent cycle, maybe every 0.1 sec, with an adjusted roll calibrated on the quantized behaviour of vanilla at a standard fps, maybe roll float (10000 * 60fps * 0.1s).
This is very confusing, but your idea seems correct.
Let's see vanilla behaviour at 60 fps.
With the default fVoiceIdleOdds (10), 10 * (1/60.f) = 0.16 requires a roll of 0 to succeed. (1/10000 chance per 1/60 seconds, i.e. 0.006 voices per second).
Due to the quantization issues, increasing fVoiceIdleOdds by say, a factor of 2, won't actually have an effect (you'd have to increase it by a factor of at least 6). Likewise, reducing fVoiceIdleOdds will not have an effect either.
Are there any known mods that change fVoiceIdleOdds?

Goal 1: make the chance FPS independent.
Goal 2: make changes to fVoiceIdleOdds work properly, with no gaps in between.
Goal 3: for the default value of the GMST, have the same frequency of voices as vanilla would have in a fixed 60 fps setting.

So, assuming we run the check at a fixed timestep INTERVAL of 0.1s.

Code: Select all

x = fVoiceIdleOdds * 0.6
if (roll float (10000) < x)
   trigger idle voice
Should yield a chance of 6 / 10000 every 0.1s, i.e. 0.006 voices per second.

And since there's no quantization left here, we don't actually need to use a fixed timestep either.

Code: Select all

x = fVoiceIdleOdds * 0.6 * (frametime / 0.1)
if (roll float (10000) < x)
   trigger idle voice
This seems about right to me in addressing all the 3 goals. Did I make any mistakes? Edit: of course I did... corrected silly mistake.
User avatar
Hrnchamd
Posts: 64
Joined: 11 Aug 2012, 00:48

Re: Reversing Morrowind's formulas

Post by Hrnchamd »

Aha, you edited it already. You achieved all the goals with that. I can only add there is no particular need to run the test every frame if it saves CPU resources, yet it needs to be low enough that NPCs don't sync up voices. It depends on how your AI is scheduled.
User avatar
Logitech
Posts: 13
Joined: 01 Feb 2013, 03:59

Re: Reversing Morrowind's formulas

Post by Logitech »

Currently OpenMW uses following formula for attack fatigue cost:

Code: Select all

fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult + weaponWeight * attackStrength * fWeaponFatigueMult
with this comment:
// somewhat of a guess, but using the weapon weight makes sense
According to scrawl it was indeed a guess at the time. Any chance for a confirmation that this one is accurate?

edit: btw. OpenMW wiki has wrong formula for Fatigue regeneration:

Code: Select all

if currentFatigue < maxFatigue:
    x = fFatigueReturnBase + fFatigueReturnMult * endurance
    recover (frameTime * x) fatigue
as far as I unerstand OpenMW code it is implemented like this:

Code: Select all

fFatigueReturnBase + fFatigueReturnMult * fEndFatigueMult * endurance
this is also correct for vanilla Morrowind, right?[/s]
Last edited by Logitech on 31 Jan 2015, 23:38, edited 1 time in total.
User avatar
scrawl
Posts: 2152
Joined: 18 Feb 2012, 11:51

Re: Reversing Morrowind's formulas

Post by scrawl »

edit: btw. OpenMW wiki has wrong formula for Fatigue regeneration:
There are two different formulas for fatigue regeneration, one used for resting and one for realtime gameplay. I think you are getting them mixed up. Both the wiki and the OpenMW implementation are accurate to MW to my knowledge.
User avatar
Logitech
Posts: 13
Joined: 01 Feb 2013, 03:59

Re: Reversing Morrowind's formulas

Post by Logitech »

yep, I missed the resting section. Thank you for clarifying this.
User avatar
scrawl
Posts: 2152
Joined: 18 Feb 2012, 11:51

Re: Reversing Morrowind's formulas

Post by scrawl »

I have some research requests on the nif format.

I've been looking at NiPlanarCollider. With some trial and error, I figured out the important parts:

Code: Select all

        mBounceFactor = nif->getFloat(); // A collided particle keeps mBounceFactor*vel of its original velocity
        /*unknown*/nif->getFloat();

        for (int i=0;i<10;++i)
            /*unknown*/nif->getFloat();

        // Collision plane (particle space?)
        mPlaneNormal = nif->getVector3();
        mPlaneDistance = nif->getFloat();
Any idea what the rest could be?

Second, I wonder why NiGravity is not working accurately, neither in OpenMW nor in NifSkope. Are there any magic factors or normalization errors in the original?
User avatar
Hrnchamd
Posts: 64
Joined: 11 Aug 2012, 00:48

Re: Reversing Morrowind's formulas

Post by Hrnchamd »

scrawl wrote:I have some research requests on the nif format.

I've been looking at NiPlanarCollider. With some trial and error, I figured out the important parts:
...
Any idea what the rest could be?

Second, I wonder why NiGravity is not working accurately, neither in OpenMW nor in NifSkope. Are there any magic factors or normalization errors in the original?
For documentation purposes:
Nif particles are based on the 3ds max SuperSpray particle type. Particle effectors are called "space warps". Documentation for the planar collider and gravity space warp can help identify unknown fields, though things may have changed since Morrowind's release.


Further research as Morrowind implements them:
NiPlanarCollider is a finite-size laminar collider. The serialization format is:

Code: Select all

float restitution;
float width, length;
vector3 centre;
vector3 lengthAxis;
vector3 widthAxis;
vector4 planeEquation;
Particles only collide if they pass the plane equation test and are within the correct bounds:
w = dot(particle.position - centre, widthAxis)
l = dot(particle.position - centre, lengthAxis)
collide if (-0.5*width <= w <= 0.5*width) and (-0.5*length <= l <= 0.5*length)

NiGravity is a global force effector with a configurable falloff. The serialization format is:

Code: Select all

float decay;
float force;
int type;
vector3 position, direction;
The actual force applied is 1.6 * calculated force in all cases, reason unknown. If decay == 0.0, there is no falloff, otherwise the force applied is multiplied by exp(-decay * distance).
User avatar
scrawl
Posts: 2152
Joined: 18 Feb 2012, 11:51

Re: Reversing Morrowind's formulas

Post by scrawl »

Great info as always, thanks! I just implemented the decay factor - it was used in the vanilla files after all, e.g. in bloodsplat.nif.

Someone just brought to my attention a problem with the persuasion mechanics - in vanilla MW, an increase in Speechcraft results in more disposition change per attempt. Though with the formula on the wiki, the maximum change is capped by iPerMinChance:

Code: Select all

target1 = max(iPerMinChance, target1)
I presume this should be a min?
Post Reply