This has grown kinda messy but includes a lot of info that I don't want to loose. Instead I'll summarize it all into a new HumanStaminaModel page.

Mod Components

I'm not entirely sure how best to break down this mod into separate components. I have 3 approaches;

Legacy Componets

This approach is what was used for 2.0-beta1 and 2.0-beta2. It is designed around extra layers of complexity, functionality, and overheads. It does not nicely group together related functionality, and made me compromise some ideas because of the seperation;

  • RealisticFatigue.esp - immersive pant/blur and game settings
  • RealisticHealth?.esp - extra visual effects for low health and disease, with extra disease fatigue affects
  • RealisticRunning?.esp - scripted ability for all actors that adds fatigue drains/burns, trip/stagger, panting, and modifies movement.
  • RealisticFatigue-(hard|easy).esp extra optional setting tweaks.

Functional Components

This is the architecture I proposed in the tsnexus comments for the mod. It groups together related functionality so it's easier to select which functionality you want. It also groups related functionality together making it easier to implemente some things;

  • RFSettings?-(easy|normal|hard).esp - game settings only that modify gameplay without any scripting or overheads.

  • RFIndicators?.esp - Immersive health/fatigue/disease indicators that only provide visual and audio indications without affecting gameplay.
    • uses an OBGE shader and maybe SoundCommands? plugin for extra visual and audio affects
    • also uses hitshader blur and playsound pant.
    • also includes optional extra fatigue/health burns for disease.
  • RFAffects?.esp - The scripted ability for all actors that adds fatigue drains/burns, trip/stagger, panting, and modifies movement.

Despite this breakdown, there are some things that still overlap between the different components... maybe the disease fatigue/health burns should also be an ability that is applied to other actors? Should the RFAffects?.esp pant be the same as the RFIndicators? pant... the player can do lip-sync panting too.

Monolithic single component

This would combine RFIndicators?.esp and RFAffects?.esp into a single RealisticFatigue.esm, and rely on console settings to turn on/off features. It would still use a RealisticFatigue-(easy|normal|hard).esp for the game settings.

Fatigue Modelling

After beta1 I decided I needed to do some more research on human running, jumping, climbing, encumbrance, and endurance. I found the following useful references;

[REF1]A Dynamical Model of Muscle Activation, Fatigue, and Recovery. - a model of how muscles activate, fatigue, and recover.
[REF2]A new model predicting locomotor cost from limb length via force production. - a model of how much energy is burned by running and walking.
[REF3]Energy cost of walking and running at extreme uphill and downhill slopes. - A study on the affects of gradients.
[REF4]Mechanical determinants of gradient walking energetics in man. - An analysis of positive and negative work in walking on gradients.
[REF5]Optimization of energy expenditure during level walking. - Another study on walking energy.
[REF6]Energy cost of running. - Another study on running energy.
[REF7]http://www.kingsown.ca/equipment.htm - summary of modern infantry operating equipment weights.
[REF8]http://www.geocities.com/Paris/Salon/2385/armour.html - a discussion of medieval equipment weights.
[REF9]http://www.pbm.com/~lindahl/cariadoc/shield_and_weapon_weights.html - a discussion of shield and weapon weights.

In summary, muscles are like "force engines". The energy burned is proportional to the force they generate. The maximum force they can generate is inversely proportional to the fatigue levels. Muscles fatigue at a rate proportional to the force they generate. Fatigued muscles regenerate at a rate proportional to the fatigue level.

This means the force you can generate (and hence how fast you run, how high you jump, how hard you hit) decreases linearly with fatigue. This matches nicely with Oblivion's FatigueFactor? used in the damage formula. Unfortunately Oblivion does not reduce running/jumping with fatigue.

Fatigue should regenerate at a rate proportional to how low your fatigue is... ie it should recover fast when your fatigue is low, and recover slower when your fatigue is high... recovering from 0 fatigue should start fast, and the slow down as you approach 100%. This does not match Oblivions fFatigueReturn* approach, which has a constant recovery rate.

Fatigue should burn at a rate proportional to how much fatigue you have... high fatigue means you are hitting/running/jumping harder and hence burning fatigue faster... as you get tired you get weaker so your feeble jog/hop/hit burns less fatigue. This does not match Oblivions fFatigueRun fFatigueJump etc model, which is constant fatigue burn irrespective of how hard you hit.

Since the max force is proportional to fatigue, how much you can lift depends on your fatigue. Load bearing occupies some of the available max force, reducing the amount of remaining force available for running/jumping. When carring your max load (based on the fatigue level), there is no spare force left for locomotion, hence the "pinned to the spot" effect. This means encumbrance should reduce running speed and jumping height. This does not match Oblivions encumbrance model, which only supports the "pinned to the spot" behaviour, and does not adjust max encumbrance based on fatigue.

