Formulae: Difference between revisions

From OpenMW Wiki
Jump to navigation Jump to search
(Redirect to newly organized Research namespace.)
 
(39 intermediate revisions by 4 users not shown)
Line 1: Line 1:
==Combat Formulae==
#REDIRECT [[Research]]
 
===Melee===
====Hit Chance====
<syntaxhighlight lang="python">
  hit chance = Attackers weapon skill * 1.25 + Attackers Attack - Defenders Sanctuary + (Attackers Agility - Defenders Agility) * 0.25 + (Attackers Luck - Defenders Luck) * 0.125
</syntaxhighlight>
Example:
 
* Warrior's Attack = 10
* Thief's Sanctuary = 10
 
====Blocking melee attacks with an equipped shield.====
On enemy hit
<syntaxhighlight lang="python">
theta = angle from player to enemy, negative is enemy left of centreline, positive is enemy right of centreline
 
if theta < fCombatBlockLeftAngle: no block
if theta > fCombatBlockRightAngle: no block
 
#Note that the above tests are inaccurate, as each comparison is calculated using a dot product which is converted to an angle improperly. The effective value of fCombatBlock*Angle end up slightly different than specified.
 
blockTerm = pcBlockSkill + 0.2 * pcAgility + 0.1 * pcLuck
enemySwing = random number in range [0.1, 1]
swingTerm = enemySwing * fSwingBlockMult + fSwingBlockBase
 
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0
Note fatigueTerm is normally 1.25 at full fatigue.
 
playerTerm = blockTerm * swingTerm
if player is not moving forward at any speed: playerTerm = playerTerm * 1.25 (note this is not fBlockStillBonus)
playerTerm = playerTerm * fatigueTerm
 
if npc is a creature: npcSkill = creature combat stat
otherwise: npcSkill = npc skill with wielded weapon
 
npcTerm = npcSkill + 0.2 * npcAgility + 0.1 * npcLuck
npcFatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
using NPC normalisedFatigue
npcTerm = npcTerm * npcFatigueTerm
 
x = int(playerTerm - npcTerm)
if x < iBlockMinChance: x = iBlockMinChance
if x > iBlockMaxChance: x = iBlockMaxChance
roll 100, block if roll < x
</syntaxhighlight>
The enemySwing variable is effectively how much the enemy has charged their attack. It seems to be uniform random and is not based on weapon damage. The other thing of note is the playerTerm bonus. The fBlockStillBonus GMST is not used at all; the multiplier is hard-coded. Unexpectedly, this bonus is still given if the player is moving backwards or strafing, only moving forward will negate it.
 
====Bodypart Hit Chance====
====Damage====
<syntaxhighlight lang="python">
  Damages dealt = (Damage * Damage) / (Damage + Opponent Armor Rating)
</syntaxhighlight>
 
===Magic===
====Chance of Successful Spell Cast====
<syntaxhighlight lang="python">
  chance = (Spells Skill * 2 + Willpower / 5 + Luck / 10 - SpellCost + CastPenalty) * (CurrentFatigue + MaximumFatigue * 1.5) / (MaximumFatigue * 2)
</syntaxhighlight>
The value for CastPenalty can be accessed with the function GetCastPenalty.
 
====Cast cost====
<syntaxhighlight lang="python">
  cost = 0.1 + ((min effect value / 20) + (max effect value / 20)) * ((duration - 1) / 2) + (range / 40).
</syntaxhighlight>
x1.5 for ranged spell. Constant effect enchantments use fEnchantmentConstantDurationMult as their duration values (default 100).
 
* Touch/Self
<syntaxhighlight lang="python">
  cost = base cost * ((max + min) * duration * 0.025 + area * 0.0125)
</syntaxhighlight>
* Target
<syntaxhighlight lang="python">
  cost = base cost * ((max + min) * duration * 0.3125 + area * 0.0125)
</syntaxhighlight>
* Constant effect
<syntaxhighlight lang="python">
  cost = base cost * ((max + min) * 2.5 + area * 0.025)
