base defence percentile, closes #746

This commit is contained in:
Alexander Drozdov
2022-10-04 18:42:07 +03:00
parent 0b57d058a2
commit df56e06cdd
12 changed files with 1320 additions and 1260 deletions
+1
View File
@@ -27,6 +27,7 @@ export interface ParsedItem {
armourES?: number
armourWARD?: number
armourBLOCK?: number
basePercentile?: number
weaponCRIT?: number
weaponAS?: number
weaponPHYSICAL?: number
+20 -1
View File
@@ -12,6 +12,7 @@ import { ItemCategory } from './meta'
import { IncursionRoom, ParsedItem, ItemInfluence, ItemRarity } from './ParsedItem'
import { magicBasetype } from './magic-name'
import { isModInfoLine, groupLinesByMod, parseModInfoLine, parseModType, ModifierInfo, ParsedModifier, ENCHANT_LINE, SCOURGE_LINE } from './advanced-mod-desc'
import { calcPropPercentile, QUALITY_STATS } from './calc-q20'
type SectionParseResult =
| 'SECTION_PARSED'
@@ -65,7 +66,8 @@ const parsers: Array<ParserFn | { virtual: VirtualParserFn }> = [
{ virtual: transformToLegacyModifiers },
{ virtual: parseFractured },
{ virtual: parseBlightedMap },
{ virtual: pickCorrectVariant }
{ virtual: pickCorrectVariant },
{ virtual: calcBasePercentile }
]
export function parseClipboard (clipboard: string) {
@@ -913,6 +915,23 @@ function transformToLegacyModifiers (item: ParsedItem) {
item.statsByType = sumStatsByModType(item.newMods)
}
function calcBasePercentile (item: ParsedItem) {
if (!item.info.armour) return
// Base percentile is the same for all defences.
// Using `AR/EV -> ES -> WARD` order to improve accuracy
// of calculation (larger rolls = more precise).
const info = item.info.armour
if (item.armourAR) {
item.basePercentile = calcPropPercentile(item.armourAR, info.ar!, QUALITY_STATS.ARMOUR, item)
} else if (item.armourEV) {
item.basePercentile = calcPropPercentile(item.armourEV, info.ev!, QUALITY_STATS.EVASION, item)
} else if (item.armourES) {
item.basePercentile = calcPropPercentile(item.armourES, info.es!, QUALITY_STATS.ENERGY_SHIELD, item)
} else if (item.armourWARD) {
item.basePercentile = calcPropPercentile(item.armourWARD, info.ward!, QUALITY_STATS.WARD, item)
}
}
export function removeLinesEnding (
lines: readonly string[], ending: string
): string[] {
+139
View File
@@ -0,0 +1,139 @@
import { ParsedItem } from './ParsedItem'
import { stat } from '@/assets/data'
import { StatRoll, StatSource, statSourcesTotal } from './modifiers'
export const QUALITY_STATS = {
ARMOUR: {
flat: [stat('+# to Armour')],
incr: [
stat('#% increased Armour'),
stat('#% increased Armour and Energy Shield'),
stat('#% increased Armour and Evasion'),
stat('#% increased Armour, Evasion and Energy Shield')
]
},
EVASION: {
flat: [stat('+# to Evasion Rating')],
incr: [
stat('#% increased Evasion Rating'),
stat('#% increased Armour and Evasion'),
stat('#% increased Evasion and Energy Shield'),
stat('#% increased Armour, Evasion and Energy Shield')
]
},
ENERGY_SHIELD: {
flat: [stat('+# to maximum Energy Shield')],
incr: [
stat('#% increased Energy Shield'),
stat('#% increased Armour and Energy Shield'),
stat('#% increased Evasion and Energy Shield'),
stat('#% increased Armour, Evasion and Energy Shield')
]
},
WARD: {
flat: [stat('+# to Ward')],
incr: [
stat('#% increased Ward')
]
},
PHYSICAL_DAMAGE: {
flat: [stat('Adds # to # Physical Damage')],
incr: [
stat('#% increased Physical Damage')
]
}
}
export function propAt20Quality (
total: number,
statRefs: { flat: string[], incr: string[] },
item: ParsedItem
): { roll: StatRoll, sources: StatSource[] } {
const { incr, flat, sources } = calcPropBase(statRefs, item)
const base = calcBase(total, incr.value + (item.quality ?? 0), flat.value)
const quality = Math.max(20, item.quality ?? 0)
return {
roll: {
value: calcIncreased(base + flat.value, incr.value + quality),
min: calcIncreased(base + flat.min, incr.min + quality),
max: calcIncreased(base + flat.max, incr.max + quality)
},
sources: sources.map(source => ({ ...source, contributes: undefined }))
}
}
export function calcPropBounds (
total: number,
statRefs: { flat: string[], incr: string[] },
item: ParsedItem
): { roll: StatRoll, sources: StatSource[] } {
const { incr, flat, sources } = calcPropBase(statRefs, item)
const base = calcBase(total, incr.value, flat.value)
return {
roll: {
value: calcIncreased(base + flat.value, incr.value),
min: calcIncreased(base + flat.min, incr.min),
max: calcIncreased(base + flat.max, incr.max)
},
sources: (statRefs.incr.length && statRefs.flat.length)
? sources.map(source => ({ ...source, contributes: undefined }))
: (statRefs.incr.length)
? sources.map(source => ({
...source,
contributes: {
value: base * source.contributes!.value / 100,
min: base * source.contributes!.min / 100,
max: base * source.contributes!.max / 100
}
}))
: sources
}
}
export function calcPropPercentile (
total: number,
bounds: [min: number, max: number],
statRefs: { flat: string[], incr: string[] },
item: ParsedItem
): number {
const { incr, flat } = calcPropBase(statRefs, item)
const roll = calcBase(total, incr.value + (item.quality ?? 0), flat.value)
const [min, max] = bounds
const result = Math.round(((roll - min) / (max - min)) * 100)
return Math.min(Math.max(result, 0), 100)
}
function calcPropBase (
statRefs: { flat: string[], incr: string[] },
item: ParsedItem
) {
const incr: StatRoll = { value: 0, min: 0, max: 0 }
const flat: StatRoll = { value: 0, min: 0, max: 0 }
const sources: StatSource[] = []
for (const calc of item.statsByType) {
let total: StatRoll
if (statRefs.flat.includes(calc.stat.ref)) {
total = flat
} else if (statRefs.incr.includes(calc.stat.ref)) {
total = incr
} else {
continue
}
const roll = statSourcesTotal(calc.sources)!
total.value += roll.value
total.min += roll.min
total.max += roll.max
sources.push(...calc.sources)
}
return { incr, flat, sources }
}
function calcBase (total: number, incr: number, flat: number) {
return (total / (1 + incr / 100)) - flat
}
function calcIncreased (flat: number, incr: number) {
return flat * (1 + incr / 100)
}