There is very little information on the energy required to bear a static load. It requires a "stabilising" force to maintain stiffness with most of the load actually carried by the bones, which may mean the muscle force required is non-linear with the load. It is also an isometric force (non-moving), which has different energy/fatigue burn rates compared to dynamic forces. The run/walk model has power proportional to force * stride rate. With isometric forces there is no "rate". One reference I found mentioned that dynamic forces burn 2x as much energy as isometric forces, but if dynamic energy is proportional to rate, what rate do we use for constant isometric forces? Dynamic movements like running use force for half of each cycle, whereas lifting requires constant force, so they probably burn energy at the same rate. I may need to find more references on energy burned bearing static loads. For now assume standing requires force proportional to encumbLevel^2 and burns fatigue at the same rate as running/walking for the same amount of force required.

With running we can assume you always want to run as fast as you can, and hence use the "max-force" available based on your fatigue level. Walking is complicated because it is not a "max-force" activity... we are not moving as fast as we can. Walking also has a non-linear V^2 energy curve. How should we adjust walking speed for "fatigue". My it looks like having walking speed decline as the square-root of fatigue on the basis that walking speed should try to maintain the "sweet spot" compromise between minimizing energy burn and covering distance. This also nicely matches running speed declining towards walking speed as encumbrance increases.

Fatigue Burn/Return

The fatigue burn/return equations from [REF1] can be simplified by assuming that the "B" muscle activation rate is infinite, so that there is no delay in muscle activation and we can instantly activate as many non-fatigued muscle as we like. Assuming we always want to apply 100% effort to hit/run/jump as hard as we can, and mapping things to Oblivion Fatigue Game Settings::

Ma is muscles activated
Mf is muscles fatigued
M0 is total muscles

fatigueLevel = 1.0 - fatigue/fatigueBase
fatigueFactor = fFatigueBase - fFatigueMult * fatigueLevel
fFatigueBase = 1.0           ; max fatigueFactor value, assume this never changes

M0 = 1.0
Ma = M0 - Mf = FatigueFactor
Mf = M0 - Ma = 1.0 - FatigueFactor

dMf/dt = F*Ma - R*Mf = fatigueRun * FatigueFactor - fatigueReturn * (1.0 - FatigueFactor)

fatigueReturn = R = fFatigueReturnBase + fFatigueReturnMult * endurance
fatigueRun = F = (fFatigueRunBase + fFatigueRunMult * encumberance/MaxEncumberance) * fPerkAthleticsXXFatigueMult

So, each frame we need to adjust the fatigue burn applied by Oblivion to reduce the fatigue return and burn based on FatigueFactor?. It is more convenient to scale fatigueReturn by fatigueLevel so that return rate is 100% when fatigue=0 irrespective of fFatigueBase and fFatigueMult settings. This gives us::

fatigueReturnAdj = -fatigueReturn * (1-fatigueLevel)
fatigueRunAdj = fatigueRun * (1-FatigueFactor)
fatigueAdj = (fatigueReturnAdj + fatigueRunAdj)*dt

Reduction of fatigue burned based on FatigueFactor? should only be done if the hit/run/jump effectiveness is reduced by FatigueFactor?. In vanilla, hit damage is reduced by fatigue factor, so theoretically fatigue burned should also be reduced. Unfortunately, it is hard for a mod to detect when hits and powerhits are performed, so I dunno if it is feasable to dynamically adjust fatigue burned by attacking. In vanilla, run/jump are not modified by FatigueFactor?, and neither is the fatigue burned. Realisicly, run/jump and the fatigue they burn should be adjusted by FatigueFactor?, and fortunately it is easier for a mod to do this.

Encumbrance

It is non-trivial to correlate Oblivion encumbrance to carried weight and how this relates to body weight and fatigue. My current estimate is 4 feathers = 1Kg. A full steel armour+sheild/weapons/misc loadout for vanilla is about 100 (armour+shield) + 40 (weapons) + 10 (misc) = 150 feathers or 37Kg, which sounds about right for a modern high-end operational load, and matches the 27+4Kg + 4Kg + 2Kg = 37Kg documented for medieval equipment in [REF8] and [REF9]. However, note that the armor/shield/weapon weight breakdowns do not match, and for higher-end armours and weapons the weights in vanilla very quickly get rediculous. A full set of Orc-level gear is ~200feathers or 50Kg, and a full set of daederic is ~300feathers or 75Kg.

Ideal operating weights are quoted as 1/3 body weight, but it looks like modern soldiers have an upper operational weight of 1/2 body weight according to [REF7]. Body weight and strength are known to be correlated. Assuming a typical male with strength 60 weighs 80Kg, ideal/max operating weight is 27/40Kg or 107/160 feathers. Assuming 100 strength corresponds with modern body-building and powerlifting champions, they would weigh 125~130Kg, for ideal/max operating equipment weight of 43Kg/65Kg or 170/260feathers. Note that body weight in Kg ~= 1.24x or 1.33x strength.