</syntaxhighlight>
The base cost are [http://www.uesp.net/wiki/Morrowind:Spell_Effects here]
 
=====Comments=====
They are more than one formulae on internet, witch one is the real?
 
====Magic Mitigation====
====Magic Damage====
<syntaxhighlight lang="python">
Spell Damage = Damage - Damage * ( Resistance - Weakness ) / 100
</syntaxhighlight>
===Ranged===
==Item Values==
===Spell cost in gold===
<syntaxhighlight lang="python">
  cost of puchasing existing spell = spell cost in magicka * fSpellValueMult
 
  cost of spellmaking = spell cost in magicka * fSpellMakingValueMult
 
  cost of enchanting service = Enchantment points * fEnchantmentValueMult
</syntaxhighlight>
 
This is adjusted as follows: 0.5% per point of disposition (base 50). 0.5% per point of mercantile skill difference. 0.05% per point of personality value difference. 0.05% per point of luck value difference.
 
===Item costs in Gold===
 
===Travel Costs in Gold===
 
==Item Durability, Item Charge==
 
===Damages===
 
With every successful hit a weapon takes fWeaponDamageMult: (default 0.1) percentage of damage rounded down. Minimum damage to weapon = 1 With weapon health going down, its damage is also going down, thus in turn slowing the rate of damaging weapon. Armor takes an amount of damage that it stopped. Only armor part that took a hit takes any damage, while each armor part equipped contributes to the armor rating.
 
===Charge===
==Armor Rating and Armor Class==
 
===Determining armor class===
 
Armor class (in case of helm) is determined by these code:
<syntaxhighlight lang="python">
  if (iHelmWeight * fLightMaxMod <= helm. weight):
    return LIGHT
  else if (iHelmWeight * fMedMaxMod <= helm. weight):
    return MEDIUM
  else:
    return HEAVY
</syntaxhighlight>
 
For other armor types another variable should be used:
 
* iHelmWeight
* iPauldronWeight
* iGauntletWeight
* iCuirassWeight
* iGreavesWeight
* iBootsWeight
 
===Armor rating===
<syntaxhighlight lang="python">
  rating = armor. rating * ArmorSkill / iBaseArmorSkill
</syntaxhighlight>
 
if player don't have armor piece unarmored skill is used instead:
<syntaxhighlight lang="python">
  rating = UnarmoredSkill * UnarmoredSkill * 0.0065
</syntaxhighlight>
 
Total armor rating determined by these formula:
<syntaxhighlight lang="python">
  totalRating = Cuirass * 0.3 + (Shield + Helm + Greaves + Boots + RPauldron + LPauldron) * 0.1 + (RGauntlet + LGauntlet) * 0.05
</syntaxhighlight>
===How Unarmored Works===
==Persuasion Formulae==
 
Persuasion options in the NPC dialogue menu.
 
===Shared terms===
<syntaxhighlight lang="python">
persTerm = personality / fPersonalityMod
luckTerm = luck / fLuckMod
repTerm = reputation * fReputationMod
levelTerm = level * fLevelMod
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
#where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0
#note fatigueTerm is normally 1.25 at full fatigue.
</syntaxhighlight>
Using player stats:
<syntaxhighlight lang="python">
playerRating1 = (repTerm + luckTerm + persTerm + speechcraft) * fatigueTerm
playerRating2 = playerRating1 + levelTerm
playerRating3 = (mercantile + luckTerm + persTerm) * fatigueTerm
</syntaxhighlight>
Using NPC stats (note differences):
<syntaxhighlight lang="python">
npcRating1 = (repTerm + luckTerm + persTerm + speechcraft) * fatigueTerm
npcRating2 = (levelTerm + repTerm + luckTerm + persTerm + npcSpeechcraft) * fatigueTerm
npcRating3 = (mercantile + repTerm + luckTerm + persTerm) * fatigueTerm
</syntaxhighlight>
Summary:
<syntaxhighlight lang="python">
d = 1 - 0.02 * abs(npcDisposition - 50)
target1 = d * (playerRating1 - npcRating1 + 50)
target2 = d * (playerRating2 - npcRating2 + 50)
target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod
where bribeMod is fBribe10Mod, fBribe100Mod or fBribe1000Mod
</syntaxhighlight>
where bribeMod is fBribe10Mod, fBribe100Mod or fBribe1000Mod
 
===Admire===
<syntaxhighlight lang="python">
  target1 = max(iPerMinChance, target1)
  roll 100, win if roll <= target1
  c = int(fPerDieRollMult * (target1 - roll))
  x = max(iPerMinChange, c) on success, c on fail
</syntaxhighlight>
 
===Intimidate===
<syntaxhighlight lang="python">
target2 = max(iPerMinChance, target2)
roll 100, win if roll <= target2
 
if roll != target2:
        r = int(target2 - roll)
else:
        r = 1
       
if roll <= target2:
        s = int(r * fPerDieRollMult * fPerTempMult)
        flee = max(iPerMinChange, s)
        fight = min(-iPerMinChange, -s)
 
c = -abs(int(r * fPerDieRollMult))
if success:
        if abs(c) < iPerMinChange:
                x = 0, y = -iPerMinChange
        else:
                x = -int(c * fPerTempMult), y = c
else fail:
        x = int(c * fPerTempMult), y = c
</syntaxhighlight>
 
===Taunt===
<syntaxhighlight lang="python">
target1 = max(iPerMinChance, target1)
roll 100, win if roll <= target1
 
c = abs(int(target1 - roll))
if roll <= target1:
    s = c * fPerDieRollMult * fPerTempMult
    flee = min(-iPerMinChange, int(-s))
    fight = max(iPerMinChange, int(s))
  x = int(-c * fPerDieRollMult)
if success and abs(x) < iPerMinChange:
    x = -iPerMinChange
</syntaxhighlight>
===Bribe===
<syntaxhighlight lang="python">
target3 = max(iPerMinChance, target3)
roll 100, win if roll <= target3
c = int((target3 - roll) * fPerDieRollMult)
x = max(iPerMinChange, c) on success, c on fail
</syntaxhighlight>
 
===Disposition===
 
For all persuasion actions there is a temporary and a permanent disposition change. The temporary one applies to the disposition meter you see in the dialogue window.
The permanent one is applied when you say goodbye to the NPC; the NPC's disposition is reset to the disposition they had when you initiated the conversation,
then the permanent disposition change is applied. You can see these values in the console by using ToggleDialogStats before persuading.
 
For all methods:
<syntaxhighlight lang="python">
Temporary disposition change = int(x * fPerTempMult)
</syntaxhighlight>
 
except for Intimidate:
<syntaxhighlight lang="python">
change = x
</syntaxhighlight>
 
This may attempt to change actual disposition below/above 0/100. Disposition changes are clamped so as not to go past the caps, and the actual amount the disposition moved is used in the next function.
<syntaxhighlight lang="python">
Permanent disposition change = int(cappedDispositionchange / fPerTempMult)
</syntaxhighlight>
 
except for Intimidate
<syntaxhighlight lang="python">
change = -int(cappedDispositionchange/ fPerTempMult) on success
y on fail
</syntaxhighlight>
 
There may also be modifications to the NPC's flee and fight ratings. The flee and fight variables hold the amount those ratings are changed. They are also capped at 0 and 100.
 
===Comments===
 
The function is long and highly redundant, much of the same formulas are repeatedly calculated many times for no reason.
It's just another poorly coded part of Morrowind. There is at least one bug with Intimidate where you can see if the calculated
change is under iPerMinChange, it fails to set x correctly (it should have the value y does). This is responsible for the disposition
meter not moving on some Intimidate Success results.
 
==NPC Behavior==
===NPC Awareness Check===
 
This check runs every 5 seconds for each NPC. It occurs whether you are sneaking or not, but isn't the same as the combat distance check.
 
====Player side====
<syntaxhighlight lang="python">
if sneaking:
    sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMult
  else:
    sneakTerm = 0
 
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0
distTerm = fSneakDistBase + fSneakDistMult*dist
x = sneakTerm * distTerm * fatigueTerm + chameleon (+ 100 if invisible)
</syntaxhighlight>
 
====NPC side====
<syntaxhighlight lang="python">
npcTerm = npcSneak + 0.2 * npcAgility + 0.1 * npcLuck - npcBlind
npcFatigueTerm = fFatigueBase - fFatigueMult * (1 - normalisedFatigue)
using NPC normalisedFatigue
 
if PC is behind NPC (180 degrees):
    y = npcTerm * npcFatigueTerm * fSneakNoViewMult
  else:
    y = npcTerm * npcFatigueTerm * fSneakViewMult
</syntaxhighlight>
 
====Final check====
<syntaxhighlight lang="python">
target = x - y
roll 100, win if roll < target
</syntaxhighlight>
 
====Comments====
 
Appears straightforward and bug-free. NPCs can take up to five seconds to notice you even if you are not sneaking. This function precedes the combat distance check.
I have not identified if there is a line of sight check occuring before or after.
 
===Combat Behavior===
 
====Combat AI====
 
====What Calm Does====
 
====What Demoralize Does====
 
====What Frenzy Does====
 
====What Rally Does====
 
==Pickpocketing==
 
Pickpocketing is a multi-stage process. Not all items in the NPC's inventory are available, depending on the initial rolls. There are checks on a steal attempt, and when the window is closed.
 
===On initiating===
<syntaxhighlight lang="python">
for each item stack:
roll 100, stack is visible if roll <= pcSneak
</syntaxhighlight>
 
===On picking an item===
<syntaxhighlight lang="python">
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
#where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0
#note fatigueTerm is normally 1.25 at full fatigue.
 
#checks the whole stack no matter how many you try to take
#note: filled soulgems have the value of an empty soulgem due to a missing calculation
stackValue = itemValue * itemsInStack
valueTerm = 10 * fPickPocketMod * stackValue
 
x = (0.2 * pcAgility + 0.1 * pcLuck + pcSneak) * fatigueTerm
y = (valueTerm + npcSneak + 0.2 * npcAgilityTerm + 0.1 * npcLuckTerm) * npcFatigueTerm
t = x - y + x  # yes, that is what it does
 
if t < pcSneak / iPickMinChance:
        roll 100, win if roll <= int(pcSneak / iPickMinChance)
else:
        t = min(iPickMaxChance, t)
        roll 100, win if roll <= int(t)
</syntaxhighlight>
===On closing the pickpocket window===
 
Same calculation as taking an item, but with valueTerm = 0
 
===Comments===
 
The stealing process is highly broken for most items; any item or stack of items worth over 100 septims has such a negative result that it is picked at minimum chance, and this is at maximum all stats. A player with stats around 50 is picking at minimum for anything valuable. The available items window is not reset after every successful steal, only when you close the window and retry the pickpocket.
 
==Factions==
 
===Favored Attributes===
 
===Favored Skills===
 
===Ranks===
 
===Faction vs. Faction Modifiers===
 
===Effect on Disposition===
 
===Effect on Behavior===
 
==PC's leveling==
 
How to achieve new level and what happens then
 
===Achieving===
 
When player receive point in the skill:
 
<syntaxhighlight lang="python">
int total # global counter of skill increases
int attribCounter[8] #counter of attribute bonuses
if skill in Major:
    total += iLevelUpMajorMult
    attribCounter [skill->basicAttribute] += iLevelUpMajorMultAttribute
 
if skill in Minor:
    total += iLevelUpMinorMult
    attribCounter [skill->basicAttribute] += iLevelUpMinorMultAttribute
if skill in Misc:
    attribCounter [skill->basicAttribute] += iLevelUpMinorMultAttriubte #note: game setting name has a typo
if ( total >= iLevelUpTotal )
    LevelUp()
</syntaxhighlight>
 
On level-up PC get 3 points to redistribute, bonus will be this:
 
<syntaxhighlight lang="python">
if attribCounter !=0
    bonus = iLevelUp$$Mult
else
    bonus = 1
</syntaxhighlight>
 
where $$ is value of attribute counter
 
===Skill increases===
 
How to achieve increase in skill
 
==PC's dynamic stats==
 
Health point's, magic, stamina...
 
===HP===
 
Initial HP
<syntaxhighlight lang="python">
health = 0.5 * (strength + endurance)
</syntaxhighlight>
 
Every level add
<syntaxhighlight lang="python">
bonusHP = fLevelUPHealthEndMult * endurance
</syntaxhighlight>
 
===Magic points===
<syntaxhighlight lang="python">
Magic = intelligence + M * intelligence
</syntaxhighlight>
 
M = magic bonus from race, item or sign.
 
===Fatigue===
<syntaxhighlight lang="python">
Fatigue = Strength + Willpower + Agility + Endurance
</syntaxhighlight>
 
==NPC Auto-calculate stats==
 
NPCs' auto-calculated stats. Affected by race, class, faction and rank.
 
===Attributes===
 
<syntaxhighlight lang="python">
for each attribute:
base = race base attribute (+ 10 if a class primary attribute)
k = 0
for each skill with this governing attribute:
    if skill is class major: k += 1
    if skill is class minor: k += 0.5
    if skill is miscellaneous: k += 0.2
final attribute = base + k * (level - 1)
</syntaxhighlight>
 
round attribute to nearest, half to nearest even (standard IEEE 754 rounding mode)
 
 
===Health===
<syntaxhighlight lang="python">
health = 0.5 * (strength + endurance) + 5 * (level - 1)
</syntaxhighlight>
 
===Skills===
<syntaxhighlight lang="python">
for each skill:
 
if skill is class major: base = 30, k = 1
if skill is class minor: base = 15, k = 1
if skill is miscellaneous: base = 5, k = 0.1
if skill is in class specialization: base += 5, k += 0.5
if skill has race bonus: base += racebonus
final skill = base + k * (level - 1)
</syntaxhighlight>
 
Round skill to nearest, half to nearest even (standard IEEE 754 rounding mode)
 
===Reputation===
<syntaxhighlight lang="python">
if not in a faction:
    reputation = 0
else:
    reputation = iAutoRepFacMod * rank + iAutoRepLevMod * (level - 1)
</syntaxhighlight>
 
where the entry level rank in the faction means rank = 1
 
===Spells===
 
==Skills==
 
===Enchanting===
 
* Percent chance to self-enchant a Constant Effect item
<syntaxhighlight lang="python">
chance = Enchant + 0.125 * Intelligence + 0.25 * Luck - 5 * [enchantment points]
note: last term in above formula is solved for default GMSTs, full version is:
7.5/(fEnchantmentChanceMult*fEnchantmentConstantChanceMult) * [enchantment points]
</syntaxhighlight>
 
* Enchanted item recharge
<syntaxhighlight lang="python">
#Recharging with a filled soulgem from your inventory.
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)
where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0
 
