Research:Player Craft Skills

From OpenMW Wiki
Jump to navigation Jump to search

{{#switch:|subgroup|child=|none=|#default=

}}


Enchanting[edit]

Self-enchanting[edit]

Actions affected On PC enchant attempt
Description
Implementation status Changes since last implementation
Analysis status Verified, contains bugs

Original behaviour

This uses the variable z from Enchanting service. <syntaxhighlight lang="python"> for each effect in the enchantment:

   x = z[effect.order] - int(-0.2 * pcIntelligence) + int(0.1 * pcLuck)
   x = int(x * fatigueTerm)
   if enchantment is constant effect:
       x = int(x * fEnchantmentConstantChanceMult)
   roll 100, success if roll < x, else fail and destroy gem

</syntaxhighlight>

Suggested implementation <syntaxhighlight lang="python"> z = enchantSkill - y * fEnchantmentChanceMult x = (z + 0.2 * pcIntelligence + 0.1 * pcLuck) * fatigueTerm if enchantment is constant effect:

   x *= fEnchantmentConstantChanceMult

x = int(x)

roll 100, success if roll < x, else fail and destroy gem </syntaxhighlight>

Comments[edit]

There is a serious bug where failure is checked for every effect instead of once for the whole attempt, while still using the cumulative cost. This makes multiple effect self-enchants near impossible, but doesn't affect single effect self-enchanting. There is also an excessive use of rounding and a double negation, which seem to indicate this function had a rushed implementation.

A better implementation should not have the effect loop, and may omit the excessive integer conversions. One implementation is suggested here.


Enchanted item recharge[edit]

Actions affected On soulgem use
Description Recharging with a filled soulgem. Uses common term fatigueTerm.
Implementation status Implemented
Analysis status Verified

<syntaxhighlight lang="python"> 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 = (pcEnchant + intelligenceTerm + luckTerm) * fatigueTerm roll 100, success if roll < x on success restore charge: soulgem charge * (roll / x) </syntaxhighlight>


Comments[edit]

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.

Armorer[edit]

Actions affected On using a repair item
Description
Implementation status Implemented, bug corrected
Analysis status Verified, but original contains bugs

<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

x = (0.1 * pcStrength + 0.1 * pcLuck + armorerSkill) / fatigueTerm roll 100, if roll <= x then repair continues

y = int(fRepairAmountMult * hammerQuality * roll) y = max(1, y) repair item by y points </syntaxhighlight>


Comments[edit]

Bug in original game: Being more tired makes it easier to repair an item. The game should have multiplied by fatigueTerm instead of dividing by it.

Alchemy[edit]

Ingredients[edit]

Actions affected On viewing ingredient tooltip
Description Effect visibility.
Implementation status Implemented
Analysis status Verified

Effects are not immediately apparent without alchemy skill. Ingredient effects are hidden, appearing as '?' unless the player has sufficient alchemy skill.

<syntaxhighlight lang="python"> First effect visible: pcAlchemy >= fWortChanceValue Second effect visible: pcAlchemy >= 2 * fWortChanceValue Third effect visible: pcAlchemy >= 3 * fWortChanceValue Fourth effect visible: pcAlchemy >= 4 * fWortChanceValue </syntaxhighlight>


Potions can still be made with the hidden effects, and the resulting effects appear normally in the potion result window.

Potions[edit]

Actions affected On potion creation
Description Uses magic effect flags.
Implementation status implemented
Analysis status Requires regression testing

<syntaxhighlight lang="python"> consume ingredients on both success and failure

x = pcAlchemy + 0.1 * pcIntelligence + 0.1 * pcLuck roll 100 vs x, succeed if roll <= x x *= mortarQuality * fPotionStrengthMult

for each ingredient:

   for each effect in ingredient:
       if effect.id == -1: continue
       magicEffect = lookupEffect(effect.id)
       
       check magicEffect for match with effects in all other ingredients:
           effect.id should match
           if magicEffect.flags & TARGET_SKILL: effect.targetSkill should match
           if magicEffect.flags & TARGET_ATTR: effect.targetAttribute should match
       
       if effect is matched:
           if magicEffect is not in effectList, add it
           