Max possible lifting weights are much higher than max operating weight, and the current deadlift world record is 400Kg! However, a more realistic lift to measure the limits at which movement is no longer possible is between a "snatch" and "clean-and-jerk", which the current world record is 215Kg=860feathers and 260Kg=1040feathers, or between 1.6x and 2x the estimated 100 strength's 130Kg body weight. It is questionable whether you could even begin to move at these loads. So at 100% encumbrance for 100 strength, total 'M' = 2.5x 125Kg body weight. It also means ideal/max operating weights are 22%/33% encumbrance, so you really want to stay below 1/3 encumbrance. This means you need to be 60 strength to operate in 150feathers of steel, 80 strength to operate in 200feathers of orcish, and 120 strength to operate in 300feathers of daederic. Note that Fperk*ArmorXXSpeedMult? armour perks can bring this down a bit, but note the vanilla settings are very unrealistic.

Since fatigueFactor is used to modify how hard you hit etc, it is convenient to treat encumbrance as a "fatigue drain" that reduced your available fatigue. The idea behind this is muscles used for carrying encumbrance are "preoccupied" and cannot contribute to running/jumping/hitting. The best way to apply these drains are with a fast modav2 like is done for low health, as this allows magic to "temporarily boost" past these limitations. It also nicely means collapse happens at zero fatigue regardless of encumbrance level. This gives::

encumbLevel = encumbrance/(fActorStrengthEncumbranceMult*strength)
encumbTotal = encumbBase + encumbMult*encumbLevel
encumbBase = 1.0                             ; normalised body weight, assume this never changes
encumbMult = fActorStrenghtEncumbranceMult/5 ; assuming body weight at 100 strength = 500 feathers
           = 2.0*fFatigueMult                ; encumb/bodyweight at which fatigueLimit = 0.0, assuming max lift = 2x bodyweight
fatigueLimit = (1.0 - encumbLevel^2)*fatigueBase

We will derive encumbMult from fActorStrenghtEncumbranceMult since this is the more direct weight relationship. If you impose this fatigueLimit, then remember that the real total available energy for running and lifting is::

Eavail = fatigueFactor + fFatigueMult*encumbLevel^2

Ideally Eavail is what should be used for modifying fatigueReturn rates, since the fatigueLimit imposed by encumbrance increases fatigueLevel but doesn't really increase Mf and hence shouldn't increase fatigue return. However, for simplicity this can probably be ignored.

In addition to the fatigueLimit, lifting will also burn fatigue proportional to encumbLevel^2::

Kstand = fFatigueMult = encumbMult/2.0
Estand = Kstand*encumbLevel^2

Different settings for different encumbMult values assuming 125Kg=500feathers body weight at 100 strength are;

encumbMult 1.0 1.6 2.0
fFatigueMult 0.5 0.8 1.0
fActorStrengthEncumbranceMult 5 8 10
33% body weight encumbLevel 33% 22% 16%
50% body weight encumbLevel 50% 33% 25%

We can define the Oblivion settings relationship to reality

encumbMult :- the "bodyweight" carried when at full encumbrance. This dictates how much encumbrance affects run/jump scaling and fatigue burn rate.

fFatigueMult :- the amount of unfatigued muscles left when fatigue is zero This is how hard you can still hit/run/jump when the fatigue meter is at zero.

fActorStrengthEncumbranceMult / encumbMult :- bodyweight in feathers per point of strength. This is mostly about how much a feather really weighs.

Note that below we discover that there is the following relationship between encumbMult and other fatigue settings::

runMult = fMoveRunMult + fMoveRunAthleticsMult
fFatigueRunBase = fatigueBurn      ; the base burn rate
fFatigueRunMult = 0.85 * encumbMult * fFatigueRunBase
fFatigueWalkBase = 0.25 * fFatigueRunBase * (4/runMult)^3
fFatigueWalkMult = 0.7 * encumbMult * fFatigueWalkBase
fFatigueStandBase = 0.0
fFatigueStandMult = 0.5 * encumbMult * fFatigueRunBase
fFatigueJumpBase = 10.0 * fFatigueRunBase * fJumpHeightMax/(fMoveCharWalkMax * runMult)
fFatigueJumpMult = encumbMult * fFatigueJumpBase

Note that with R/F=1/3 and realistic fatigue burn/return, fatigue will approach 1/4. With encumbMult=2.0, 50% encumbrance or 1x body weight will drain 1/4 of fatigue. This means that anything over 1x body weight is unsustainable, as your fatigue will drop below what is required to lift that much.

Running and Walking

The [REF2] has formulas for run/walk cost that can be approximated and simplified to::