enchantTerm = enchant skill
 
luckTerm = 0.1 * luck
if luckTerm < 1 or luckTerm > 10: luckTerm = 1
 
intelligenceTerm = 0.2 * intelligence
if intelligenceTerm > 20: intelligenceTerm = 20
if intelligenceTerm < 1: intelligenceTerm = 1
 
x = (enchantTerm + intelligenceTerm + luckTerm) * fatigueTerm
roll 100, success if roll < x
on success restore charge: soulgem charge * (roll / x)
</syntaxhighlight>
 
===Armorer===
 
Chance to repair item:
<syntaxhighlight lang="python">
chance = 0.8 * Armorer + 0.1 * Strength + 0.05 * Luck
</syntaxhighlight>
 
Restored health of item:
<syntaxhighlight lang="python">
maxPossible = ( 2.4 * Armorer + 0.25 * Strength + 0.2 * Luck ) * hammerQuality
restoredHealth = random(maxPossible)
</syntaxhighlight>
 
===Alchemy===
<syntaxhighlight lang="python">
duration = [(Alchemy + [(Intelligence + Luck) / 10]) / Cost] * quality_of_the_mortar
power = duration / 3
</syntaxhighlight>
 
The cost is related to basic cost of effect.
 
* Alembic reduces the time of negative effects:
<syntaxhighlight lang="python">
duration_with_alembic = duration_without_alembic / ( 1 + quality_of_alembic)
</syntaxhighlight>
 
* Power of negative effects:
<syntaxhighlight lang="python">
power = duration_with_alembic / 3
</syntaxhighlight>
 
 
Simply eating ingredients raw will improve your alchemy skill. (Though at 1/4 the rate of mixing potions.)
 
===Security===
<syntaxhighlight lang="python">
Picklock chance = ((Security Skill * 1.25 + intelligence * 0.25 + luck * 0.1) * Lockpick Quality Multiplier - (Lock Difficulty * fPickLockMult)).
</syntaxhighlight>
 
===Acrobatics===
 
==Basic OpenMW Changes==
 
==Sources==
 
* [http://forums.bethsoft.com/index.php?/topic/1097214-gameplay-mechanics-analysis/ bethsoft forum]
* [http://www.uesp.net/wiki/Morrowind:Combat Uesp's wiki: Combat section]

Latest revision as of 18:29, 3 September 2012

Redirect to: