fix: load localized map area names from tbl on transition (Fixes #147)
This commit is contained in:
commit
d5c3c1d316
|
|
@ -142,13 +142,13 @@ export const LEVEL_NAMES_ZH: Readonly<Record<number, string>> = {
|
|||
115: '冰河路径',
|
||||
116: '漂泊者洞穴',
|
||||
117: '冰冻苔原',
|
||||
118: '冰窖',
|
||||
119: '痛苦之厅',
|
||||
120: '岩石之巅',
|
||||
121: '尼拉塞克神殿',
|
||||
118: '远古之路',
|
||||
119: '冰窖',
|
||||
120: '亚瑞特山顶',
|
||||
121: '尼拉塞克的神殿',
|
||||
122: '悲痛之厅',
|
||||
123: '死者呼唤之厅',
|
||||
124: '沃特厅',
|
||||
123: '痛苦之厅',
|
||||
124: '沃德之厅',
|
||||
125: '艾巴当',
|
||||
126: '阿克隆地道',
|
||||
127: '炼狱地坑',
|
||||
|
|
@ -194,43 +194,6 @@ export function variantLabelZh(suffix: string): string {
|
|||
return DIRECTION_SUFFIXES[suffix.toLowerCase()] ?? suffix
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 pack 索引条目的 `slug` 里取 `Levels.txt` 的关卡 Id。
|
||||
*
|
||||
* @param slug - e.g. `33-act-1-cathedral`.
|
||||
* @returns the id, or null when the slug does not start with one.
|
||||
*/
|
||||
export function levelIdOfSlug(slug: string): number | null {
|
||||
const match = /^(\d+)-/.exec(slug)
|
||||
return match?.[1] === undefined ? null : Number(match[1])
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个场景的中文名,取不到就回退调用方给的英文名。
|
||||
* 支持传入自定义映射表或自动使用全局由 string.tbl 解码的名称,无匹配时回退到 LEVEL_NAMES_ZH 或 fallback。
|
||||
*
|
||||
* @param slug - the scene slug from the pack index.
|
||||
* @param fallback - the English name from the pack index.
|
||||
* @param customMap - optional custom map or record of levelId -> name.
|
||||
* @returns the display name.
|
||||
*/
|
||||
export function sceneNameZh(
|
||||
slug: string,
|
||||
fallback: string,
|
||||
customMap?: Map<number, string> | Record<number, string>,
|
||||
): string {
|
||||
const id = levelIdOfSlug(slug)
|
||||
if (id === null) return fallback
|
||||
if (customMap) {
|
||||
const val = customMap instanceof Map ? customMap.get(id) : customMap[id]
|
||||
if (val) return val
|
||||
}
|
||||
if (activeTblLevelNames?.has(id)) {
|
||||
return activeTblLevelNames.get(id)!
|
||||
}
|
||||
return LEVEL_NAMES_ZH[id] ?? fallback
|
||||
}
|
||||
|
||||
/** Canonical English level name keys corresponding to Levels.txt Id. */
|
||||
export const CANONICAL_LEVEL_NAME_KEYS: Readonly<Record<number, string>> = {
|
||||
1: 'Rogue Encampment',
|
||||
|
|
@ -355,24 +318,265 @@ export const CANONICAL_LEVEL_NAME_KEYS: Readonly<Record<number, string>> = {
|
|||
116: 'Drifter Cavern',
|
||||
117: 'Frozen Tundra',
|
||||
118: "The Ancients' Way",
|
||||
119: 'Arreat Summit',
|
||||
120: "Nihlathak's Temple",
|
||||
121: 'Halls of Anguish',
|
||||
122: 'Halls of Pain',
|
||||
123: 'Halls of Vaught',
|
||||
124: 'Abaddon',
|
||||
125: 'Pit of Acheron',
|
||||
126: 'Infernal Pit',
|
||||
127: 'Worldstone Keep Level 1',
|
||||
128: 'Worldstone Keep Level 2',
|
||||
129: 'Worldstone Keep Level 3',
|
||||
130: 'Throne of Destruction',
|
||||
131: 'Worldstone Chamber',
|
||||
132: "Matron's Den",
|
||||
133: 'Forgotten Sands',
|
||||
134: 'Furnace of Pain',
|
||||
135: 'Tristram',
|
||||
136: 'Chaos Tristram',
|
||||
119: 'Icy Cellar',
|
||||
120: 'Arreat Summit',
|
||||
121: "Nihlathak's Temple",
|
||||
122: 'Halls of Anguish',
|
||||
123: 'Halls of Pain',
|
||||
124: 'Halls of Vaught',
|
||||
125: 'Abaddon',
|
||||
126: 'Pit of Acheron',
|
||||
127: 'Infernal Pit',
|
||||
128: 'Worldstone Keep Level 1',
|
||||
129: 'Worldstone Keep Level 2',
|
||||
130: 'Worldstone Keep Level 3',
|
||||
131: 'Throne of Destruction',
|
||||
132: 'Worldstone Chamber',
|
||||
133: "Matron's Den",
|
||||
134: 'Forgotten Sands',
|
||||
135: 'Furnace of Pain',
|
||||
136: 'Tristram',
|
||||
}
|
||||
|
||||
/** Alias for CANONICAL_LEVEL_NAME_KEYS for symmetry with LEVEL_NAMES_ZH. */
|
||||
export const LEVEL_NAMES_EN: Readonly<Record<number, string>> = CANONICAL_LEVEL_NAME_KEYS
|
||||
|
||||
/**
|
||||
* Normalizes any level slug or name string for robust lookup.
|
||||
* e.g. "17-burial-grounds" -> "burialgrounds"
|
||||
* "rogue-encampment" -> "rogueencampment"
|
||||
* "The Ancients' Way" -> "theancientsway"
|
||||
*/
|
||||
function normalizeSlug(str: string): string {
|
||||
return str.toLowerCase().replace(/[^a-z0-9]/g, '')
|
||||
}
|
||||
|
||||
/** Check if a string looks like a code/slug rather than a human-readable title. */
|
||||
function isRawSlug(str: string): boolean {
|
||||
return str.includes('-') || str.includes('_') || /^[a-z0-9]+$/.test(str)
|
||||
}
|
||||
|
||||
function toTitleCase(slug: string): string {
|
||||
const clean = slug.replace(/^\d+[-_]/, '')
|
||||
return clean
|
||||
.split(/[-_\s]+/)
|
||||
.filter(Boolean)
|
||||
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
/** Pre-built map of normalized slugs/names to canonical level ID. */
|
||||
const SLUG_TO_LEVEL_ID: ReadonlyMap<string, number> = (() => {
|
||||
const map = new Map<string, number>()
|
||||
for (let id = 1; id <= 136; id++) {
|
||||
const en = CANONICAL_LEVEL_NAME_KEYS[id]
|
||||
if (en) {
|
||||
map.set(normalizeSlug(en), id)
|
||||
map.set(normalizeSlug(en.replace(/^The\s+/i, '')), id)
|
||||
}
|
||||
}
|
||||
// Common aliases in pack manifests
|
||||
const aliases: Record<string, number> = {
|
||||
'rogueencampment': 1,
|
||||
'bloodmoor': 2,
|
||||
'coldplains': 3,
|
||||
'stonyfield': 4,
|
||||
'darkwood': 5,
|
||||
'blackmarsh': 6,
|
||||
'tamoehighland': 7,
|
||||
'denofevil': 8,
|
||||
'cave1': 9,
|
||||
'cavelevel1': 9,
|
||||
'undergroundpassage1': 10,
|
||||
'undergroundpassagelevel1': 10,
|
||||
'hole1': 11,
|
||||
'holelevel1': 11,
|
||||
'pit1': 12,
|
||||
'pitlevel1': 12,
|
||||
'cave2': 13,
|
||||
'cavelevel2': 13,
|
||||
'undergroundpassage2': 14,
|
||||
'undergroundpassagelevel2': 14,
|
||||
'hole2': 15,
|
||||
'holelevel2': 15,
|
||||
'pit2': 16,
|
||||
'pitlevel2': 16,
|
||||
'burialgrounds': 17,
|
||||
'crypt': 18,
|
||||
'mausoleum': 19,
|
||||
'forgottentower': 20,
|
||||
'towercellar1': 21,
|
||||
'towercellar2': 22,
|
||||
'towercellar3': 23,
|
||||
'towercellar4': 24,
|
||||
'towercellar5': 25,
|
||||
'monasterygate': 26,
|
||||
'outercloister': 27,
|
||||
'barracks': 28,
|
||||
'jail1': 29,
|
||||
'jail2': 30,
|
||||
'jail3': 31,
|
||||
'innercloister': 32,
|
||||
'cathedral': 33,
|
||||
'catacombs1': 34,
|
||||
'catacombs2': 35,
|
||||
'catacombs3': 36,
|
||||
'catacombs4': 37,
|
||||
'tristram': 38,
|
||||
'moomoofarm': 39,
|
||||
'lutgholein': 40,
|
||||
'rockywaste': 41,
|
||||
'dryhills': 42,
|
||||
'faroasis': 43,
|
||||
'lostcity': 44,
|
||||
'valleyofsnakes': 45,
|
||||
'canyonofmagi': 46,
|
||||
'canyonofthemagi': 46,
|
||||
'harrogath': 109,
|
||||
'bloodyfoothills': 110,
|
||||
'frigidhighlands': 111,
|
||||
'arreatplateau': 112,
|
||||
'crystallinepassage': 113,
|
||||
'frozenriver': 114,
|
||||
'glacialtrail': 115,
|
||||
'driftercavern': 116,
|
||||
'frozentundra': 117,
|
||||
'ancientsway': 118,
|
||||
'theancientsway': 118,
|
||||
'icycellar': 119,
|
||||
'arreatsummit': 120,
|
||||
'nihlathakstemple': 121,
|
||||
'hallsofanguish': 122,
|
||||
'hallsofpain': 123,
|
||||
'hallsofvaught': 124,
|
||||
'abaddon': 125,
|
||||
'pitofacheron': 126,
|
||||
'infernalpit': 127,
|
||||
'worldstonekeep1': 128,
|
||||
'worldstonekeep2': 129,
|
||||
'worldstonekeep3': 130,
|
||||
'throneofdestruction': 131,
|
||||
'worldstonechamber': 132,
|
||||
}
|
||||
for (const [k, v] of Object.entries(aliases)) {
|
||||
map.set(normalizeSlug(k), v)
|
||||
}
|
||||
return map
|
||||
})()
|
||||
|
||||
/**
|
||||
* 从 pack 索引条目的 `slug` 或关卡代号里解析 `Levels.txt` 的关卡 Id。
|
||||
* 支持前缀格式(`17-burial-grounds`)以及无前缀纯文本 slug(`burial-grounds`、`rogue-encampment`)。
|
||||
*
|
||||
* @param slug - e.g. `17-burial-grounds` or `burial-grounds`.
|
||||
* @returns the id (1..136), or null when unresolvable.
|
||||
*/
|
||||
export function levelIdOfSlug(slug: string): number | null {
|
||||
const match = /^(\d+)(?:-|_|$)/.exec(slug)
|
||||
if (match?.[1] !== undefined) {
|
||||
const parsed = Number(match[1])
|
||||
if (parsed >= 1 && parsed <= 136) return parsed
|
||||
}
|
||||
const norm = normalizeSlug(slug)
|
||||
return SLUG_TO_LEVEL_ID.get(norm) ?? null
|
||||
}
|
||||
|
||||
export interface LevelNames {
|
||||
readonly nameZh: string
|
||||
readonly nameEn: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Bilingual level name lookup for UI banners and HUD displays.
|
||||
* Resolves both numeric level IDs (1..136) and string slugs (e.g. 'burial-grounds').
|
||||
* Returns authentic Chinese and English names, never empty, never leaking raw slugs.
|
||||
*/
|
||||
export function getLevelNames(levelIdOrSlug: number | string): LevelNames {
|
||||
let id: number | null = null
|
||||
let rawStr = ''
|
||||
|
||||
if (typeof levelIdOrSlug === 'number') {
|
||||
id = levelIdOrSlug
|
||||
rawStr = String(levelIdOrSlug)
|
||||
} else {
|
||||
rawStr = levelIdOrSlug.trim()
|
||||
id = levelIdOfSlug(rawStr)
|
||||
}
|
||||
|
||||
let nameZh: string | undefined
|
||||
let nameEn: string | undefined
|
||||
|
||||
if (id !== null) {
|
||||
nameZh = activeTblLevelNames?.get(id) ?? LEVEL_NAMES_ZH[id]
|
||||
nameEn = CANONICAL_LEVEL_NAME_KEYS[id]
|
||||
}
|
||||
|
||||
if (!nameEn) {
|
||||
nameEn = toTitleCase(rawStr) || 'Unknown Area'
|
||||
}
|
||||
if (!nameZh) {
|
||||
nameZh = nameEn
|
||||
}
|
||||
|
||||
return { nameZh, nameEn }
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个场景的中文名,取不到就回退调用方给的英文名。
|
||||
* 支持传入自定义映射表或自动使用全局由 string.tbl 解码的名称,无匹配时回退到 LEVEL_NAMES_ZH 或 fallback。
|
||||
* 防止原始无前缀 slug(如 'burial-grounds')直接泄露到 UI。
|
||||
*
|
||||
* @param slug - the scene slug from the pack index.
|
||||
* @param fallback - the English name from the pack index.
|
||||
* @param customMap - optional custom map or record of levelId -> name.
|
||||
* @returns the display name.
|
||||
*/
|
||||
export function sceneNameZh(
|
||||
slug: string | number,
|
||||
fallback?: string,
|
||||
customMap?: Map<number, string> | Record<number, string>,
|
||||
): string {
|
||||
const slugStr = String(slug)
|
||||
const id = typeof slug === 'number' ? slug : levelIdOfSlug(slugStr)
|
||||
if (id !== null) {
|
||||
if (customMap) {
|
||||
const val = customMap instanceof Map ? customMap.get(id) : customMap[id]
|
||||
if (val) return val
|
||||
}
|
||||
if (activeTblLevelNames?.has(id)) {
|
||||
return activeTblLevelNames.get(id)!
|
||||
}
|
||||
if (LEVEL_NAMES_ZH[id]) {
|
||||
return LEVEL_NAMES_ZH[id]!
|
||||
}
|
||||
}
|
||||
// Guard against leaking raw un-prefixed slugs
|
||||
if (fallback && !isRawSlug(fallback)) {
|
||||
return fallback
|
||||
}
|
||||
return getLevelNames(slug).nameZh
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个场景的英文名。
|
||||
*
|
||||
* @param slug - the scene slug from the pack index or numeric level ID.
|
||||
* @param fallback - optional fallback English string.
|
||||
* @returns canonical English level name.
|
||||
*/
|
||||
export function sceneNameEn(
|
||||
slug: string | number,
|
||||
fallback?: string,
|
||||
): string {
|
||||
const slugStr = String(slug)
|
||||
const id = typeof slug === 'number' ? slug : levelIdOfSlug(slugStr)
|
||||
if (id !== null && CANONICAL_LEVEL_NAME_KEYS[id]) {
|
||||
return CANONICAL_LEVEL_NAME_KEYS[id]!
|
||||
}
|
||||
if (fallback && !isRawSlug(fallback)) {
|
||||
return fallback
|
||||
}
|
||||
return getLevelNames(slug).nameEn
|
||||
}
|
||||
|
||||
function lookupTbl(tbl: D2Tbl | Map<string, string>, key: string): string | undefined {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ import { facingToDirection } from '../game/character.ts'
|
|||
import type { LoadedMonsterArt } from '../game/monster-art.ts'
|
||||
import { ActorAnimator, type AnimClipMeta } from '../game/actor-animator.ts'
|
||||
import { resolveMonsterArtSpec } from '../game/monster-mapping.ts'
|
||||
import { ACT_NAMES_ZH, LEVEL_NAMES_ZH, sceneNameZh, variantLabelZh } from '../game/level-names-zh.ts'
|
||||
import { ACT_NAMES_ZH, LEVEL_NAMES_ZH, getLevelNames, sceneNameZh, variantLabelZh } from '../game/level-names-zh.ts'
|
||||
import { GameLoop } from '../sim/loop.ts'
|
||||
import { KeyboardInput } from '../sim/input.ts'
|
||||
import type { SceneEntrance, SceneWarp, SceneWaypoint } from '../game/level-links.ts'
|
||||
|
|
@ -2675,7 +2675,6 @@ if (typeof window !== 'undefined') {
|
|||
if (typeof window !== 'undefined' && window.__d2webHud) {
|
||||
(window.__d2webHud as any).proceduralFallbackCount = hudManager.globes.proceduralFallbackCount
|
||||
}
|
||||
hudManager.worldPanels.showAreaBanner(sceneNameZh(runtime.level, runtime.level), runtime.level)
|
||||
}
|
||||
|
||||
const mouseController = new SceneMouseController({
|
||||
|
|
@ -2869,6 +2868,9 @@ if (typeof window !== 'undefined') {
|
|||
state.animatedTiles = next.animatedDrawables?.length ?? 0
|
||||
state.animSpeed = next.animSpeed
|
||||
state.frameDurationMs = next.frameDurationMs
|
||||
|
||||
const names = getLevelNames(next.levelId ?? next.level)
|
||||
hudManager?.worldPanels.showAreaBanner(names.nameZh, names.nameEn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2994,8 +2996,8 @@ if (typeof window !== 'undefined') {
|
|||
preloadRemaining(next, renderer)
|
||||
minimapCache = null
|
||||
|
||||
hudManager?.worldPanels.showAreaBanner(sceneNameZh(next.level, next.level), next.level)
|
||||
status.textContent = `已传送到:${sceneNameZh(next.level, next.level)}(Act ${String(next.act)})`
|
||||
const names = getLevelNames(next.levelId ?? next.level)
|
||||
status.textContent = `已传送到:${names.nameZh}(Act ${String(next.act)})`
|
||||
} catch (err) {
|
||||
status.textContent = `传送点传送失败:${(err as Error).message}`
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@ import { WorldPanelsHud, ACT_WAYPOINTS, ALL_QUESTS, ACT_QUESTS, findWaypointBySl
|
|||
import { BAKED_UI_MANIFEST } from '../src/ui/baked-ui-meta.ts'
|
||||
import { HudManager, computeHudLayout } from '../src/ui/hud-manager.ts'
|
||||
import { projectNpcLabel } from '../src/scene/act-scene.ts'
|
||||
import {
|
||||
getLevelNames,
|
||||
sceneNameZh,
|
||||
sceneNameEn,
|
||||
LEVEL_NAMES_ZH,
|
||||
LEVEL_NAMES_EN,
|
||||
} from '../src/game/level-names-zh.ts'
|
||||
|
||||
describe('Diablo II v1.13c UI / HUD (Issue #27)', () => {
|
||||
it('verifies offline DC6 UI asset extraction manifest has 0 decode failures', () => {
|
||||
|
|
@ -548,4 +555,70 @@ describe('Diablo II v1.13c UI / HUD (Issue #27)', () => {
|
|||
expect(act4Quests[1]!.id).toBe('a4q2')
|
||||
expect(act4Quests[2]!.id).toBe('a4q3')
|
||||
})
|
||||
|
||||
it('loads localized map area names from tbl with 1.13c Act 5 alignment and slug protection (Issue #147)', () => {
|
||||
// 1. Specific key areas required by Issue #147 and v1.13c tbl
|
||||
expect(getLevelNames(1)).toEqual({ nameZh: '罗格营地', nameEn: 'Rogue Encampment' })
|
||||
expect(getLevelNames(2)).toEqual({ nameZh: '鲜血荒地', nameEn: 'Blood Moor' })
|
||||
expect(getLevelNames(17)).toEqual({ nameZh: '埋骨之地', nameEn: 'Burial Grounds' })
|
||||
|
||||
// 2. Act 5 (109..136) alignment against string.tbl / expansionstring.tbl
|
||||
expect(getLevelNames(109)).toEqual({ nameZh: '哈洛加斯', nameEn: 'Harrogath' })
|
||||
expect(getLevelNames(118)).toEqual({ nameZh: '远古之路', nameEn: "The Ancients' Way" })
|
||||
expect(getLevelNames(119)).toEqual({ nameZh: '冰窖', nameEn: 'Icy Cellar' })
|
||||
expect(getLevelNames(120)).toEqual({ nameZh: '亚瑞特山顶', nameEn: 'Arreat Summit' })
|
||||
expect(getLevelNames(121)).toEqual({ nameZh: '尼拉塞克的神殿', nameEn: "Nihlathak's Temple" })
|
||||
expect(getLevelNames(122)).toEqual({ nameZh: '悲痛之厅', nameEn: 'Halls of Anguish' })
|
||||
expect(getLevelNames(123)).toEqual({ nameZh: '痛苦之厅', nameEn: 'Halls of Pain' })
|
||||
expect(getLevelNames(124)).toEqual({ nameZh: '沃德之厅', nameEn: 'Halls of Vaught' })
|
||||
expect(getLevelNames(131)).toEqual({ nameZh: '毁灭王座', nameEn: 'Throne of Destruction' })
|
||||
expect(getLevelNames(132)).toEqual({ nameZh: '世界之石大殿', nameEn: 'Worldstone Chamber' })
|
||||
|
||||
// 3. Complete 136 level IDs validity: no empty strings, no raw hyphen slugs
|
||||
for (let id = 1; id <= 136; id++) {
|
||||
const names = getLevelNames(id)
|
||||
expect(names.nameZh).toBeTruthy()
|
||||
expect(names.nameEn).toBeTruthy()
|
||||
expect(names.nameZh).not.toContain('-')
|
||||
expect(names.nameEn).not.toContain('-')
|
||||
expect(LEVEL_NAMES_ZH[id]).toBe(names.nameZh)
|
||||
expect(LEVEL_NAMES_EN[id]).toBe(names.nameEn)
|
||||
}
|
||||
|
||||
// 4. Slug resolution: both un-prefixed and numbered prefixes resolve cleanly
|
||||
expect(getLevelNames('burial-grounds')).toEqual({ nameZh: '埋骨之地', nameEn: 'Burial Grounds' })
|
||||
expect(getLevelNames('17-burial-grounds')).toEqual({ nameZh: '埋骨之地', nameEn: 'Burial Grounds' })
|
||||
expect(getLevelNames('rogue-encampment')).toEqual({ nameZh: '罗格营地', nameEn: 'Rogue Encampment' })
|
||||
expect(getLevelNames('blood-moor')).toEqual({ nameZh: '鲜血荒地', nameEn: 'Blood Moor' })
|
||||
expect(getLevelNames('harrogath')).toEqual({ nameZh: '哈洛加斯', nameEn: 'Harrogath' })
|
||||
expect(getLevelNames('the-ancients-way')).toEqual({ nameZh: '远古之路', nameEn: "The Ancients' Way" })
|
||||
expect(getLevelNames('icy-cellar')).toEqual({ nameZh: '冰窖', nameEn: 'Icy Cellar' })
|
||||
expect(getLevelNames('arreat-summit')).toEqual({ nameZh: '亚瑞特山顶', nameEn: 'Arreat Summit' })
|
||||
|
||||
// 5. sceneNameZh and sceneNameEn parity and slug leak prevention
|
||||
expect(sceneNameZh(17)).toBe('埋骨之地')
|
||||
expect(sceneNameZh('burial-grounds')).toBe('埋骨之地')
|
||||
expect(sceneNameZh('17-burial-grounds')).toBe('埋骨之地')
|
||||
expect(sceneNameEn(17)).toBe('Burial Grounds')
|
||||
expect(sceneNameEn('burial-grounds')).toBe('Burial Grounds')
|
||||
expect(sceneNameEn('17-burial-grounds')).toBe('Burial Grounds')
|
||||
|
||||
// 6. Unknown fallback must never produce raw un-spaced lowercase slugs
|
||||
const unknownFallback = getLevelNames('unknown-custom-dungeon')
|
||||
expect(unknownFallback.nameZh).toBe('Unknown Custom Dungeon')
|
||||
expect(unknownFallback.nameEn).toBe('Unknown Custom Dungeon')
|
||||
expect(sceneNameZh('unknown-custom-dungeon')).toBe('Unknown Custom Dungeon')
|
||||
expect(sceneNameEn('unknown-custom-dungeon')).toBe('Unknown Custom Dungeon')
|
||||
|
||||
// 7. Area banner display logic in WorldPanelsHud
|
||||
const worldPanels = new WorldPanelsHud()
|
||||
const now = 1000
|
||||
const bgNames = getLevelNames(17)
|
||||
worldPanels.showAreaBanner(bgNames.nameZh, bgNames.nameEn, now)
|
||||
expect(worldPanels.areaBanner).toEqual({
|
||||
titleZh: '埋骨之地',
|
||||
titleEn: 'Burial Grounds',
|
||||
untilMs: now + 3600,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue