-
-
-
- {{ $t('units.level') + ' ' + (index + 1) }}
-
-
-
-
-
-
- {{ `(${bond.value}%)` }}
-
-
-
-
- {{ $t('stats.' + bond.name) }}
-
-
- {{
- `${$t('stats.' + bond.name)}: ${getStatValueName(bond.name, bond.value)}`
- }}
-
-
-
-
+
+
+
+
+ {{ bonds.key }}
+
+
+
+
+
+ {{ bond.name }}
+
+
+
-
-
-
-
+
+
+
+
diff --git a/src/components/cards/MainCard.vue b/src/components/cards/MainCard.vue
index e5c9cf6..6f2910b 100644
--- a/src/components/cards/MainCard.vue
+++ b/src/components/cards/MainCard.vue
@@ -1,20 +1,20 @@
@@ -41,12 +41,12 @@ const defaults: DefaultsOptions = {
- {{ $t("meta.follower") }}
+ {{ $t('meta.follower') }}
- {{ $t("follower." + entry.follower) }}
+ {{ $t('follower.' + entry.follower) }}
diff --git a/src/components/chips/EntryChip.vue b/src/components/chips/EntryChip.vue
deleted file mode 100644
index 3519411..0000000
--- a/src/components/chips/EntryChip.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
diff --git a/src/components/chips/StatsChips.vue b/src/components/chips/StatsChips.vue
index 409f818..94d1a27 100644
--- a/src/components/chips/StatsChips.vue
+++ b/src/components/chips/StatsChips.vue
@@ -1,9 +1,9 @@
-
+
diff --git a/src/components/home/CodexPagination.vue b/src/components/home/CodexPagination.vue
index be86094..9f54eb2 100644
--- a/src/components/home/CodexPagination.vue
+++ b/src/components/home/CodexPagination.vue
@@ -42,19 +42,19 @@ function jumpPage() {
variant: 'tonal',
rounded: 'lg',
color: 'secondary',
+ size: 'large',
},
}"
>
-
+
{{ props.page }} / {{ props.pages }}
@@ -77,7 +71,7 @@ function jumpPage() {
{
- jumpPage()
- isActive.value = false
- }
- "
+ @keydown.enter="(jumpPage(), (isActive.value = false))"
>
{
- jumpPage()
- isActive.value = false
- }
- "
- >
+ @click.stop="(jumpPage(), (isActive.value = false))"
+ />
@@ -112,7 +98,6 @@ function jumpPage() {
-
-
+
+
{{ entry.tierName }}
- -
- {{ getOptionValueName('category', entry.category) }}
+ /
+ {{ getOptionValueName('category', entry.category) }}
- -
+ /
{{ getOptionName('exotic') }}
-
+
@@ -66,6 +67,9 @@ const settings = useSettingsStore()
+
+
+
diff --git a/src/components/settings/AppSettings.vue b/src/components/settings/AppSettings.vue
index 2eb5d3a..8b0ea06 100644
--- a/src/components/settings/AppSettings.vue
+++ b/src/components/settings/AppSettings.vue
@@ -52,6 +52,18 @@ const defaults: DefaultsOptions = {
+
+
+
+
+
+
+
+
-
+
diff --git a/src/components/shared/EffectViewer.vue b/src/components/shared/EffectViewer.vue
index 3d27d1b..823aba5 100644
--- a/src/components/shared/EffectViewer.vue
+++ b/src/components/shared/EffectViewer.vue
@@ -3,29 +3,42 @@ import type { CodexEntry } from '@/utils/codex'
import SettingDialog from './SettingDialog.vue'
import type { DefaultsOptions } from 'vuetify/lib/composables/defaults.mjs'
import { getOptionValueName, getIcon } from '@/utils'
-import { isUndefined } from 'es-toolkit'
-const props = defineProps({
- entry: {
- type: Object as PropType,
- required: true,
- },
-})
-
-const debuffSkills = computed(() => props.entry.debuffSkills)
-const buffSkills = computed(() => props.entry.buffSkills)
+const props = defineProps<{
+ entry: CodexEntry
+}>()
const defaults: DefaultsOptions = {
VChip: {
size: 'small',
rounded: 'lg',
- color: 'white',
- variant: 'outlined',
},
VListItem: {
class: 'pa-2',
},
}
+
+const sections = computed(() => {
+ const list = []
+ const debuffSkills = props.entry.getEffectSkills('debuff')
+ if (debuffSkills.size > 0) {
+ list.push({
+ title: 'meta.causes',
+ data: debuffSkills
+ })
+ }
+
+ if (props.entry.category === 'followers') {
+ const buffSkills = props.entry.getEffectSkills('buff')
+ if (buffSkills.size > 0) {
+ list.push({
+ title: 'meta.gives',
+ data: buffSkills
+ })
+ }
+ }
+ return list
+})
@@ -69,58 +82,26 @@ const defaults: DefaultsOptions = {
-
-
- {{ $t('helper.notFound') }}
-
-
-
-
-
-
- {{ $t('status.' + debuff) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ `(${skill.chance}%)` }}
-
-
-
-
-
-
-
-
-
-
+
{{ $t('helper.notFound') }}
-
-
-
+
+
+
+
+ {{ $t('status.' + statusId) }}
+
-
+
diff --git a/src/components/shared/GearViewer.vue b/src/components/shared/GearViewer.vue
index 347499e..f52d97a 100644
--- a/src/components/shared/GearViewer.vue
+++ b/src/components/shared/GearViewer.vue
@@ -240,7 +240,7 @@ function setQuality(n: number) {
class="text-right text-body-medium text-high-emphasis"
>
- {{ getStatValueName(key, stat) }}
+ {{ getStatValueName('stats.' + key, stat) }}
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 08ef2eb..59c33d4 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -39,7 +39,9 @@
"showPage": "Show Entries Pagination",
"showPageSubtitle": "Use pagination instead of infinite scroll to show entries",
"showStats": "Show Entries Stats",
- "showStatsSubtitle": "Show stats of entry"
+ "showStatsSubtitle": "Show stats of entry",
+ "showStatus": "Show Entries Status",
+ "showStatusSubtitle": "Show Entries Status"
},
"credit": {
"title": "Credit",
@@ -99,7 +101,7 @@
"setQualityCode": "Default Quality",
"title": "Assess",
"gearChoice": "Choice Gear",
- "viewGear": "VIew Gear"
+ "viewGear": "Gear Stats"
},
"bossScaling": {
"name": {
diff --git a/src/i18n/messages/zh-hans.json b/src/i18n/messages/zh-hans.json
index 83c2c9d..b614d4c 100644
--- a/src/i18n/messages/zh-hans.json
+++ b/src/i18n/messages/zh-hans.json
@@ -46,7 +46,9 @@
"showPage": "显示条目分页",
"showPageSubtitle": "以分页形式代替无限滚动来显示条目",
"showStats": "显示条目属性",
- "showStatsSubtitle": "显示条目属性"
+ "showStatsSubtitle": "显示条目属性",
+ "showStatusSubtitle": "显示条目的状态效果",
+ "showStatus": "显示条目的状态效果"
},
"credit": {
"website": "官方网站",
diff --git a/src/i18n/messages/zh-hant.json b/src/i18n/messages/zh-hant.json
index da7ce9d..8b80330 100644
--- a/src/i18n/messages/zh-hant.json
+++ b/src/i18n/messages/zh-hant.json
@@ -143,7 +143,9 @@
"forceUpdateSubtitle": "清除數據以重置應用",
"showDetail": "顯示詳細標簽",
"showStats": "顯示條目屬性",
- "title": "通用"
+ "title": "通用",
+ "showStatus": "顯示條目的狀態效果",
+ "showStatusSubtitle": "顯示條目的狀態效果"
},
"locale": {
"applySetting": "應用語言設置",
diff --git a/src/stores/codex.ts b/src/stores/codex.ts
index d192e24..7d5ccbf 100644
--- a/src/stores/codex.ts
+++ b/src/stores/codex.ts
@@ -9,9 +9,8 @@ import {
} from '@/types/codex'
import type { CodexEntry } from '@/utils/codex'
import { isNull, isUndefined, pick, zipObject } from 'es-toolkit'
-import { i18n } from '@/i18n'
+import { i18n, locales as supportedLocales } from '@/i18n'
import { fetchCodexData, fetchCodexI18n, fetchCodexUpdate } from '@/api/codex'
-import { supportedLocales } from '@/i18n/config'
import diff from 'semver/functions/diff'
import config from '@/config'
@@ -21,7 +20,6 @@ export const useCodexState = defineStore('codex', () => {
options: {},
sorts: {},
ability_stats: {},
- attached_ability: {},
value_types: {},
})
@@ -185,7 +183,6 @@ export const useCodexState = defineStore('codex', () => {
'options',
'sorts',
'ability_stats',
- 'attached_ability',
'value_types',
]
const cache = await yacoStore.getItems(metaKeys)
diff --git a/src/stores/settings.ts b/src/stores/settings.ts
index dfe72e3..a8a277b 100644
--- a/src/stores/settings.ts
+++ b/src/stores/settings.ts
@@ -5,6 +5,7 @@ const defaultSettings = {
detail: true,
page: false,
material: false,
+ status: false,
},
enemyEditor: false,
}
diff --git a/src/types/assess.ts b/src/types/assess.ts
index e926ac7..5697f2a 100644
--- a/src/types/assess.ts
+++ b/src/types/assess.ts
@@ -20,7 +20,7 @@ export interface AssessQuery {
isCelestialWeapon: boolean
isTwoHanded: boolean
isUpgradable: boolean
- isOffHand: boolean
+ hasScalingSlots: boolean
}
}
}
diff --git a/src/types/codex.ts b/src/types/codex.ts
index a6593db..09d79bc 100644
--- a/src/types/codex.ts
+++ b/src/types/codex.ts
@@ -17,7 +17,6 @@ export interface CodexData {
options: Record>
sorts: Record>
ability_stats: Record
- attached_ability: Record
value_types: Record
}
}
@@ -71,11 +70,12 @@ export type BestialBond =
| {
name: string
type: 'BONUS'
- value: StatValue
+ value: string | number | boolean
}
| {
name: string
type: 'ABILITY'
+ key?: string
}
export interface Status {
@@ -94,7 +94,12 @@ export interface PendingUpdate {
export type StatusKey = 'immunities' | 'gives' | 'causes' | 'cures' | 'summons'
-export type StatValue = string | number | boolean | Array | Array
+export interface Spell {
+ name: string
+ key?: string
+}
+
+export type StatValue = string | number | boolean | Array | Array | Array
export type BooleanValue = 1 | 0
@@ -105,7 +110,7 @@ export type CodexEntryKey = string
export interface CodexEntryRaw {
category: string
id: string
- tier?: number
+ tier: number
///
aura?: string
icon?: string
diff --git a/src/utils/assess.ts b/src/utils/assess.ts
index a80e0d1..c8c0161 100644
--- a/src/utils/assess.ts
+++ b/src/utils/assess.ts
@@ -148,7 +148,9 @@ export function getItemQuality(input: number, base: number, level: number, isBos
})
}
-export function getAdditionalSlots(quality: number) {
+export function getAdditionalSlots(quality: number, level?: number) {
+ if (level === 13) return 4
+ if (level === 11 || level === 12) return 3
if (quality >= 170) return 2
if (quality > 100) return 1
return 0
@@ -223,25 +225,22 @@ export function getQualityName(code: Quality) {
return 'quality.' + name
}
-const smallBonusScalings: Record = {
- no_follower_bonus: 1 / 5,
-}
-
const qualityCodeBonusKeySet = new Set([
'orn_bonus',
'exp_bonus',
'luck_bonus',
'gold_bonus',
'monster_encounters',
- 'no_follower_bonus',
'manaflask_power',
'apex',
+ 'no_follower_bonus',
'bestial_bond',
'player_r_follower_ability_chance',
'mana_overspend_chance',
])
const noBaseBonusKeySet = new Set([
+ 'no_follower_bonus',
'bestial_bond',
'player_r_follower_ability_chance',
'mana_overspend_chance',
@@ -280,9 +279,7 @@ export function getQualityBonus(
return (base * (qualityScaling + 100)) / 100
}
- const bonusScaling = smallBonusScalings[key] ?? 1
-
- return ((100 + base) * (100 + qualityScaling * bonusScaling) - 100 * 100) / 100
+ return ((100 + base) * (100 + qualityScaling) - 100 * 100) / 100
}
return base
@@ -299,8 +296,7 @@ function getQueryMeta(entry: T): AssessQuery['meta'] {
isTwoHanded: entry.isTwoHanded,
// levels: 1 | 13
isUpgradable: entry.isUpgradable,
- // offhand: no +2 slots
- isOffHand: entry.isOffHand,
+ hasScalingSlots: entry.hasScalingSlots,
},
}
}
@@ -360,8 +356,8 @@ export function getAssessResult(
result.exact = currentStat === maxValue
// get range for not exact quality
if (result.exact && Math.abs(maxValue) < 100) {
- const left = Math.ceil(((currentStat + (currentStat > 0 ? -1 : 0)) / baseStat) * 100)
- const right = Math.ceil(((currentStat + (currentStat > 0 ? 0 : 1)) / baseStat) * 100)
+ const left = getItemQuality((currentStat + (currentStat > 0 ? -1 : 0)), baseStat, input.level, input.bossScaling > 0)
+ const right = getItemQuality((currentStat + (currentStat > 0 ? 0 : 1)), baseStat, input.level, input.bossScaling > 0)
const leftOut = getUpgradedStatFunc(left)
const rightOut = getUpgradedStatFunc(right)
const normalize = (n: number, out: number) => {
@@ -416,12 +412,7 @@ export function getAssessResult(
// adornment slots
const baseAdornmentSlots = get(meta.baseStats, 'adornment_slots', 0) as number
- if (meta.flags.isOffHand) {
- result.stats['adornment_slots'] = {
- base: baseAdornmentSlots,
- values: fill(Array(result.levels), baseAdornmentSlots),
- }
- } else {
+ if (meta.flags.hasScalingSlots) {
if (result.angLevel > 0) {
result.stats['adornment_slots'] = {
base: baseAdornmentSlots,
@@ -439,6 +430,11 @@ export function getAssessResult(
).concat([baseAdornmentSlots + 3, baseAdornmentSlots + 3, baseAdornmentSlots + 4]),
}
}
+ } else {
+ result.stats['adornment_slots'] = {
+ base: baseAdornmentSlots,
+ values: fill(Array(result.levels), baseAdornmentSlots),
+ }
}
}
@@ -494,5 +490,31 @@ export function getFullResult(input: AssessQuery['input']): FullResult {
}
})
+ // adornment_slots
+ if (meta.flags.isUpgradable) {
+ const slots_key = 'adornment_slots'
+
+ if (meta.flags.isCelestialWeapon) {
+ result.stats[slots_key] =
+ (celestialWeaponSlots[input.level] ?? 0) + (meta.flags.isTwoHanded ? 1 : 0)
+ } else {
+ const baseAdornmentSlots = get(meta.baseStats, slots_key, 0) as number
+ if (meta.flags.hasScalingSlots) {
+ if (result.angLevel > 0) {
+ result.stats[slots_key] = baseAdornmentSlots + 4
+ } else {
+ const additionalSlots = getAdditionalSlots(result.quality, result.level)
+ if (additionalSlots > 0 || baseAdornmentSlots > 0) {
+ result.stats[slots_key] = baseAdornmentSlots + additionalSlots
+ }
+ }
+ } else {
+ if (baseAdornmentSlots > 0) {
+ result.stats[slots_key] = baseAdornmentSlots
+ }
+ }
+ }
+ }
+
return result
}
diff --git a/src/utils/codex.ts b/src/utils/codex.ts
index 3331f21..4a71117 100644
--- a/src/utils/codex.ts
+++ b/src/utils/codex.ts
@@ -45,7 +45,7 @@ export class CodexEntry implements CodexEntryRaw {
constructor(
public category: string,
public id: string,
- public tier?: number,
+ public tier: number,
public aura?: string,
public icon?: string,
public stats?: Record,
@@ -226,8 +226,8 @@ export class CodexEntry implements CodexEntryRaw {
return this.isWeapon || this.isArmor || this.isOffHand
}
- get isUpgradableSlots() {
- return this.isWeapon || this.isArmor
+ get hasScalingSlots() {
+ return (this.isWeapon || this.isArmor) && this.tier > 2
}
get hasSkill() {
@@ -238,51 +238,33 @@ export class CodexEntry implements CodexEntryRaw {
return this.skills?.map((key) => getCodexEntryByKey(key)).filter((v) => !isUndefined(v))
}
- get debuffSkills() {
- const debuffMap = new Map>()
+ getEffectSkills(type: 'buff' | 'debuff') {
+ const statusKey = type === 'buff' ? 'gives' : 'causes'
+ const statusMap = new Map>()
if (isUndefined(this.skillsArray)) {
- return undefined
+ return statusMap
}
- this.skillsArray.forEach((entry) => {
- if (isUndefined(entry.causes)) {
- return
- }
-
- entry.causes.forEach((status) => {
- const a = debuffMap.get(status.name)
- const arr = a ?? Array<{ entry: CodexEntry; chance?: number }>()
- arr.push({ entry: entry, chance: status.chance })
- debuffMap.set(status.name, arr)
- })
- })
-
- return debuffMap
- }
- get buffSkills() {
- const buffMap = new Map>()
- if (isUndefined(this.skillsArray)) {
- return undefined
- }
this.skillsArray.forEach((entry) => {
- if (isUndefined(entry.gives)) {
+ const statuses = entry[statusKey]
+ if (isUndefined(statuses)) {
return
}
- entry.gives.forEach((status) => {
- const a = buffMap.get(status.name)
+ statuses.forEach((status) => {
+ const a = statusMap.get(status.name)
const arr = a ?? Array<{ entry: CodexEntry; chance?: number }>()
arr.push({ entry: entry, chance: status.chance })
- buffMap.set(status.name, arr)
+ statusMap.set(status.name, arr)
})
})
- return buffMap
+ return statusMap
}
get isAssessable() {
// include upgraded slots
- return this.isUpgradableSlots || Object.keys(this.stats ?? {}).some((k) => assessKeySet.has(k))
+ return this.hasScalingSlots || Object.keys(this.stats ?? {}).some((k) => assessKeySet.has(k))
}
get isComparable() {
diff --git a/src/utils/index.ts b/src/utils/index.ts
index b50d07b..e5345b5 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -69,14 +69,12 @@ export function getStatName(key: string) {
export function getValueType(key: string) {
const codexState = useCodexState()
- const valueType = get(
- codexState.meta.value_types,
- key.startsWith('stats.') ? key : 'stats.' + key,
- )
+ const valueType = get(codexState.meta.value_types, key)
return valueType
}
export function getStatValueName(key: string, value: StatValue | undefined) {
+ // key startswtih 'stats.' | 'abilities.' | 'bonds.'
const valueType = getValueType(key)
if (isUndefined(value)) {
@@ -113,7 +111,7 @@ export function getStatValueName(key: string, value: StatValue | undefined) {
}
if (typeof value === 'string') {
- if (key !== 'power') {
+ if (key !== 'stats.power') {
return i18n.global.t('stats_text.' + value)
}
}
@@ -153,11 +151,6 @@ export function getStatType(key: string): ValueType | undefined {
return get(codexState.meta.value_types, key)
}
-export function getAttachedAbilityKey(id: string) {
- const codexState = useCodexState()
- return get(codexState.meta.attached_ability, id)
-}
-
export function getBossScalingName(value: BossScaling) {
return i18n.global.t('bossScaling.name.' + value)
}
diff --git a/src/views/AssessView.vue b/src/views/AssessView.vue
index b431be5..2bd38f8 100644
--- a/src/views/AssessView.vue
+++ b/src/views/AssessView.vue
@@ -19,7 +19,7 @@ import {
mdiRestore,
mdiStarCircle,
} from '@mdi/js'
-import { isNull, isUndefined, range } from 'es-toolkit'
+import { isEmptyObject, isNull, isUndefined, range } from 'es-toolkit'
import colors from '@/styles/colors.module.scss'
import { useGoTo } from 'vuetify'
import type { DefaultsOptions } from 'vuetify/lib/composables/defaults.mjs'
@@ -161,15 +161,17 @@ const defaults: DefaultsOptions = {
+
-
+
+
-
-
+
+
-
+
{{ $t('assess.assess') }}
@@ -181,29 +183,20 @@ const defaults: DefaultsOptions = {
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
{{ $t('assess.bossScalingInput') }}
-
+
-
+
{{ entry?.name }}
+
@@ -346,15 +345,20 @@ const defaults: DefaultsOptions = {
-
-
- {{ `| ${(result.quality / 2).toFixed(1)}/100` }}
-
+
+
+
+ {{ result.quality + '%' }}
+
+
+
+ {{ (result.quality / 2).toFixed(1) + '/100' }}
+
+
diff --git a/src/views/CompareView.vue b/src/views/CompareView.vue
index ebbd9b4..ed5e8b7 100644
--- a/src/views/CompareView.vue
+++ b/src/views/CompareView.vue
@@ -103,7 +103,7 @@ function getSignedNumber(n: number) {
- {{ getStatValueName(key, stat.base) }}
+ {{ getStatValueName('stats.' + key, stat.base) }}
diff --git a/src/views/ProofView.vue b/src/views/ProofView.vue
index 9d0d8c3..9b237b9 100644
--- a/src/views/ProofView.vue
+++ b/src/views/ProofView.vue
@@ -142,11 +142,13 @@ function updateMaterialCount(id: string) {
+
-
+
+
-
-
+
+
@@ -162,12 +164,12 @@ function updateMaterialCount(id: string) {
clearable
/>
-
+
getTowerFloorsInNextDays(now.value, 7))
-
+
{{ now.toLocaleString() }}
@@ -45,7 +45,7 @@ const floorsInWeek = computed(() => getTowerFloorsInNextDays(now.value, 7))
-
+