if effectList is empty: return with failure

for each magicEffect in effectList:

   if not magicEffect.flags & NO_MAGNITUDE:
       if not magicEffect.flags & NO_DURATION:
           magnitude = (x / fPotionT1MagMult) / magicEffect.baseMagickaCost
           duration = (x / fPotionT1DurMult) / magicEffect.baseMagickaCost
           if not magicEffect.flags & NEGATIVE:
               if retort and calcinator: (magnitude, duration) += 2*retort + calcinator
               if only retort: (magnitude, duration) += retort
               if only calcinator: (magnitude, duration) += calcincator
           else:
               if alembic and calcinator: (magnitude, duration) /= 2*alembic + 3*calcinator
               if only alembic: (magnitude, duration) /= 1 + alembic
               if only calcinator: (magnitude, duration) += calcinator
           
       else:
           magnitude = (x / fPotionT1MagMult) / magicEffect.baseMagickaCost
           duration = 1
           if not magicEffect.flags & NEGATIVE:
               if retort and calcinator: magnitude += 2/3 * (retort + calcinator) + 0.5
               if only retort: magnitude *= retort + 0.5
               if only calcinator: magnitude *= calcincator + 0.5
           else:
               if alembic and calcinator: magnitude /= 2*alembic + 3*calcinator
               if only alembic: magnitude /= 1 + alembic
               if only calcinator: magnitude *= calcinator + 0.5
           
   else:
       if not magicEffect.flags & NO_DURATION:
           magnitude = 1
           duration =  (x / fPotionT1DurMult) / magicEffect.baseMagickaCost
           if not magicEffect.flags & NEGATIVE:
               if retort and calcinator: duration += 2/3 * (retort + calcinator) + 0.5
               if only retort: duration *= retort + 0.5
               if only calcinator: duration *= calcincator + 0.5
           else:
               if alembic and calcinator: duration /= 2*alembic + 3*calcinator
               if only alembic: duration /= 1 + alembic
               if only calcinator: duration *= calcinator + 0.5
       else:
           magnitude = 1
           duration = 1
       
   magnitude = floor(magnitude + 0.5)
   duration = floor(duration + 0.5);
   if magnitude > 0 and duration > 0: add potionEffect(magicEffect, magnitude, duration) else nullify effect

if all effects are nullified: return with failure

price = int(iAlchemyMod * x) weight = sum of ingredient weights / total ingredients added visual = roll { 'Bargain', 'Cheap', 'Exclusive', 'Fresh', 'Quality', 'Standard' } pick model and icon { 'm/misc_potion_{visual}_01.nif', 'm/tx_potion_{visual}_01.tga' } exercise alchemy skill (potion creation) </syntaxhighlight>


Comments[edit]

It is notable how effective a calcinator becomes when combined with a retort or alembic. One of the few mechanics unaffected by fatigue. Each code path is slightly different; the most common (effects with magnitude and duration) acts differently enough with only a calcinator that it may be considered a bug. GMSTs fPotionMinUsefulDuration, fPotionT4BaseStrengthMult, fPotionT4EquipStrengthMult are unused.

Wortcraft[edit]

Actions affected On eating a raw ingredient
Description Uses magic effect flags. Uses common term fatigueTerm.
Implementation status implemented
Analysis status Verified

<syntaxhighlight lang="python"> magicEffect is the first effect of ingredient eaten x = (pcAlchemy + 0.2 * pcIntelligence + 0.1 * pcLuck) * fatigueTerm roll 100, succeed if roll <= x

y = roll / min(x, 100) y *= 0.25 * x if not magicEffect.flags & NO_DURATION:

   duration = int(y)

else:

   duration = 1

if not magicEffect.flags & NO_MAGNITUDE:

   if not magicEffect.flags & NO_DURATION:
       magnitude = int((0.05 * y) / (0.1 * magicEffect.baseMagickaCost));
   else:
       magnitude = int(y / (0.1 * baseCost))
   magnitude = max(1, magnitude)

else:

   magnitude = 1

apply effect(magicEffect, magnitude, duration) to player exercise alchemy skill (ingredient use) </syntaxhighlight>



{{#switch:|subgroup|child=|none=|#default=

}}