Elimb = Klimb*f*a*(1-(f0/f)^2) = Klimb*f*a*(1-(V0/V)^2)
Erun = Krun'*M*V*(1+a/2)/a + Elimb ~= Krun*M*V + Klimb*V/2
Ewalk = Kwalk'*f*M*V^2*(1+a/2) + Elimb ~= Kwalk*M*V^3 + Klimb*V

Where we assume stride angle 'a' is less than 45 degrees which allows the following approximations and assumptions::

cos(a/2) = 1
sin(a/2) = tan(a/2) = a/2
stride angle 'a' is relatively constant
(f0/f)^2 = (V0/V)^2 is much less than 1
for walking f*a = V
for running f*a = V/2 because we spend 50% of our time in the air.

The model reports walking and running have the same energy burn at about 2.5m/s, or 1/4 max running speed of 10m/s. The comfortable walking speed is 1.5m/s, or about 1/6 max running speed.

For running, the vertical/horizontal/limb energy percentages stayed relatively constant for different speeds at around 64/20/16. These figures give us a/2=20/64=0.31 radians, and f=2.5/(2*0.31*0.95)=4.2/sec. That limb energy percentages stayed fairly constant shows that the (f0/f)^2 term is not significant. For Erun=1, V=1, M=1, we get Krun=0.84, Klimb=0.16.

At the 1/4 walking speed, energy percentages are 50/21/29, giving a/2=21/50=0.42 radians, f=2.5/(0.42*0.95m)=6.3. At the 1/6 walking speed, energy percentages are 60/25/15, giving a/2=25/60=0.42, f=1.5/(0.42*0.95m)=3.8. This shows that a/2 is relatively constant above 1/6 walking speed. I suspect 0.42 is a max stride length that is hit at the comfortable 1/6 walking speed, and to go faster than that we must increase our step frequency 'f' at high energy cost. That limb energy percentage increases with velocity tells us that the (f0/f)^2 term is significant at 1/6 speeds, but probably vanishes by the 1/4 speed. Using the un-encumbered Erun=Ewalk at V=1/4, and using an aproximate 50/20/30 walk energy ratio we get::

M = 1
V = Erun = Ewalk = 1/4
1/4 = Kwalk*(1/4)^3 + Klimb*(1/4)
1 = Kwalk/16 + Klimb = 0.7 + 0.3
Klimb = 0.3
Kwalk = 0.7 * 16 = 11.2
Krun = 1 - Klimb/2 = 0.85

Plugging these back into Ewalk using the V=1/6 speed and 60/25/15 walk energy ratio, we can figure out V0::

Ewalk = Kwalk*(1/6)^3 + Klimb*V*(1 - (V0/V)^2)
      = 0.052 + 0.3*1/6*(1 - (6*V0)^2) = Ewalk*0.85 + Ewalk*0.15
Ewalk = 0.052/0.85 = 0.061
Elimb = Ewalk*0.15 = 0.009 = 0.3/6*(1 - (6*V0)^2)
v0^2 = (1 - 6*0.009/0.3)/6
V0 = 0.15

Which shows that V0 at which Elimb becomes negligable is pretty close to 1/6, however, because the vertical and horizontal energy is declining as V^3, Elimb still counts for 15%. This also shows how Ewalk at V=1/6 becomes very small. For V<1/6 it would be OK to assume Elimb is negligable. For V>1/4 you can assume Elimb=Klimb*V.

The mass 'encumbTotal' includes both body and carried weight. Note that these formula's imply that energy burned is 0 if velocity is zero, which does not take into account high encumbrance eventually "grinding you to a halt". The documented run/walk models talk about ignoring the base energy required for standing. For our purposes we want to include the extra energy required for just carrying a load, so we have an additional Estand burn applied as defined above. This gives::

Krun = 0.85                         ; from model
Klimb = 0.3                         ; from model
Kwalk = 11.2 = 0.7 * 4^2            ; from model
Kstand = fFatigueMult               ; for max 2x bodyweight lift.
Estand = Kstand*encumbLevel^2
Erun = Krun*encumbTotal*Vrun + (Klimb/2)*Vrun  = (0.85*(1 + encumbMult*encumbLevel) + 0.15)*Vrun = (1.0 + Krun*encumbMult*encumbLevel)*Vrun
Ewalk = Kwalk*encumbTotal*Vwalk^3 + Klimb*Vwalk

Putting Vrun=1.0 and Vwalk=1/4 and then simplifying to assume Ewalk is proportional to walkScale^3 and runMult ~= 4 gives::

Vrun = runScale
Vwalk = walkScale/runMult
Erun = (1.0 + 0.85*encumbMult*encumbLevel) * runScale
Ewalk = (1.0 + 0.7*encumbMult*encumbLevel)/4 * (4/runMult)^3 * walkScale^3
Estand = 0.5 * encumbMult * encumbLevel^2

