map check changes

This commit is contained in:
Alexander Drozdov
2021-04-30 16:24:06 +03:00
parent 86197f85f7
commit 2af746f173
6 changed files with 113 additions and 220 deletions
+7 -21
View File
@@ -62,7 +62,7 @@ type WidgetWellKnownFlag =
'hide-on-focus'
export const defaultConfig: Config = {
configVersion: 7,
configVersion: 8,
priceCheckKey: 'D',
priceCheckKeyHold: 'Ctrl',
priceCheckLocked: 'Ctrl + Alt + D',
@@ -148,41 +148,27 @@ export const defaultConfig: Config = {
wmZorder: 'exclusive',
wmFlags: ['hide-on-blur', 'skip-menu'],
maps: {
showNewStats: false,
selectedStats: [
{
matcher: 'Slaying Enemies close together has a #% chance to attract monsters from Beyond',
invert: false,
valueWarning: '',
valueDanger: '',
valueDesirable: '+'
decision: 'desirable'
},
{
matcher: '#% maximum Player Resistances',
invert: true,
valueWarning: '-10',
valueDanger: '',
valueDesirable: ''
decision: 'warning'
},
{
matcher: 'Monsters reflect #% of Physical Damage',
invert: false,
valueWarning: '',
valueDanger: '+',
valueDesirable: ''
decision: 'danger'
},
{
matcher: 'Monsters reflect #% of Elemental Damage',
invert: false,
valueWarning: '',
valueDanger: '+',
valueDesirable: ''
decision: 'danger'
},
{
matcher: 'Area contains two Unique Bosses',
invert: false,
valueWarning: '',
valueDanger: '',
valueDesirable: '+'
decision: 'desirable'
}
]
}
+16 -1
View File
@@ -118,7 +118,7 @@ export const config = (() => {
if (config.configVersion < 7) {
const mapCheck = config.widgets.find(w => w.wmType === 'map-check')!
mapCheck.wmType = 'item-check'
;(mapCheck as ItemCheckWidget).maps = { selectedStats: mapCheck.selectedStats }
mapCheck.maps = { selectedStats: mapCheck.selectedStats }
mapCheck.selectedStats = undefined
config.itemCheckKey = (config as any).mapCheckKey || null
@@ -127,6 +127,21 @@ export const config = (() => {
config.configVersion = 7
}
if (config.configVersion < 8) {
const itemCheck = config.widgets.find(w => w.wmType === 'item-check')!
;(itemCheck as ItemCheckWidget).maps.showNewStats = false
itemCheck.maps.selectedStats = (itemCheck as ItemCheckWidget).maps.selectedStats.map(entry => ({
matcher: entry.matcher,
decision:
(entry as any).valueDanger ? 'danger'
: (entry as any).valueWarning ? 'warning'
: (entry as any).valueDesirable ? 'desirable'
: 'seen'
}))
config.configVersion = 8
}
store.store = config
return store
})()
+39 -81
View File
@@ -2,24 +2,24 @@
<div class="flex">
<button @click="handleClick"
class="px-2 leading-none py-2 m-0 text-left flex-1 flex items-center overflow-hidden"
:class="state === 'danger' ? 'bg-red-700'
: state === 'warning' ? 'bg-orange-600'
: state === 'desirable' ? 'bg-green-700'
:class="decision === 'danger' ? 'bg-red-700'
: decision === 'warning' ? 'bg-orange-600'
: decision === 'desirable' ? 'bg-green-700'
: 'hover:bg-gray-700'">
<template v-if="true">
<i v-if="!state || state === 'not-matched'"
<i v-if="!decision || decision === 'seen'"
class="inline-block mr-2" style="min-width: 1rem;">{{ '\u2009' }}</i>
<i v-else
class="fas mr-2 text-center" style="min-width: 1rem;"
:class="state === 'danger' ? 'fa-skull-crossbones'
: state === 'warning' ? 'fa-exclamation-triangle'
:class="decision === 'danger' ? 'fa-skull-crossbones'
: decision === 'warning' ? 'fa-exclamation-triangle'
: 'fa-check'"></i>
</template>
<item-modifier-text :text="stat.matcher" :roll="stat.roll" class="truncate" />
</button>
<div class="flex leading-none items-center text-gray-600 w-8 text-center justify-center">
<i v-if="state === 'not-matched'" class="fas fa-eye-slash"></i>
</div>
<button @click="toggleSeenStatus" class="flex leading-none items-center text-gray-600 w-8 text-center justify-center">
<i v-if="showNewStatIcon" class="fas fa-eye-slash"></i>
</button>
</div>
</template>
@@ -42,89 +42,47 @@ export default defineComponent({
const config = computed(() => {
return Config.store.widgets.find(widget => widget.wmType === 'item-check') as ItemCheckWidget
})
const entryInSelected = computed(() => {
const entry = computed(() => {
return config.value.maps.selectedStats.find(_ => _.matcher === props.stat.matcher)
})
const state = computed(() => {
if (!entryInSelected.value) return undefined
const valueDanger = Number(entryInSelected.value.valueDanger || 'NaN')
const valueWarning = Number(entryInSelected.value.valueWarning || 'NaN')
const valueDesirable = Number(entryInSelected.value.valueDesirable || 'NaN')
if (entryInSelected.value.valueDanger !== '' && Number.isNaN(valueDanger)) {
return 'danger'
}
if (entryInSelected.value.valueWarning !== '' && Number.isNaN(valueWarning)) {
return 'warning'
}
if (entryInSelected.value.valueDesirable !== '' && Number.isNaN(valueDesirable)) {
return 'desirable'
}
if (!entryInSelected.value.invert) {
if (!Number.isNaN(valueDanger) && props.stat.roll! >= valueDanger) {
return 'danger'
}
if (!Number.isNaN(valueWarning) && props.stat.roll! >= valueWarning) {
return 'warning'
}
if (!Number.isNaN(valueDesirable) && props.stat.roll! >= valueDesirable) {
return 'desirable'
}
} else {
if (!Number.isNaN(valueDanger) && props.stat.roll! <= valueDanger) {
return 'danger'
}
if (!Number.isNaN(valueWarning) && props.stat.roll! <= valueWarning) {
return 'warning'
}
if (!Number.isNaN(valueDesirable) && props.stat.roll! <= valueDesirable) {
return 'desirable'
}
}
return 'not-matched'
const showNewStatIcon = computed(() => {
return config.value.maps.showNewStats && !entry.value
})
const canCycle = computed(() => {
const values = [
entryInSelected.value!.valueDanger,
entryInSelected.value!.valueWarning,
entryInSelected.value!.valueDesirable
]
return (
values.filter(_ => _ === '+').length === 1 &&
values.filter(_ => _ === '').length === 2
)
})
function toggleSeenStatus () {
if (!config.value.maps.showNewStats) return
if (!entry.value) {
config.value.maps.selectedStats.push({
matcher: props.stat.matcher,
decision: 'seen'
})
} else if (entry.value.decision === 'seen') {
config.value.maps.selectedStats = config.value.maps.selectedStats.filter(selected => selected !== entry.value)
}
}
return {
state,
decision: computed(() => entry.value?.decision),
showNewStatIcon,
toggleSeenStatus,
handleClick () {
if (!entryInSelected.value) {
if (!entry.value) {
config.value.maps.selectedStats.push({
matcher: props.stat.matcher,
invert: false,
valueDanger: '+',
valueWarning: '',
valueDesirable: ''
decision: 'danger'
})
return
}
if (!canCycle.value) return
if (entryInSelected.value.valueDanger === '+') {
entryInSelected.value.valueDanger = ''
entryInSelected.value.valueWarning = '+'
} else if (entryInSelected.value.valueWarning === '+') {
entryInSelected.value.valueWarning = ''
entryInSelected.value.valueDesirable = '+'
} else if (entryInSelected.value.valueDesirable === '+') {
config.value.maps.selectedStats = config.value.maps.selectedStats.filter(selected => selected !== entryInSelected.value)
} else {
if (entry.value.decision === 'danger') {
entry.value.decision = 'warning'
} else if (entry.value.decision === 'warning') {
entry.value.decision = 'desirable'
} else if (entry.value.decision === 'desirable') {
entry.value.decision = 'seen'
} else if (entry.value.decision === 'seen') {
entry.value.decision = 'danger'
}
}
}
}
+2 -4
View File
@@ -53,12 +53,10 @@ export interface PriceCheckWidget extends Widget {
export interface ItemCheckWidget extends Widget {
maps: {
showNewStats: boolean
selectedStats: Array<{
matcher: string
invert: boolean
valueWarning: string
valueDanger: string
valueDesirable: string
decision: string
}>
}
}
+29 -89
View File
@@ -1,13 +1,12 @@
<template>
<div class="flex py-1 w-full" :class="$style.row">
<div class="flex-1 truncate px-2">{{ matcher }}</div>
<div class="flex" :class="{ [$style.controlsAutoHide]: !entryInSelected }">
<div class="pr-1"><ui-toggle v-model="invert" /></div>
<div><input v-model.trim="valueWarning" @focus="handleFocus($event, 'valueWarning')" :placeholder="t(!invert ? 'min' : 'max')" class="bg-gray-900 w-12 text-center rounded-l"></div>
<div><input v-model.trim="valueDanger" @focus="handleFocus($event, 'valueDanger')" :placeholder="t(!invert ? 'min' : 'max')" class="bg-gray-900 w-12 text-center mx-px"></div>
<div><input v-model.trim="valueDesirable" @focus="handleFocus($event, 'valueDesirable')" :placeholder="t(!invert ? 'min' : 'max')" class="bg-gray-900 w-12 text-center rounded-r"></div>
<div class="flex" :class="{ [$style.controlsAutoHide]: !removable }">
<ui-radio v-model="decision" value="warning" class="pl-1 w-7 mx-2"></ui-radio>
<ui-radio v-model="decision" value="danger" class="pl-1 w-7 mx-2"></ui-radio>
<ui-radio v-model="decision" value="desirable" class="pl-1 w-7 mx-2"></ui-radio>
<div class="flex w-6">
<button v-if="entryInSelected"
<button v-if="removable"
@click="remove"
class="mx-1 leading-none flex items-center"><i class="fas fa-times w-4"></i></button>
</div>
@@ -16,8 +15,7 @@
</template>
<script lang="ts">
import { defineComponent, computed, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { defineComponent, computed } from 'vue'
import { Config } from '@/web/Config'
import { ItemCheckWidget } from '../overlay/interfaces'
@@ -27,105 +25,47 @@ export default defineComponent({
matcher: {
type: String,
required: true
},
autoRemove: {
type: Boolean,
required: true
}
},
setup (props) {
const config = computed(() => {
return Config.store.widgets.find(widget => widget.wmType === 'item-check') as ItemCheckWidget
})
const entryInSelected = computed(() => {
const entry = computed(() => {
return config.value.maps.selectedStats.find(_ => _.matcher === props.matcher)
})
const invert = computed<boolean>({
get () { return entryInSelected.value?.invert ?? false },
set (value) { setProp('invert', value) }
})
const valueWarning = computed<string>({
get () { return entryInSelected.value?.valueWarning ?? '' },
set (value) { setProp('valueWarning', value) }
})
const valueDanger = computed<string>({
get () { return entryInSelected.value?.valueDanger ?? '' },
set (value) { setProp('valueDanger', value) }
})
const valueDesirable = computed<string>({
get () { return entryInSelected.value?.valueDesirable ?? '' },
set (value) { setProp('valueDesirable', value) }
})
function setProp<T extends keyof ItemCheckWidget['maps']['selectedStats'][number]> (
key: T,
value: ItemCheckWidget['maps']['selectedStats'][number][T]
) {
if (entryInSelected.value) {
entryInSelected.value[key] = value
if (props.autoRemove) {
removeIfNotUsed()
const decision = computed<undefined | string>({
get () {
return entry.value?.decision
},
set (value) {
if (entry.value) {
entry.value.decision = value!
} else {
config.value.maps.selectedStats.push({
matcher: props.matcher,
decision: value!
})
}
} else {
config.value.maps.selectedStats.push({
matcher: props.matcher,
invert: false,
valueWarning: '',
valueDanger: '',
valueDesirable: '',
...{ [key]: value }
})
}
}
function removeIfNotUsed () {
if (
entryInSelected.value &&
!entryInSelected.value.invert &&
entryInSelected.value.valueWarning === '' &&
entryInSelected.value.valueDanger === '' &&
entryInSelected.value.valueDesirable === ''
) {
remove()
}
}
})
function remove () {
if (!entryInSelected.value) return
config.value.maps.selectedStats = config.value.maps.selectedStats
.filter(selected => selected !== entryInSelected.value)
}
function handleFocus (e: FocusEvent, type: 'valueWarning' | 'valueDanger' | 'valueDesirable') {
const target = e.target as HTMLInputElement
if (target.value === '') {
;({
valueWarning,
valueDanger,
valueDesirable
})[type].value = '+'
nextTick(() => {
target.select()
})
} else {
target.select()
if (entry.value) {
decision.value = 'seen'
}
}
const { t } = useI18n()
const removable = computed(() => {
return entry.value && decision.value !== 'seen'
})
return {
t,
entryInSelected,
invert,
valueWarning,
valueDanger,
valueDesirable,
remove,
handleFocus
entry,
decision,
removable,
remove
}
}
})
+20 -24
View File
@@ -3,27 +3,16 @@
<div class="p-1 flex">
<input :placeholder="t('Search') + '...'" v-model="search" class="bg-gray-900 rounded px-1">
<ui-toggle v-model="onlySelected" class="mx-2">{{ t('Only selected') }}</ui-toggle>
<ui-popover>
<template #target>
<div class="flex items-center justify-center">
<i class="fas fa-question-circle leading-none mr-1"></i>{{ t('help') }}</div>
</template>
<template #content>
<div class="flex flex-col justify-center text-base"
v-html="t('help-text')">
</div>
</template>
</ui-popover>
<ui-toggle v-model="showNewStats" class="ml-12">{{ t('Show new stats') }}</ui-toggle>
</div>
<div class="flex font-bold py-1 shadow">
<div class="flex-1 px-2">{{ t('Stat (found: {0})', [filteredStats.length]) }}</div>
<div class="flex" style="padding-right: calc(0.875rem + 1.5rem);">
<div class="px-2">{{ t('Invert') }}</div>
<div class="w-12 bg-orange-600 rounded-l leading-none flex items-center justify-center">
<div class="w-8 mx-1 leading-none flex items-center justify-center bg-orange-600">
<i class="fas fa-exclamation-triangle"></i></div>
<div class="w-12 bg-red-700 mx-px leading-none flex items-center justify-center">
<div class="w-8 mx-0 leading-none flex items-center justify-center bg-red-700">
<i class="fas fa-skull-crossbones"></i></div>
<div class="w-12 bg-green-700 rounded-r leading-none flex items-center justify-center">
<div class="w-8 mx-1 leading-none flex items-center justify-center bg-green-700">
<i class="fas fa-check"></i></div>
</div>
</div>
@@ -36,8 +25,7 @@
>
<maps-stat-entry
:style="{ position: 'absolute', top: `${props.top}px` }"
:matcher="props.item.matchStr"
:auto-remove="!onlySelected" />
:matcher="props.item.matchStr" />
</virtual-scroll>
</div>
</template>
@@ -70,19 +58,32 @@ export default defineComponent({
return Config.store.widgets.find(widget => widget.wmType === 'item-check') as ItemCheckWidget
})
const selectedMatchers = computed(() => {
return new Set(
config.value.maps.selectedStats
.filter(entry => entry.decision !== 'seen')
.map(entry => entry.matcher))
})
const showNewStats = computed<boolean>({
get () { return config.value.maps.showNewStats },
set (value) { config.value.maps.showNewStats = value }
})
const { t } = useI18n()
return {
t,
search,
onlySelected,
showNewStats,
filteredStats: computed(() => {
const q = search.value.toLowerCase().split(' ')
return statList.value.filter(stat =>
q.every(part => stat.searchStr.includes(part)) &&
(onlySelected.value
? (config.value.maps.selectedStats.some(selected => selected.matcher === stat.matchStr))
? selectedMatchers.value.has(stat.matchStr)
: true)
)
}),
@@ -96,15 +97,10 @@ export default defineComponent({
<i18n>
{
"en": {
"help-text": "- Use \"Invert\" toggle on stats like \"-7% maximum Player Resistances\".<br>- Non-numeric values are valid (for example, \"+\"), it just means<br>highlight stat without taking into account rolled value."
},
"ru": {
"Only selected": "Только выбранные",
"help": "справка",
"Stat (found: {0})": "Свойства (найдено: {0})",
"Invert": "Инвертировать",
"help-text": "- Используйте переключатель \"Инвертировать\" на таких свойствах как \"-7% максимум сопротивлений игроков\".<br>- Нечисловые значения действительны (например, \"+\"), это просто означает<br>подсветить свойство не обращая внимание на ролл."
"Show new stats": "Показывать новые статы"
}
}
</i18n>