update mod parser impl

This commit is contained in:
Alexander Drozdov
2020-01-24 01:40:46 +02:00
parent eaf4cbeead
commit 23b81a86af
4 changed files with 132 additions and 63 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
<template>
<div>
<div v-for="modifier in item.modifiers" :key="`${modifier.mod.text}${modifier.type}`"
<div v-for="modifier in item.modifiers" :key="`${modifier.modInfo.text}${modifier.type}`"
class="py-2 border-b border-gray-700">
<div class="pb-1 flex items-baseline">
<i class="fas fa-check-square w-5"></i>
<span class="">{{ modifier.mod.text }}</span>
<span class="">{{ modifier.modInfo.text }}</span>
</div>
<div class="flex">
<div class="w-5"></div>
+23 -60
View File
@@ -14,9 +14,10 @@ import {
PREFIX_SUPERIOR,
SUFFIX_INFLUENCE
} from './parser-constants'
import { Prophecies, ItemisedMonsters, Mods, Mod } from '../data'
import { Prophecies, ItemisedMonsters } from '../data'
import { getDetailsId, nameToDetailsId } from './trends/getDetailsId'
import { ItemInfo, Prices } from './Prices'
import { ItemModifier, ModifierType, sectionToStatStrings, tryFindModifier } from './parser/modifiers'
export {
ItemRarity,
@@ -37,11 +38,7 @@ export interface ParsedItem {
gemLevel?: number
influences: ItemInfluence[]
rawText: string
modifiers: Array<{
mod: Mod
values?: number[]
type: ModifierType
}>
modifiers: ItemModifier[]
computed: {
category?: ItemCategory
mapName?: string
@@ -56,14 +53,6 @@ export enum ItemCategory {
ItemisedMonster = 'Itemised Monster'
}
export enum ModifierType {
Pseudo = 'pseudo',
Explicit = 'explicit',
Implicit = 'implicit',
Crafted = 'crafted',
Enchant = 'enchant'
}
const SECTION_PARSED = 1
const SECTION_SKIPPED = 0
const PARSER_SKIPPED = -1
@@ -332,56 +321,27 @@ function parseModifiers (section: string[], item: ParsedItem) {
const countBefore = item.modifiers.length
for (let line of section) {
const statIterator = sectionToStatStrings(section)
let stat = statIterator.next()
while (!stat.done) {
let modType: ModifierType | undefined
let mod: Mod | undefined
const values: number[] = []
let mod: ItemModifier | undefined
// cleanup suffix
if (line.endsWith(IMPLICIT_SUFFIX)) {
line = line.slice(0, -IMPLICIT_SUFFIX.length)
if (stat.value.endsWith(IMPLICIT_SUFFIX)) {
stat.value = stat.value.slice(0, -IMPLICIT_SUFFIX.length)
modType = ModifierType.Implicit
} else if (line.endsWith(CRAFTED_SUFFIX)) {
line = line.slice(0, -CRAFTED_SUFFIX.length)
} else if (stat.value.endsWith(CRAFTED_SUFFIX)) {
stat.value = stat.value.slice(0, -CRAFTED_SUFFIX.length)
modType = ModifierType.Crafted
}
// 1. try to find "as is"
mod = Mods.get(line)
if (!mod) {
// 2. replace
// example: +#%, #%, #, +#, # to #, #-#
line = line.replace(/(?<![\d])[+-]?[\d.]+/g, (value) => {
values.push(Number(value))
return '#'
})
if (values.length !== 1 && values.length !== 2) {
continue
}
// 3. match variants
// @TODO: IMPORTANT! distinguish between local and global mods
const variants = [
{ invertSign: false, text: line },
{ invertSign: false, text: line + ' (Local)' },
{ invertSign: false, text: line.replace('#', '+#') },
{ invertSign: false, text: line.replace('#', '+#') + ' (Local)' },
{ invertSign: true, text: line.replace('#% reduced', '#% increased') },
{ invertSign: true, text: line.replace('#% reduced', '#% increased') + ' (Local)' }
]
for (const variant of variants) {
mod = Mods.get(variant.text)
if (mod) {
if (variant.invertSign) {
values[0] *= -1
}
break
}
}
}
mod = tryFindModifier(stat.value)
if (mod) {
// @TODO: IMPORTANT! distinguish between local and global mods
if (modType == null) {
for (const type of mod.types) {
for (const type of mod.modInfo.types) {
if (
type.name !== ModifierType.Pseudo &&
type.name !== ModifierType.Implicit &&
@@ -393,11 +353,14 @@ function parseModifiers (section: string[], item: ParsedItem) {
}
}
item.modifiers.push({
mod,
type: modType!,
values: values.length ? values : undefined
})
mod.type = modType!
if (mod.modInfo.types.find(type => type.name === mod!.type)!.tradeId) {
item.modifiers.push(mod)
}
stat = statIterator.next(true)
} else {
stat = statIterator.next(false)
}
}
+95
View File
@@ -0,0 +1,95 @@
import { Mods, Mod, StatMatcher } from '../../data'
export enum ModifierType {
Pseudo = 'pseudo',
Explicit = 'explicit',
Implicit = 'implicit',
Crafted = 'crafted',
Enchant = 'enchant'
}
export interface ItemModifier {
modInfo: Mod
values?: number[]
option?: StatMatcher['option']
condition?: StatMatcher['condition']
type: ModifierType
}
export function * sectionToStatStrings (section: string[]) {
let idx = 0
let multi = (idx + 1) < section.length
while (idx < section.length) {
const str = multi ? `${section[idx]}\n${section[idx + 1]}` : section[idx]
const isParsed: boolean = yield str
if (isParsed) {
idx += multi ? 2 : 1
multi = (idx + 1) < section.length
} else {
if (multi) {
multi = false
} else {
idx += 1
multi = (idx + 1) < section.length
}
}
}
return true
}
const PLACEHOLDER_MAP = [
// 0 #
[[]],
// 1 #
[[0], []],
// 2 #
[[0, 1], [0], [1], []],
// 3 #
[[0, 1, 2], [1, 2], [0, 2], [0, 1], [2], [1], [0]],
// 4 #
[[0, 1, 2, 3], [1, 2, 3], [0, 2, 3], [0, 1, 3], [0, 1, 2], [2, 3], [1, 3], [1, 2], [0, 3], [0, 2], [0, 1]]
]
export function tryFindModifier (stat: string): ItemModifier | undefined {
const matches = [] as string[]
const withPlaceholders = stat
.replace(/[ ]{2,}/gm, ' ') // required to align with how "mods.json" was generated
.replace(/(?<![\d#])[+-]?[\d.]+/gm, (value) => {
matches.push(value)
return '#'
})
if (matches.length > PLACEHOLDER_MAP.length) return
const comboVariants = PLACEHOLDER_MAP[matches.length]
for (const combo of comboVariants) {
let pIdx = -1
const possibleStat = withPlaceholders.replace(/#/gm, () => {
pIdx += 1
if (combo.includes(pIdx)) {
return matches[pIdx]
} else {
return '#'
}
})
const found = Mods.get(possibleStat)
if (found) {
const values = matches
.filter((_, idx) => !combo.includes(idx))
.map(str => Number(str) * (found.condition.negate ? -1 : 1))
return {
modInfo: found.mod,
values: values.length ? values : undefined,
option: found.condition.option,
condition: found.condition.condition,
type: undefined!
}
}
}
}
+12 -1
View File
@@ -25,4 +25,15 @@ export interface Mod {
export const Prophecies = new Set(prophecies as string[])
export const ItemisedMonsters = new Set(monsters as string[])
export const Mods = new Map<string, Mod>(mods)
// Mods
export const Mods = new Map<string, { condition: StatMatcher, mod: Mod }>()
for (const entry of (mods as Array<{ conditions: StatMatcher[], mod: Mod }>)) {
for (const condition of entry.conditions) {
Mods.set(condition.string, {
condition,
mod: entry.mod
})
}
}