Then putting Erun=fatigueFactor and Ewalk to fatigueFactor/4 * (4/runMult)^3 to get variable movement scaling factors::

runScale = fatigueFactor/(1.0 + 0.85*encumbMult*encumbLevel)
walkScale = (fatigueFactor/(1.0 + 0.7*encumbMult*encumbLevel))^(1/3)

After doing this model and using it for RF upto v2.1, I found [REF5] and [REF6] based on measuring subjects that suggest that Ewalk, at least within the normal walking ranges, scales with V^2, not V^3. They report that at 1.1~1.3m/s ~= 1/8*Vmax, Ewalk ~= 1/2*Erun which requires the V^2 relationship if Ewalk/m ~= Erun/m at ~2.5m/s ~= 1/4*Vmax. This means you can use a highly simplified but pretty accurate model of::

Estand = fFatigueMult*encumbLevel^2
Erun = Krun*encumbTotal*Vrun = fatigueFactor
Ewalk = Kwalk*encumbTotal*Vwalk^2 = Erun/runMult
runScale = fatigueFactor/encumbTotal
walkScale = runScale^(1/2)
Vrun = runScale
Vwalk = walkScale/runMult

Where::

Krun = 1.0
Kwalk = 4*Krun = 4.0

Movement Scaling

When using only game settings, movement speeds are fixed. It may be best to set max run-speed below the realistic max for a fatigued jog. We want to get the game engine to calculate the burn from just running, and we can calculate additional burns to apply for encumbrance and walking. Using the advanced equations above we get::

runMult = fMoveRunMult + fMoveRunAthleticsMult
runScale = walkScale = 1.0
runBurn = fFatigueRunBase + fFatigueRunMult * encumbLevel = fatigueBurn * (1.0 + 0.85*encumbMult*encumbLevel)
walkBurn = fFatigueWalkBase + fFatigueWalkMult * encumbLevel = 0.25 * fatigueBurn * (4/runMult)^3 * (1.0 + 0.7*encumbMult*encumbLevel)
standBurn = fFatigueStandBase + fFatigueStandMult * encumbLevel^2 = fatigueBurn * (0.0 + 0.5 * encumbMult * encumbLevel^2)

And this gives the following settings::

fFatigueRunBase = fatigueBurn      ; the base burn rate
fFatigueRunMult =  fFatigueRunBase * 0.85 * encumbMult
fFatigueWalkBase = 0.25 * fatigueBurn * (4/runMult)^3
fFatigueWalkMult =  fFatigueWalkBase * 0.7 * encumbMult
fFatigueStandBase = 0.0
fFatigueStandMult = fatigueBurn * 0.5 * encumbMult

If we can vary running speed and fatigue burn to scale with FatigueFactor?, we want running and walking to scale like this::

runScale = fatigueFactor * fFatigueRunBase/runBurn
walkScale = (fatigueFactor * fFatigueWalkBase/walkBurn)^(1/3)

And we need to compensate fatigue burn rates like this::

runBurnAdjust = runBurn * (1.0 - runScale)
walkBurnAdjust = walkBurn * (1.0 - walkScale^3)

Note that below 1/4 speed, it is more efficient to walk. We should switch to walking mode when this happens.

There are several ways to reduce run speed; reduce speed attribute, reduce athletics skill, modify position. Using speed or athletics skill reductions unfortunately does not reduce speed linearly, which is what we want. There are ways to make it behave linearly by adjusting game settings, but this would have rather drastic changes to the game mechanics (ie speed or athletics would have a vastly disproportionate affect on run/walk speed). Using an ability to modify position based on slower movement speeds every frame works fairly well, but has some touchy corner cases, and has proven to be too complicated to be worth it.

If we cannot directly modify movement speeds and/or jump, we have to do it by modifying the speed attribute, athletics ability, and acrobatics ability. Movement speeds, ignoring minor ajustment factors, are calculated as::

walkSpeed = fMoveCharWalkMin + (fMoveCharWalkMax - fMoveCharWalkMin)* speed/100
walkSpeed = walkBase + walkMult*speed/100
walkBase = fMoveCharWalkMin
walkMult = fMoveCharWalkMax - fMoveCharWalkMin
runSpeed = walkSpeed * (fMoveRunMult + fMoveRunAthleticsMult * athletics/100)
runspeed = walkSpeed * runMult
runMult = fMoveRunMult + fMoveRunAthleticsMult * athletics/100

speed = 100*(walkScale*walkSpeed - walkBase)/walkMult
speed = 100*(walkScale*walkMult*speed0/100 - (1.0 - walkScale)*walkBase)/walkMult)
      = walkScale*speed0 - 100*(1.0-walkScale)*walkBase/walkMult
      = walkScale*(speed0 + 100*walkBase/walkMult) - 100*walkBase/walkMult
