All calculations are done in floating point unless indicated otherwise.
Persuasion formulae
Persuasion options in the NPC dialogue menu.
Shared terms
persTerm = personality / fPersonalityModluckTerm = luck / fLuckModrepTerm = reputation * fReputationModlevelTerm = level * fLevelModfatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0Note fatigueTerm is normally 1.25 at full fatigue.using player stats:playerRating1 = (repTerm + luckTerm + persTerm + speechcraft) * fatigueTermplayerRating2 = playerRating1 + levelTermplayerRating3 = (mercantile + luckTerm + persTerm) * fatigueTermusing NPC stats (note differences):npcRating1 = (repTerm + luckTerm + persTerm + speechcraft) * fatigueTermnpcRating2 = (levelTerm + repTerm + luckTerm + persTerm + npcSpeechcraft) * fatigueTermnpcRating3 = (mercantile + repTerm + luckTerm + persTerm) * fatigueTermd = 1 - 0.02 * abs(npcDisposition - 50)target1 = d * (playerRating1 - npcRating1 + 50)target2 = d * (playerRating2 - npcRating2 + 50)target3 = d * (playerRating3 - npcRating3 + 50) + bribeModwhere bribeMod is fBribe10Mod, fBribe100Mod or fBribe1000Mod
Admire
target1 = max(iPerMinChance, target1)roll 100, win if roll <= target1c = int(fPerDieRollMult * (target1 - roll))x = max(iPerMinChange, c) on success, c on fail
Intimidate
target2 = max(iPerMinChance, target2)roll 100, win if roll <= target2if 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 = celse fail: x = int(c * fPerTempMult), y = c
Taunt
target1 = max(iPerMinChance, target1)roll 100, win if roll <= target1c = 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
Bribe
target3 = max(iPerMinChance, target3)roll 100, win if roll <= target3c = int((target3 - roll) * fPerDieRollMult)x = max(iPerMinChange, c) on success, c on fail
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:
Temporary disposition change = int(x * fPerTempMult)except for Intimidate: change = x
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.
Permanent disposition change = int(cappedDispositionChange / fPerTempMult)except for Intimidate: change = -int(cappedDispositionChange/ fPerTempMult) on success, y on fail
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. This issue is fixed in MCP 1.8.
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
if sneaking: sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMultelse: sneakTerm = 0 fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0Note fatigueTerm is normally 1.25 at full fatigue.distTerm = fSneakDistBase + fSneakDistMult*distx = sneakTerm * distTerm * fatigueTerm + chameleon (+ 100 if invisible)
NPC side
npcTerm = npcSneak + 0.2 * npcAgility + 0.1 * npcLuck - npcBlindnpcFatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)using NPC normalisedFatigueif PC is behind NPC (180 degrees): y = npcTerm * npcFatigueTerm * fSneakNoViewMultelse: y = npcTerm * npcFatigueTerm * fSneakViewMult
Final check
target = x - yroll 100, win if roll < target
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.
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
for each item stack:roll 100, stack is visible if roll <= pcSneak
On picking an item
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0Note fatigueTerm is normally 1.25 at full fatigue.checks the whole stack no matter how many you try to takenote: filled soulgems have the value of an empty soulgem due to a missing calculationstackValue = http://forums.bethsoft.com/index.php?/topic/1097214-gameplay-mechanics-analysis/itemValue * itemsInStackvalueTerm = 10 * fPickPocketMod * stackValuex = (0.2 * pcAgility + 0.1 * pcLuck + pcSneak) * fatigueTermy = (valueTerm + npcSneak + 0.2 * npcAgilityTerm + 0.1 * npcLuckTerm) * npcFatigueTermt = x - y + x (yes, that's 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)
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.
Auto-calculate stats
NPCs' auto-calculated stats. Affected by race, class, faction and rank.
Attributes
for each attribute:base = race base attribute (+ 10 if a class primary attribute)k = 0for 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.2final attribute = base + k * (level - 1)round attribute to nearest, half to nearest even (standard IEEE 754 rounding mode)
Health
health = 0.5 * (strength + endurance) + 5 * (level - 1)
Skills
for each skill:if skill is class major: base = 30, k = 1if skill is class minor: base = 15, k = 1if skill is miscellaneous: base = 5, k = 0.1if skill is in class specialization: base += 5, k += 0.5if skill has race bonus: base += racebonusfinal skill = base + k * (level - 1)round skill to nearest, half to nearest even (standard IEEE 754 rounding mode)
Reputation
if not in a faction, reputation = 0reputation = iAutoRepFacMod * rank + iAutoRepLevMod * (level - 1)where the entry level rank in the faction means rank = 1
Comment
Hnh. Stats.
Enchanted item recharge
Recharging with a filled soulgem from your inventory.
On recharge
fatigueTerm = fFatigueBase - fFatigueMult*(1 - normalisedFatigue)where normalisedFatigue is a function of fatigue. empty fatigue bar -> 0.0, full fatigue bar -> 1.0enchantTerm = enchant skillluckTerm = 0.1 * luckif luckTerm < 1 or luckTerm > 10: luckTerm = 1intelligenceTerm = 0.2 * intelligenceif intelligenceTerm > 20: intelligenceTerm = 20if intelligenceTerm < 1: intelligenceTerm = 1x = (enchantTerm + intelligenceTerm + luckTerm) * fatigueTermroll 100, success if roll < xon success restore charge: soulgem charge * (roll / x)
Comments
Recharging for most characters has a good chance of wasting a soul gem, as the enchant skill is the dominant term used for success. You would require enchant skill of over 65 with average stats to have a 100% success rate. The amount restored is a uniform random percentage of the soul gem, except if you have over a 100% success rate, in which case you will never get the full charge range out of a gem. The missing range increases as your skill does, but the lost charge is no more than 25% at the natural stat limit. Finally, note the strange luck term capping behaviour.