athletics = 100*(runSpeed*runScale/walkSpeed*walkScale - fMoveRunMult)/fMoveRunAthleticsMult
          = 100*(runMult0*runScale/walkScale - fMoveRunMult)/fMoveRunAthleticsMult

The problem with this is speed and athletics need to go negative to bring movement speeds to zero. So instead we can only reduce them to zero, and compensate with extra energy burned. To make it clearer to the player what causes the speed and athletics drains, we can calculate encumbrance and fatigue drains independently. Walking scales linearly with speed, and running scales linearly with speed and athletics. Since walkScale scales non-linearly with a x^(1/3) curve but runScale scales linearly, we should scale speed with a ^(1/3) curve and athletics with a ^(2/3) curve::

encumbRunScale = fFatigueRunBase/runBurn
encumbJumpScale = fFatigueJumpBase/jumpBurn
encumbSpeedDrain = (1 - encumbRunScale^(1/3)) * speedBase
encumbAthleticsDrain = (1 - encumbRunScale^(2/3)) * athleticsBase
encumbAcrobaticsDrain = (1 - encumbJumpScale) * acrobaticsBase
fatigueRunScale = fatigueJumpScale = fatigueFactor
fatigueSpeedDrain = (1 - fatigueFactor^(1/3)) * (speedBase - encumbSpeedDrain)
fatigueAthleticsDrain = (1 - fatigueFactor^(2/3)) * (athleticsBase - encumbAthleticsDrain)
fatigueAcrobaticsDrain = (1 - fatigueFactor) * (acrobaticsBase - encumbAcrobaticsDrain)

zeroSpeed = -100 * fMoveCharWalkMin / (fMoveCharWalkMax - fMoveCharWalkMin) ; speed at which walkSpeed is 0
zeroAthletics = -100 * fMoveRunMult / fMoveRunAthleticsMult ; athletics at which runSpeed is 0
zeroAcrobatics = -100 * fJumpHeightMin / (fJumpHeightMax - fJumpHeightMin) ; acrobatics at which jumpHeight is 0

walkScale = 1.0 - (encumbSpeedDrain + fatigueSpeedDrain) / (speedBase - zeroSpeed)
runScale = walkScale * (1.0 - (encumbAthleticsDrain + encumbFatigueDrain)/(athleticsBase - zeroAthletics))
jumpScale = 1.0 - (encumbAcrobaticsDrain + fatigueAcrobaticsDrain) / (acrobaticsBase - zeroAcrobatics)

walkBurn = walkBurn * walkScale^3
runBurnAdj = runBurn * (1.0 - runScale)
jumpBurnAdj = jumpBurn * (1.0 - jumpScale)

Note that to maximize the movement scaling for fatigue and encumbrance, you should set the "min" game settings as low as possible.

Uphill/Downhill movement

If we assume running is a max-force or max-energy activity, then running uphill vs downhill should have no effect on fatigueBurn (which is max'ed), but it should impact on running speed. Since walking is not a max-energy activity, walking uphill vs downhill could either impact on fatigue burned, or on walking speed, or on both.

In [REF3] there is detailed information on the effects of gradient on running and walking, and in [REF4] there is an explanation of how gradient affects movement in terms of positive and negative work. We can assume running and walking on the flat consist of equal amounts positive (pushing up) and negative (landing down) work. However, positive work is 5x as expensive as negative work, so 5/6 of the energy burned is spent on positive work. When you add a positive gradient, it starts to cancel out some of the negative work (you don't "land" as hard) and adds more positive work (pushing uphill). When you add a negative gradient, it starts to cancel some of the positive work (you don't "push" as hard) and adds negative work (landing downhill). Pure uphill positive work is about 10x as expensive as running per meter, and downhill negative work is 2x as expensive. This can be modeled by breaking the Ewalk/Erun down into positive/negative components and adding them to the gradient positive/negative work. However, the measured results suggest that Erun/Ewalk taper away and become negligible beyond about +-0.15 gradients. This can be modeled like this::

walkMult = 0.2
climbRate = Vclimb/Vrun = dz/dd
Kclimb = 10.0
Kdown = 2
Krun = 1.0
Kwalk = 4.0
climbScale = 1/((climbRate/0.1)^2 + 1)
Eclimb = max(Kclimb*climbRate, -Kdown*climbRate) * encumbTotal * V
Erun = climbScale * Krun * encumbTotal * V
Ewalk = climbScale * Kwalk * encumbTotal * V^2
runScale = (fatigueFactor/encumbTotal)*1/(max(Kclimb*climbRate, -Kdown*climbRate) + climbScale*Krun)
walkScale = walkMult*sqrt(fatigueFactor/(encumbTotal*climbScale)) * Kwalk/(max(Kclimb*climbRate, -Kdown*climbRate) + Kwalk)
S/encumbTotal = max(Kclimb*climbRate, -Kdown*climbRate) * V + climbScale*Kwalk*V^2

This is surprisingly close to the measured performance of running/walking athletes.

The research on gradient running shows in theory downhill running could be nearly 2x as fast at a -0.1~0.2 gradient, but in practice this doesn't seem to be the case. The problem is downhill running at high speed is dangerous and risks injury, so in practice it is not a max-energy activity. This means downhill running speeds should maybe be limited and instead provide some reduced fatigue burn.

Jumping

Jumping heights should be directly proportional to FatigueFactor?. Energy burned by jumping should be directly proportional to jumping height. Jumping height is directly proportional to kinetic energy at takeoff, which gives us the initial jump velocity as a function of jump height::

Ejump = (1/2)*M*Vjump^2 = g*M*jumpHeight
Vjump = sqrt(2*g*jumpHeight)

This means we can scale the jump height by scaling the initial jump velocity::

jumpScale = fatigueFactor/encumbTotal
jumpHeight0 = fJumpHeightMin + (fJumpHeightMax - fJumpHeightMin) * acrobatics/100
jumpHeight = jumpScale * jumpHeight0
Vjump = Vjump0 * sqrt(jumpScale)

fatigueJumpBurn0 = fFatigueJumpBase + fFatigueJumpMult * encumbLevel
fatigueJumpBurn = jumpScale * fatigueJumpBurn0 = fatigueFactor * fFatigueJumpBase
fFatigueJumpMult = encumbMult * fFatigueJumpBase

climbBurn = fatigueJumpBurn0/jumpHeight0 * Vclimb*runSpeed0 = burnrate * Eclimb = fFatigueRunBase * Kclimb * Vclimb
runBurn = fatigueRunBurn0/runSpeed0 * Vrun = burnrate * Erun
Kclimb = fFatigueJumpBase/fFatigueRunBase * runSpeed0/jumpHeight0 ~=  30/8 * 130*4/164 ~= 11

Kclimb = 10 = fFatigueJumpBase/fFatigueRunBase * runSpeedMax/fJumpHeightMax

fFatigueJumpBase = Kclimb * fFatigueWalkBase * fJumpHeightMax/fMoveCharWalkMax

Sleep

According to Wikipedia on sleep deprivation the effects of sleep deprivation are mostly mental, with a longer term impact on overall health. It doesn't seem to have much impact on physical ability at all. On mental ability, it initially it starts to cause mental impairment, then emotional instability, then psychosis and illusions. On physical health it impairs healing, reduces resistance to disease, causes weight loss, and ultimately death. It's also very difficult to stay awake when very sleep deprived, with a high probability of nodding-off into microsleeps in the middle of any sort of activity. There doesn't seem to be any correlation between tiredness and physical activity, so tiredness depends entirely on time awake vs time asleep. Performance degradation starts after about 16hrs awake, with microsleeps becoming impossible to avoid after a couple of days. The record for no sleeping without a medical condition is 11 days. Medical fatal insomnia results in death in 7~36months.

This suggests sleep deprivation is best modelled with slowly increasing drains/burns on mental and health attributes. For mental impairment you could use drains on personality/intelligence/willpower or a slowly increasing burn on magika. For health impairment you could use a drain endurance/strength/speed, or a slowly increasing burn on health. I generally prefer burns on the derived attributes magika and health. The symptoms are also similar to diseases, so maybe a disease visual effects from RealisticHealth? would be good.

Hunger

According to Wikipedia on starvation, the effects of food deprivation are almost entirely on health. After about 24hrs the bodies sugar energy reserves are depleted and it starts metabolising fats, and then proteins. The body literally consumes itself for energy, eventually resulting in organ failure and death. Note that the rate this happens depends on energy burned, so high activity makes it happen faster. How long you can last without food varies largely between individuals, but it's in the order of weeks.

Hunger is probably best modelled as another energy layer on top of the short term fatigue model, that is replenished by eating, and depleted by low fatigue. When it gets to zero it should start consuming health instead. It should probably also should provide and be depleted by a slow healing effect.

Thirst

According to Wikipedia on dehydration the observable effects progress through headaches, dizziness, weakness, fainting, delirium, unconsciousness, and eventually death. You need about 2.5l of water/day, but this varies heavily with temperature and physical activity. How long you can last without water varies by individual, temperature, and physical activity, but death typically results within days. This means mental, fatigue, and health effects, though you could say the fatigue effects are a result of the rapid decline in health.

Thirst is probably best modelled as a slow burn of health and magika as a function of low fatigue, and drinking should have a limited healing effect on health and magika. This means drinking regularly will repair the effects of thirst. Note this also means magical healing of health/magika will also heal the damage caused by thirst. The effects might also be implemented using a disease and RealisticHealth? disease effects.

User Feedback on Realistic Fatigue --Erik Wade, Thu, 18 Mar 2010 19:11:07 -0400 reply

Hi Donovan, I ran across your mod for Oblivion and started using it recently. I really like what it adds to the game, even if it singificantly increases the level of difficulty. When I found this wiki explaining your research, I was sold. I intend to comment on tesnexus but thought this would be a better place to offer some constructive criticism for fear that the same on tesnexus would be perceived as negative criticism for the non-technically inclined. To start off with, I think you must realize that what you are trying to do is impossible. Representing the energy flows within a human with the tools available in Oblivion is not going to succeed in a precise manner. That being understood, I find your effort remarkable, providing a good approximation given the tools at your disposal. I have not gone through the math in detail, nor tried to pick apart the esp to really understand the implementation, though I may should the need arise. Allow me to start with my play experience. For the most part, I find Realistic Fatigue works quite nicely and makes fatigue and its management into an important consideration, as it should be. That the original designers chose a different route is easy to understand, in that managing fatigue becomes a significant task which probably detracts from the "fun" for many, probably even most, gamers. Where I do perceive weaknesses in Realistic Fatigue is in relation to climbing and in combat. With respect to climbing, a low-level character wearing a typical equipment load coming out of the sewer will find the trek up the hill to the Imperial City slow going. A load which creates no significant emcumberance on level ground brings the player to a stop on a slope of moderate size, such as approaching Aleswell from the east. My feeling is that the fatigue cost for climbing slopes is a little too high. As you point out, climbing can affect speed and fatigue. My feeling is that the character would probably slow down more and burn less fatigue than in the current implementation. The other concern I have is that if I understand the settings correctly, once you are in combat, your fatigue expenditure is equivalent to running, regardless of whether you are running or not. In fact I get the impression that this cost might be cumulative with the cost of running, though, as I said, I have not carefully looked at the esp to see if that is the case. It seems that once combat is joined, fatigue rapidly evaporates. Perhaps with higher level characters the effect is not so devastating, but at level 1, your battles better end quick or you will lose. The fatigue cost for combat may be too high, given that the cost for using weapons is assessed on top of it. If you are interested, I would be pleased to discuss this more, but I think I will leave it at this for a start. Maybe in closing a final thought for where this could go. The other mods handling fatigue, sleep, eating and drinking operate on a different paradigm, where fatigue represents something more like the overall level of energy reserves in the body(mostly the liver), as opposed to the short-term reserves (mostly in peripheral muscles) modelled here. Using several of the mods adds greatly to the immersive effect but I don't feel they are entirely compatible, given the differing paradigms at their root. Perhaps you could consider drafting a set of mods or a single unified mod that handles these various aspects but in a means that is more self-consisent. Just to give you an example, lack of sleep is not likely to have much of an impact on short-term fatigue levels until it is fairly extreme. But lack of sleep has a clear impact on mental processes, so a sleep mod should leave physical parameters alone, at least for the first 24 hours or so, but could influence intelligence as soon as the character is deemed tired. Thanks again for your impressive effort and know that at least someone appreciates it and your scientific approach. Erik Wade

Feedback on Feedback --DonovanBaarda, Mon, 26 May 2014 12:43:06 -0400 reply

Thanks for the feedback Eric, and appologies for taking so long to followup. A couple of points;

  • Overall difficulty: RF is completely tuneable, and you can easily adjust it so that fatigue is almost irrelevant if you want. The default settings are pretty harsh on a starting character, but you also really notice your progress as you advance.
  • Excessive Uphill fatigue burn: You are correct that in reality going uphill would reduce your speed and thus fatigue burn rates. Unfortunately Oblivion doesn't provide a mechanism for reducing speed, so the model would accurately burn fatigue unrealistically fast as you speed uphill. In later iterations of RF I reduced the default settings for uphill fatigue burn to limit this.
  • Combat Fatigue burn: I used running as the base fatigue burn rate. Fatigue burn for attack/block actions are on top of that, so yes, running around and swinging weapons does burn more fatigue than just running around. In reality your attack/block force and speed would decline as you fatigue, and this so would your burn rate. Unfortunately, although Oblivion does take fatigue into account when calculating attack force/damage, it doesn't have a mechanism for reducing the attack/block fatigue burn. This means it does burn more fatigue than it realistically should. This can be tweaked in the RF settings, and I did change the defaults in later versions of RF.
  • Other hunger/thirst/sleep mods: You are correct that many other hunger/thirst mods completely change the fatigue mechanism and are incompatible with RF. However, in theory compatible mods for this could be implemented that don't change the fatigue mechanism, but instead use a slowly increasing fatigue/magica drain that undermines the fatigue recovery rate. This is what I used in RealisticHealth? for diseases.



subject:
  ( 3 subscribers )