diff --git a/docs/02-game-design.md b/docs/02-game-design.md index 9f4db13..7defbde 100644 --- a/docs/02-game-design.md +++ b/docs/02-game-design.md @@ -75,160 +75,124 @@ ## 2. 地图结构 -### 2.1 导航层级 +### 2.1 MVP 导航(当前实现) + +MVP 只开放亚洲 6 城市,直入城市选择页,不启用完整多级导航。 ``` -洲(3x3 棋盘) - └── 国家(3x3 棋盘,超过 9 国分页) - └── 城市(3x3 棋盘,超过 9 城分页) - └── 关卡(单局游戏) +启动 → 亚洲城市页(3x3 棋盘,6 城市)→ 关卡 ``` -中国因城市数量多(40 城),额外增加"地区"层级: +- 跳过洲选择页(只有 1 个洲,多余点击) +- 跳过国家/地区层级 +- 6 城市平铺在一页 3x3 棋盘中 +- 其他洲的入口不显示 + +**MVP 城市列表(固定 6 城):** + +| 序号 | 城市 | 国家 | 解锁条件 | +|------|------|------|---------| +| 1 | 北京 | 中国 | 默认解锁 | +| 2 | 东京 | 日本 | 通关北京 | +| 3 | 曼谷 | 泰国 | 通关东京 | +| 4 | 首尔 | 韩国 | 通关曼谷 | +| 5 | 新加坡 | 新加坡 | 通关首尔 | +| 6 | 伊斯坦布尔 | 土耳其 | 通关新加坡 | + +### 2.2 V1.1+ 完整导航(后续启用) + +城市数量超过 9 时启用完整多级导航,每页 3×3 棋盘,超过 9 项翻页。 ``` -亚洲 → 中国 → 地区(3x3)→ 城市(3x3)→ 关卡 -亚洲 → 日本 → 城市(3x3)→ 关卡 +洲选择(3x3) + └── 国家选择(3x3,分页) + └── 城市选择(3x3,分页) + └── 关卡 ``` -### 2.2 洲选择页(第 1 级) - -1 页,3×3 棋盘: +中国因城市多(41 城),额外增加地区层级: ``` -┌────────┬────────┬────────┐ -│ 亚洲 │ 欧洲 │ 北美洲 │ -├────────┼────────┼────────┤ -│ 南美洲 │ 非洲 │ 大洋洲 │ -├────────┼────────┼────────┤ -│ │ │ │ -└────────┴────────┴────────┘ +亚洲 → 中国 → 地区(3x3)→ 城市(3x3) +亚洲 → 日本 → 城市(3x3) ``` -6 个洲,3 个空位留给未来扩展。MVP 只开放亚洲,其余显示锁定 + "即将开放"。 +#### 洲选择页 -每个洲的封面:洲标志性图案 + 解锁进度(如"6/70 城市已解锁")。 +6 个洲,1 页:亚洲、欧洲、北美洲、南美洲、非洲、大洋洲。 -### 2.3 国家选择页(第 2 级) +#### 亚洲国家页 -#### 亚洲 — 2 页 +18 国,分 2 页: +- Page 1: 中国、日本、韩国、泰国、新加坡、越南、马来西亚、印度尼西亚、菲律宾 +- Page 2: 印度、阿联酋、土耳其、以色列、尼泊尔、柬埔寨、斯里兰卡、缅甸、蒙古 -Page 1: -``` -┌────────┬────────┬────────┐ -│ 中国 │ 日本 │ 韩国 │ -├────────┼────────┼────────┤ -│ 泰国 │ 新加坡 │ 越南 │ -├────────┼────────┼────────┤ -│马来西亚│印度尼西亚│ 菲律宾│ -└────────┴────────┴────────┘ -``` +#### 中国地区页 -Page 2: -``` -┌────────┬────────┬────────┐ -│ 印度 │ 阿联酋 │ 土耳其 │ -├────────┼────────┼────────┤ -│ 以色列 │ 尼泊尔 │ 柬埔寨 │ -├────────┼────────┼────────┤ -│斯里兰卡│ 缅甸 │ 蒙古 │ -└────────┴────────┴────────┘ -``` +8 个地区,1 页: -#### 欧洲 — 2 页 +| 地区 | 城市 | 数量 | +|------|------|------| +| 华北 | 北京、天津、石家庄、太原、呼和浩特 | 5 | +| 东北 | 沈阳、长春、哈尔滨、大连 | 4 | +| 华东 | 上海、南京、杭州、合肥、福州、南昌、济南、苏州、厦门、青岛、宁波 | 11(2 页) | +| 华中 | 郑州、武汉、长沙 | 3 | +| 华南 | 广州、深圳、南宁、海口、三亚 | 5 | +| 西南 | 成都、重庆、贵阳、昆明、拉萨 | 5 | +| 西北 | 西安、兰州、西宁、银川、乌鲁木齐 | 5 | +| 港澳台 | 香港、澳门、台北 | 3 | -Page 1: -``` -法国、英国、意大利、西班牙、德国、荷兰、瑞士、奥地利、捷克 -``` +> 港澳台统一为 3 城(香港、澳门、台北),文档和索引一致。 -Page 2: -``` -希腊、俄罗斯、葡萄牙、挪威、瑞典、丹麦、芬兰、冰岛、匈牙利 -``` +### 2.3 导航数据合同 -#### 北美洲 — 1 页 -``` -美国、加拿大、墨西哥 + 6 空位 -``` +所有导航层级使用统一的 NavNode schema(定义在 `js/content/navigation/nav-schema.js`): -#### 南美洲 — 1 页 -``` -巴西、阿根廷、秘鲁、智利、哥伦比亚 + 4 空位 -``` +| 字段 | 类型 | 说明 | +|------|------|------| +| `type` | string | `continent` / `country` / `region` / `city-group` | +| `id` | string | 全局唯一标识 | +| `parentId` | string\|null | 父节点 id,洲级为 null | +| `name` | string | 中文名 | +| `nameEn` | string | 英文名 | +| `sortOrder` | number | 同级排序 | +| `themeColor` | string | 主题色 HEX | +| `childType` | string\|null | 子节点类型:`country` / `region` / `city` / null | +| `childIds` | string[] | 当前可消费的子节点 id(仅已启用的) | +| `pageSize` | number | 每页格子数,默认 9 | +| `isEnabled` | boolean | 当前版本是否启用 | +| `isUnlockedByDefault` | boolean | 是否默认解锁 | -#### 非洲 — 1 页 -``` -埃及、南非、摩洛哥、肯尼亚 + 5 空位 -``` +分页通过 `shared/pagination.js` 的 `paginate(childIds, pageSize)` 动态计算,不手写 page 常量。 -#### 大洋洲 — 1 页 -``` -澳大利亚、新西兰 + 7 空位 -``` +**数据文件分层:** -### 2.4 中国地区选择页(第 3 级,仅中国) +| 文件 | 用途 | 是否进入 runtime | +|------|------|-----------------| +| `navigation/runtime-nav.js` | 当前版本可消费的导航节点 | ✅ 是 | +| `navigation/future-catalog.js` | 未来版本城市/国家/地区规划 | ❌ 否 | +| `navigation/nav-schema.js` | NavNode schema 定义 + 验证 | ✅ 是(类型定义) | +| `docs/city-roadmap.md` | 全量 127 城市路线图 | ❌ 否 | -中国 40 个城市按地区分组,1 页 3×3: - -``` -┌────────┬────────┬────────┐ -│ 华北 │ 东北 │ 华东 │ -├────────┼────────┼────────┤ -│ 华中 │ 华南 │ 西南 │ -├────────┼────────┼────────┤ -│ 西北 │ 港澳台 │ │ -└────────┴────────┴────────┘ -``` - -各地区包含城市: - -| 地区 | 城市 | 数量 | 页数 | -|------|------|------|------| -| 华北 | 北京、天津、石家庄、太原、呼和浩特 | 5 | 1 | -| 东北 | 沈阳、长春、哈尔滨、大连 | 4 | 1 | -| 华东 | 上海、南京、杭州、合肥、福州、南昌、济南、苏州、厦门、青岛、宁波 | 11 | 2 | -| 华中 | 郑州、武汉、长沙 | 3 | 1 | -| 华南 | 广州、深圳、南宁、海口、三亚 | 5 | 1 | -| 西南 | 成都、重庆、贵阳、昆明、拉萨 | 5 | 1 | -| 西北 | 西安、兰州、西宁、银川、乌鲁木齐 | 5 | 1 | -| 港澳台 | 香港、澳门、台北 | 3 | 1 | - -### 2.5 城市选择页(第 3/4 级) - -#### 其他国家(非中国)— 直接展示城市 - -| 国家 | 城市数 | 页数 | -|------|--------|------| -| 日本 | 4(东京、大阪、京都、札幌) | 1 | -| 韩国 | 3(首尔、釜山、济州) | 1 | -| 泰国 | 3(曼谷、清迈、普吉) | 1 | -| 美国 | 7(纽约、洛杉矶、旧金山、拉斯维加斯、华盛顿、芝加哥、迈阿密) | 1 | -| 意大利 | 4(罗马、米兰、威尼斯、佛罗伦萨) | 1 | -| 其他国家 | 1-3 | 1 | - -所有国家的城市数均 ≤ 9,无需分页(除华东 11 城需 2 页外)。 - -### 2.6 城市封面设计 +### 2.4 城市封面设计 每个格子显示: -- 该城市/国家/洲的特色猫猫头像(已解锁:彩色,未解锁:灰色剪影) +- 猫猫头像(已解锁:彩色,未解锁:灰色剪影) - 名称(中文) -- 进度信息(已解锁:"X/6 关",未解锁:"🔒") +- 进度(已解锁:"X/6 关",未解锁:"🔒") -### 2.7 分页交互 +### 2.5 分页交互 - 左右滑动翻页,底部圆点指示器 -- 首次进入默认第 1 页 - 翻页动画 0.3s 弹性过渡 +- MVP 只有 1 页 6 城市,无需翻页 -### 2.8 解锁规则 +### 2.6 解锁规则 -- 同一层级内顺序解锁(通关前一个解锁下一个) +- 同级顺序解锁(通关前一个解锁下一个) - 第一个项目默认解锁 -- 跨层级:通关一个国家所有城市后,解锁下一个国家 -- 中国地区内:通关华北所有城市后解锁东北,以此类推 -- MVP 只开放亚洲洲,亚洲内只开放中国(北京)和已有 MVP 城市 +- MVP:北京默认解锁,顺序通关解锁后续城市 +- V1.1+:跨层级解锁(通关一个国家所有城市后解锁下一个国家) --- diff --git a/js/content/cities/bangkok.js b/js/content/cities/bangkok.js index 993e7d1..28a7297 100644 --- a/js/content/cities/bangkok.js +++ b/js/content/cities/bangkok.js @@ -1,9 +1,13 @@ -module.exports = { +import { createLevelPresets } from '../shared/level-presets.js' + +export const bangkok = { id: 'bangkok', contentVersion: 1, continentId: 'asia', + countryId: 'thailand', + regionId: null, sortOrder: 3, - unlockAfterCityId: 'tokyo', + unlockAfterCityId: 'seoul', bundle: { packId: 'asia-rest', preload: 'on-city-page', @@ -11,54 +15,45 @@ module.exports = { display: { name: '曼谷', nameEn: 'Bangkok', - bgColor: '#FFB347', - tagline: '湄南河畔追猫猫', - funFact: '曼谷的全名有168个字母,是世界最长城市名', + bgColor: '#F9A826', + tagline: '热带夜市看猫猫', + funFact: '曼谷是东南亚热门旅行城市', }, cover: { catImage: 'images/cats/cat_bangkok.png', catThumb: 'images/cats/cat_bangkok_thumb.png', - shareAccent: '#C9A96E', + shareAccent: '#FFD166', }, cat: { id: 'cat_bangkok', - name: '暹暹', - intro: '戴泰式花环的暹罗猫,最爱在水上市场闲逛', - baseColor: '#FAF0E6', + name: '泰泰', + baseColor: '#D9C2A7', pattern: 'siamese', - patternColor: '#6B4226', - accessory: '泰式花环', - expression: 'curious', + accessory: '花环', }, passport: { stampId: 'stamp_bangkok', stampLabel: '曼谷', }, elements: [ - { id: 'bangkok_01', name: '冬阴功', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_01.png', tags: ['soup', 'spicy', 'shrimp'] }, - { id: 'bangkok_02', name: '嘟嘟车', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_02.png', tags: ['vehicle', 'colorful', 'street'] }, - { id: 'bangkok_03', name: '大象', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_03.png', tags: ['animal', 'grey', 'gentle'] }, - { id: 'bangkok_04', name: '泰拳手套', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_04.png', tags: ['sport', 'red', 'fighting'] }, - { id: 'bangkok_05', name: '芒果糯米饭', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_05.png', tags: ['sweet', 'mango', 'sticky'] }, - { id: 'bangkok_06', name: '金佛', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_06.png', tags: ['buddha', 'golden', 'temple'] }, - { id: 'bangkok_07', name: '莲花', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_07.png', tags: ['flower', 'pink', 'water'] }, - { id: 'bangkok_08', name: '榴莲', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_08.png', tags: ['fruit', 'spiky', 'yellow'] }, - { id: 'bangkok_09', name: '泰丝', category: 'culture', rarity: 'core', image: 'images/elements/bangkok/bangkok_09.png', tags: ['fabric', 'shimmer', 'craft'] }, - { id: 'bangkok_10', name: '船面', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_10.png', tags: ['noodle', 'boat', 'dark'] }, - { id: 'bangkok_11', name: '佛塔', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_11.png', tags: ['stupa', 'golden', 'tall'] }, - { id: 'bangkok_12', name: '椰子', category: 'nature', rarity: 'accent', image: 'images/elements/bangkok/bangkok_12.png', tags: ['fruit', 'tropical', 'green'] }, - { id: 'bangkok_13', name: '夜市灯笼', category: 'culture', rarity: 'accent', image: 'images/elements/bangkok/bangkok_13.png', tags: ['lantern', 'glow', 'night'] }, - ], - levelPresets: [ - { id: 1, difficultyTier: 'intro', seedBase: 13001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] }, - { id: 2, difficultyTier: 'easy', seedBase: 13002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] }, - { id: 3, difficultyTier: 'normal', seedBase: 13003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] }, - { id: 4, difficultyTier: 'normal', seedBase: 13004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] }, - { id: 5, difficultyTier: 'hard', seedBase: 13005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] }, - { id: 6, difficultyTier: 'boss', seedBase: 13006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] }, + { id: 'bangkok_01', name: '玉佛寺', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_01.png', tags: ['temple'] }, + { id: 'bangkok_02', name: '佛塔', category: 'landmark', rarity: 'core', image: 'images/elements/bangkok/bangkok_02.png', tags: ['tower'] }, + { id: 'bangkok_03', name: '冬阴功', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_03.png', tags: ['soup'] }, + { id: 'bangkok_04', name: '芒果糯米饭', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_04.png', tags: ['dessert'] }, + { id: 'bangkok_05', name: '船面', category: 'food', rarity: 'core', image: 'images/elements/bangkok/bangkok_05.png', tags: ['noodle'] }, + { id: 'bangkok_06', name: '泰丝', category: 'culture', rarity: 'core', image: 'images/elements/bangkok/bangkok_06.png', tags: ['fabric'] }, + { id: 'bangkok_07', name: '泰拳手套', category: 'culture', rarity: 'core', image: 'images/elements/bangkok/bangkok_07.png', tags: ['sport'] }, + { id: 'bangkok_08', name: '嘟嘟车', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_08.png', tags: ['vehicle'] }, + { id: 'bangkok_09', name: '椰子杯', category: 'item', rarity: 'core', image: 'images/elements/bangkok/bangkok_09.png', tags: ['drink'] }, + { id: 'bangkok_10', name: '莲花', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_10.png', tags: ['flower'] }, + { id: 'bangkok_11', name: '大象', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_11.png', tags: ['animal'] }, + { id: 'bangkok_12', name: '榴莲', category: 'nature', rarity: 'core', image: 'images/elements/bangkok/bangkok_12.png', tags: ['fruit'] }, ], + levelPresets: createLevelPresets(13000), shareCard: { titleUnlocked: '我解锁了曼谷猫!', titlePassport: '曼谷护照盖章完成', }, } + +export default bangkok diff --git a/js/content/cities/beijing.js b/js/content/cities/beijing.js index 42b1c2d..3abfb35 100644 --- a/js/content/cities/beijing.js +++ b/js/content/cities/beijing.js @@ -1,9 +1,13 @@ -module.exports = { +import { createLevelPresets } from '../shared/level-presets.js' + +export const beijing = { id: 'beijing', contentVersion: 1, continentId: 'asia', + countryId: 'china', + regionId: 'north_china', sortOrder: 1, - unlockAfterCityId: null, + unlockAfterCityId: 'tokyo', bundle: { packId: 'main', preload: 'startup', @@ -13,7 +17,7 @@ module.exports = { nameEn: 'Beijing', bgColor: '#CC2936', tagline: '天安门前看猫猫', - funFact: '北京是世界上拥有最多宫殿的城市', + funFact: '北京是中国的历史文化名城', }, cover: { catImage: 'images/cats/cat_beijing.png', @@ -23,43 +27,33 @@ module.exports = { cat: { id: 'cat_beijing', name: '京京', - intro: '戴虎头帽的橘猫,最爱在胡同里晒太阳', baseColor: '#F28C28', pattern: 'tabby', - patternColor: '#D4761A', accessory: '虎头帽', - expression: 'smile', }, passport: { stampId: 'stamp_beijing', stampLabel: '北京', }, elements: [ - { id: 'beijing_01', name: '糖葫芦', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_01.png', tags: ['sweet', 'street-food', 'red'] }, - { id: 'beijing_02', name: '京剧脸谱', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_02.png', tags: ['opera', 'mask', 'colorful'] }, - { id: 'beijing_03', name: '天安门', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_03.png', tags: ['building', 'red', 'iconic'] }, - { id: 'beijing_04', name: '烤鸭', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_04.png', tags: ['meat', 'famous', 'golden'] }, - { id: 'beijing_05', name: '兔儿爷', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_05.png', tags: ['toy', 'festival', 'rabbit'] }, - { id: 'beijing_06', name: '长城砖', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_06.png', tags: ['wall', 'historic', 'grey'] }, - { id: 'beijing_07', name: '故宫角楼', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_07.png', tags: ['palace', 'golden', 'ancient'] }, - { id: 'beijing_08', name: '豆汁', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_08.png', tags: ['drink', 'green', 'local'] }, - { id: 'beijing_09', name: '铜锣', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_09.png', tags: ['instrument', 'golden', 'round'] }, - { id: 'beijing_10', name: '毛笔', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_10.png', tags: ['writing', 'black', 'thin'] }, - { id: 'beijing_11', name: '鸟巢', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_11.png', tags: ['stadium', 'modern', 'steel'] }, - { id: 'beijing_12', name: '冰糖葫芦', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_12.png', tags: ['candy', 'stick', 'red'] }, - { id: 'beijing_13', name: '四合院门', category: 'culture', rarity: 'accent', image: 'images/elements/beijing/beijing_13.png', tags: ['door', 'red', 'traditional'] }, - { id: 'beijing_14', name: '景泰蓝', category: 'item', rarity: 'accent', image: 'images/elements/beijing/beijing_14.png', tags: ['craft', 'blue', 'enamel'] }, - ], - levelPresets: [ - { id: 1, difficultyTier: 'intro', seedBase: 11001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] }, - { id: 2, difficultyTier: 'easy', seedBase: 11002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] }, - { id: 3, difficultyTier: 'normal', seedBase: 11003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] }, - { id: 4, difficultyTier: 'normal', seedBase: 11004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] }, - { id: 5, difficultyTier: 'hard', seedBase: 11005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] }, - { id: 6, difficultyTier: 'boss', seedBase: 11006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] }, + { id: 'beijing_01', name: '天安门', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_01.png', tags: ['gate', 'red'] }, + { id: 'beijing_02', name: '鸟巢', category: 'landmark', rarity: 'core', image: 'images/elements/beijing/beijing_02.png', tags: ['stadium'] }, + { id: 'beijing_03', name: '糖葫芦', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_03.png', tags: ['sweet'] }, + { id: 'beijing_04', name: '烤鸭', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_04.png', tags: ['duck'] }, + { id: 'beijing_05', name: '豆汁', category: 'food', rarity: 'core', image: 'images/elements/beijing/beijing_05.png', tags: ['drink'] }, + { id: 'beijing_06', name: '京剧脸谱', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_06.png', tags: ['opera'] }, + { id: 'beijing_07', name: '兔儿爷', category: 'culture', rarity: 'core', image: 'images/elements/beijing/beijing_07.png', tags: ['folk'] }, + { id: 'beijing_08', name: '毛笔', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_08.png', tags: ['brush'] }, + { id: 'beijing_09', name: '铜锣', category: 'item', rarity: 'core', image: 'images/elements/beijing/beijing_09.png', tags: ['instrument'] }, + { id: 'beijing_10', name: '银杏叶', category: 'nature', rarity: 'core', image: 'images/elements/beijing/beijing_10.png', tags: ['leaf'] }, + { id: 'beijing_11', name: '玉兰花', category: 'nature', rarity: 'core', image: 'images/elements/beijing/beijing_11.png', tags: ['flower'] }, + { id: 'beijing_12', name: '白鸽', category: 'nature', rarity: 'core', image: 'images/elements/beijing/beijing_12.png', tags: ['bird'] }, ], + levelPresets: createLevelPresets(11000), shareCard: { titleUnlocked: '我解锁了北京猫!', titlePassport: '北京护照盖章完成', }, } + +export default beijing diff --git a/js/content/cities/index.js b/js/content/cities/index.js new file mode 100644 index 0000000..aea6d23 --- /dev/null +++ b/js/content/cities/index.js @@ -0,0 +1,10 @@ +import bangkok from './bangkok.js' +import beijing from './beijing.js' +import istanbul from './istanbul.js' +import seoul from './seoul.js' +import singapore from './singapore.js' +import tokyo from './tokyo.js' + +export const cities = [beijing, tokyo, bangkok, seoul, singapore, istanbul] + +export default cities diff --git a/js/content/cities/istanbul.js b/js/content/cities/istanbul.js index 2d4a4e2..c438e6e 100644 --- a/js/content/cities/istanbul.js +++ b/js/content/cities/istanbul.js @@ -1,9 +1,13 @@ -module.exports = { +import { createLevelPresets } from '../shared/level-presets.js' + +export const istanbul = { id: 'istanbul', contentVersion: 1, continentId: 'asia', + countryId: 'turkey', + regionId: null, sortOrder: 6, - unlockAfterCityId: 'singapore', + unlockAfterCityId: null, bundle: { packId: 'asia-rest', preload: 'on-city-page', @@ -11,55 +15,45 @@ module.exports = { display: { name: '伊斯坦布尔', nameEn: 'Istanbul', - bgColor: '#1A5276', - tagline: '博斯普鲁斯的猫', - funFact: '伊斯坦布尔是唯一横跨两大洲的城市', + bgColor: '#7B2CBF', + tagline: '海峡两岸看猫猫', + funFact: '伊斯坦布尔横跨欧亚两洲', }, cover: { catImage: 'images/cats/cat_istanbul.png', catThumb: 'images/cats/cat_istanbul_thumb.png', - shareAccent: '#E8D5B7', + shareAccent: '#9D4EDD', }, cat: { id: 'cat_istanbul', - name: '安安', - intro: '佩戴恶魔之眼项圈的安哥拉猫,神秘优雅', - baseColor: '#F5F5F5', - pattern: 'solid', - patternColor: '#E8E8E8', + name: '蓝眸', + baseColor: '#F5E6CC', + pattern: 'angora', accessory: '恶魔之眼项圈', - expression: 'mysterious', }, passport: { stampId: 'stamp_istanbul', stampLabel: '伊斯坦布尔', }, elements: [ - { id: 'istanbul_01', name: '蓝色清真寺', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_01.png', tags: ['mosque', 'blue', 'dome'] }, - { id: 'istanbul_02', name: '土耳其红茶', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_02.png', tags: ['tea', 'glass', 'red'] }, - { id: 'istanbul_03', name: '热气球', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_03.png', tags: ['balloon', 'sky', 'colorful'] }, - { id: 'istanbul_04', name: '烤肉串', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_04.png', tags: ['kebab', 'grill', 'meat'] }, - { id: 'istanbul_05', name: '郁金香', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_05.png', tags: ['flower', 'red', 'spring'] }, - { id: 'istanbul_06', name: '恶魔之眼', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_06.png', tags: ['amulet', 'blue', 'protection'] }, - { id: 'istanbul_07', name: '土耳其冰淇淋', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_07.png', tags: ['dessert', 'stretchy', 'fun'] }, - { id: 'istanbul_08', name: '地毯', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_08.png', tags: ['textile', 'pattern', 'woven'] }, - { id: 'istanbul_09', name: '石榴', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_09.png', tags: ['fruit', 'red', 'juicy'] }, - { id: 'istanbul_10', name: '旋转舞裙', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_10.png', tags: ['dance', 'whirl', 'white'] }, - { id: 'istanbul_11', name: '圣索菲亚', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_11.png', tags: ['cathedral', 'dome', 'historic'] }, - { id: 'istanbul_12', name: '土耳其软糖', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_12.png', tags: ['candy', 'sweet', 'colorful'] }, - { id: 'istanbul_13', name: '波斯猫雕像', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_13.png', tags: ['statue', 'cat', 'elegant'] }, - { id: 'istanbul_14', name: '香料', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_14.png', tags: ['spice', 'bazaar', 'aromatic'] }, - ], - levelPresets: [ - { id: 1, difficultyTier: 'intro', seedBase: 16001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] }, - { id: 2, difficultyTier: 'easy', seedBase: 16002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] }, - { id: 3, difficultyTier: 'normal', seedBase: 16003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] }, - { id: 4, difficultyTier: 'normal', seedBase: 16004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] }, - { id: 5, difficultyTier: 'hard', seedBase: 16005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] }, - { id: 6, difficultyTier: 'boss', seedBase: 16006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] }, + { id: 'istanbul_01', name: '蓝色清真寺', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_01.png', tags: ['mosque'] }, + { id: 'istanbul_02', name: '加拉塔塔', category: 'landmark', rarity: 'core', image: 'images/elements/istanbul/istanbul_02.png', tags: ['tower'] }, + { id: 'istanbul_03', name: '土耳其红茶', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_03.png', tags: ['tea'] }, + { id: 'istanbul_04', name: '烤肉串', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_04.png', tags: ['meat'] }, + { id: 'istanbul_05', name: '土耳其冰淇淋', category: 'food', rarity: 'core', image: 'images/elements/istanbul/istanbul_05.png', tags: ['dessert'] }, + { id: 'istanbul_06', name: '旋转舞裙', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_06.png', tags: ['dance'] }, + { id: 'istanbul_07', name: '地毯纹样', category: 'culture', rarity: 'core', image: 'images/elements/istanbul/istanbul_07.png', tags: ['pattern'] }, + { id: 'istanbul_08', name: '恶魔之眼', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_08.png', tags: ['amulet'] }, + { id: 'istanbul_09', name: '铜咖啡壶', category: 'item', rarity: 'core', image: 'images/elements/istanbul/istanbul_09.png', tags: ['pot'] }, + { id: 'istanbul_10', name: '郁金香', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_10.png', tags: ['flower'] }, + { id: 'istanbul_11', name: '石榴', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_11.png', tags: ['fruit'] }, + { id: 'istanbul_12', name: '海鸥', category: 'nature', rarity: 'core', image: 'images/elements/istanbul/istanbul_12.png', tags: ['bird'] }, ], + levelPresets: createLevelPresets(16000), shareCard: { titleUnlocked: '我解锁了伊斯坦布尔猫!', titlePassport: '伊斯坦布尔护照盖章完成', }, } + +export default istanbul diff --git a/js/content/cities/seoul.js b/js/content/cities/seoul.js index 643bc2e..1066bd0 100644 --- a/js/content/cities/seoul.js +++ b/js/content/cities/seoul.js @@ -1,9 +1,13 @@ -module.exports = { +import { createLevelPresets } from '../shared/level-presets.js' + +export const seoul = { id: 'seoul', contentVersion: 1, continentId: 'asia', + countryId: 'korea', + regionId: null, sortOrder: 4, - unlockAfterCityId: 'bangkok', + unlockAfterCityId: 'singapore', bundle: { packId: 'asia-rest', preload: 'on-city-page', @@ -11,54 +15,45 @@ module.exports = { display: { name: '首尔', nameEn: 'Seoul', - bgColor: '#4A90D9', - tagline: '景福宫前遇猫猫', - funFact: '首尔的地铁系统覆盖率全球第一', + bgColor: '#5C7AEA', + tagline: '首尔夜景看猫猫', + funFact: '首尔是东亚流行文化重镇', }, cover: { catImage: 'images/cats/cat_seoul.png', catThumb: 'images/cats/cat_seoul_thumb.png', - shareAccent: '#F5E6CA', + shareAccent: '#86A8E7', }, cat: { id: 'cat_seoul', name: '韩韩', - intro: '戴韩服小帽的韩国短尾猫,爱在景福宫前眨眼卖萌', - baseColor: '#F5E6CA', - pattern: 'bicolor', - patternColor: '#D4A574', + baseColor: '#B0B7C3', + pattern: 'short-tail', accessory: '韩服小帽', - expression: 'wink', }, passport: { stampId: 'stamp_seoul', stampLabel: '首尔', }, elements: [ - { id: 'seoul_01', name: '泡菜坛', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_01.png', tags: ['fermented', 'jar', 'red'] }, - { id: 'seoul_02', name: '石锅拌饭', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_02.png', tags: ['rice', 'hot', 'colorful'] }, - { id: 'seoul_03', name: '韩服', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_03.png', tags: ['clothing', 'traditional', 'colorful'] }, - { id: 'seoul_04', name: '景福宫', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_04.png', tags: ['palace', 'historic', 'grand'] }, - { id: 'seoul_05', name: '烧酒瓶', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_05.png', tags: ['drink', 'green', 'bottle'] }, - { id: 'seoul_06', name: '年糕', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_06.png', tags: ['sweet', 'chewy', 'white'] }, - { id: 'seoul_07', name: 'K-pop话筒', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_07.png', tags: ['music', 'pop', 'shiny'] }, - { id: 'seoul_08', name: '太极旗扇', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_08.png', tags: ['flag', 'fan', 'national'] }, - { id: 'seoul_09', name: '韩式炸鸡', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_09.png', tags: ['fried', 'crispy', 'golden'] }, - { id: 'seoul_10', name: '柿子', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_10.png', tags: ['fruit', 'orange', 'autumn'] }, - { id: 'seoul_11', name: '海苔卷', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_11.png', tags: ['seaweed', 'roll', 'green'] }, - { id: 'seoul_12', name: '南山塔', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_12.png', tags: ['tower', 'romantic', 'iconic'] }, - { id: 'seoul_13', name: '木槿花', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_13.png', tags: ['flower', 'pink', 'national'] }, - ], - levelPresets: [ - { id: 1, difficultyTier: 'intro', seedBase: 14001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] }, - { id: 2, difficultyTier: 'easy', seedBase: 14002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] }, - { id: 3, difficultyTier: 'normal', seedBase: 14003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] }, - { id: 4, difficultyTier: 'normal', seedBase: 14004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] }, - { id: 5, difficultyTier: 'hard', seedBase: 14005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] }, - { id: 6, difficultyTier: 'boss', seedBase: 14006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] }, + { id: 'seoul_01', name: '景福宫', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_01.png', tags: ['palace'] }, + { id: 'seoul_02', name: '南山塔', category: 'landmark', rarity: 'core', image: 'images/elements/seoul/seoul_02.png', tags: ['tower'] }, + { id: 'seoul_03', name: '石锅拌饭', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_03.png', tags: ['rice'] }, + { id: 'seoul_04', name: '韩式炸鸡', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_04.png', tags: ['snack'] }, + { id: 'seoul_05', name: '海苔卷', category: 'food', rarity: 'core', image: 'images/elements/seoul/seoul_05.png', tags: ['roll'] }, + { id: 'seoul_06', name: '韩服', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_06.png', tags: ['dress'] }, + { id: 'seoul_07', name: 'K-pop话筒', category: 'culture', rarity: 'core', image: 'images/elements/seoul/seoul_07.png', tags: ['music'] }, + { id: 'seoul_08', name: '烧酒瓶', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_08.png', tags: ['drink'] }, + { id: 'seoul_09', name: '太极旗扇', category: 'item', rarity: 'core', image: 'images/elements/seoul/seoul_09.png', tags: ['fan'] }, + { id: 'seoul_10', name: '银杏', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_10.png', tags: ['leaf'] }, + { id: 'seoul_11', name: '柿子', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_11.png', tags: ['fruit'] }, + { id: 'seoul_12', name: '松树', category: 'nature', rarity: 'core', image: 'images/elements/seoul/seoul_12.png', tags: ['tree'] }, ], + levelPresets: createLevelPresets(14000), shareCard: { titleUnlocked: '我解锁了首尔猫!', titlePassport: '首尔护照盖章完成', }, } + +export default seoul diff --git a/js/content/cities/singapore.js b/js/content/cities/singapore.js index f0b8972..8185509 100644 --- a/js/content/cities/singapore.js +++ b/js/content/cities/singapore.js @@ -1,9 +1,13 @@ -module.exports = { +import { createLevelPresets } from '../shared/level-presets.js' + +export const singapore = { id: 'singapore', contentVersion: 1, continentId: 'asia', + countryId: 'singapore_country', + regionId: null, sortOrder: 5, - unlockAfterCityId: 'seoul', + unlockAfterCityId: 'istanbul', bundle: { packId: 'asia-rest', preload: 'on-city-page', @@ -11,54 +15,45 @@ module.exports = { display: { name: '新加坡', nameEn: 'Singapore', - bgColor: '#2ECC71', - tagline: '鱼尾狮旁撸猫猫', - funFact: '新加坡是全球绿化覆盖率最高的城市之一', + bgColor: '#00A896', + tagline: '海湾花园看猫猫', + funFact: '新加坡是花园城市国家', }, cover: { catImage: 'images/cats/cat_singapore.png', catThumb: 'images/cats/cat_singapore_thumb.png', - shareAccent: '#F28C28', + shareAccent: '#02C39A', }, cat: { id: 'cat_singapore', - name: '狮狮', - intro: '戴小狮子鬃毛的花猫,骄傲地守护鱼尾狮', - baseColor: '#FFFFFF', + name: '狮喵', + baseColor: '#F2C078', pattern: 'calico', - patternColor: '#F28C28', - accessory: '小狮子鬃毛', - expression: 'proud', + accessory: '小狮鬃', }, passport: { stampId: 'stamp_singapore', stampLabel: '新加坡', }, elements: [ - { id: 'singapore_01', name: '鱼尾狮', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_01.png', tags: ['statue', 'water', 'iconic'] }, - { id: 'singapore_02', name: '辣椒螃蟹', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_02.png', tags: ['seafood', 'spicy', 'red'] }, - { id: 'singapore_03', name: '榴莲建筑', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_03.png', tags: ['theater', 'spiky', 'modern'] }, - { id: 'singapore_04', name: '叻沙', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_04.png', tags: ['noodle', 'coconut', 'spicy'] }, - { id: 'singapore_05', name: '金沙酒店', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_05.png', tags: ['hotel', 'pool', 'skyline'] }, - { id: 'singapore_06', name: '兰花', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_06.png', tags: ['flower', 'purple', 'national'] }, - { id: 'singapore_07', name: '肉骨茶', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_07.png', tags: ['soup', 'herbal', 'pork'] }, - { id: 'singapore_08', name: '冰激凌三明治', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_08.png', tags: ['dessert', 'cold', 'colorful'] }, - { id: 'singapore_09', name: '组屋', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_09.png', tags: ['housing', 'colorful', 'block'] }, - { id: 'singapore_10', name: '咖椰吐司', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_10.png', tags: ['toast', 'jam', 'breakfast'] }, - { id: 'singapore_11', name: '摩天轮', category: 'item', rarity: 'core', image: 'images/elements/singapore/singapore_11.png', tags: ['wheel', 'night', 'view'] }, - { id: 'singapore_12', name: '热带雨林', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_12.png', tags: ['forest', 'green', 'tropical'] }, - { id: 'singapore_13', name: '娘惹瓷砖', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_13.png', tags: ['tile', 'pattern', 'peranakan'] }, - ], - levelPresets: [ - { id: 1, difficultyTier: 'intro', seedBase: 15001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] }, - { id: 2, difficultyTier: 'easy', seedBase: 15002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] }, - { id: 3, difficultyTier: 'normal', seedBase: 15003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] }, - { id: 4, difficultyTier: 'normal', seedBase: 15004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] }, - { id: 5, difficultyTier: 'hard', seedBase: 15005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] }, - { id: 6, difficultyTier: 'boss', seedBase: 15006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] }, + { id: 'singapore_01', name: '鱼尾狮', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_01.png', tags: ['statue'] }, + { id: 'singapore_02', name: '金沙酒店', category: 'landmark', rarity: 'core', image: 'images/elements/singapore/singapore_02.png', tags: ['hotel'] }, + { id: 'singapore_03', name: '叻沙', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_03.png', tags: ['noodle'] }, + { id: 'singapore_04', name: '辣椒螃蟹', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_04.png', tags: ['crab'] }, + { id: 'singapore_05', name: '咖椰吐司', category: 'food', rarity: 'core', image: 'images/elements/singapore/singapore_05.png', tags: ['toast'] }, + { id: 'singapore_06', name: '娘惹花砖', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_06.png', tags: ['tile'] }, + { id: 'singapore_07', name: '小红点贴纸', category: 'culture', rarity: 'core', image: 'images/elements/singapore/singapore_07.png', tags: ['badge'] }, + { id: 'singapore_08', name: '冰淇淋三明治车', category: 'item', rarity: 'core', image: 'images/elements/singapore/singapore_08.png', tags: ['cart'] }, + { id: 'singapore_09', name: '组屋钥匙', category: 'item', rarity: 'core', image: 'images/elements/singapore/singapore_09.png', tags: ['key'] }, + { id: 'singapore_10', name: '兰花', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_10.png', tags: ['flower'] }, + { id: 'singapore_11', name: '榴莲', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_11.png', tags: ['fruit'] }, + { id: 'singapore_12', name: '雨树', category: 'nature', rarity: 'core', image: 'images/elements/singapore/singapore_12.png', tags: ['tree'] }, ], + levelPresets: createLevelPresets(15000), shareCard: { titleUnlocked: '我解锁了新加坡猫!', titlePassport: '新加坡护照盖章完成', }, } + +export default singapore diff --git a/js/content/cities/tokyo.js b/js/content/cities/tokyo.js index f6b7ac3..166cf71 100644 --- a/js/content/cities/tokyo.js +++ b/js/content/cities/tokyo.js @@ -1,9 +1,13 @@ -module.exports = { +import { createLevelPresets } from '../shared/level-presets.js' + +export const tokyo = { id: 'tokyo', contentVersion: 1, continentId: 'asia', + countryId: 'japan', + regionId: null, sortOrder: 2, - unlockAfterCityId: 'beijing', + unlockAfterCityId: 'bangkok', bundle: { packId: 'asia-rest', preload: 'on-city-page', @@ -11,55 +15,45 @@ module.exports = { display: { name: '东京', nameEn: 'Tokyo', - bgColor: '#E84057', - tagline: '东京タワー下的喵', - funFact: '东京的铁路网是世界上最密集的', + bgColor: '#FF6F91', + tagline: '樱花街头看猫猫', + funFact: '东京拥有世界级的铁路网络', }, cover: { catImage: 'images/cats/cat_tokyo.png', catThumb: 'images/cats/cat_tokyo_thumb.png', - shareAccent: '#FFB7C5', + shareAccent: '#FF9671', }, cat: { id: 'cat_tokyo', - name: '樱樱', - intro: '戴着招财猫铃铛的白猫,喜欢在樱花树下打盹', - baseColor: '#FFFFFF', + name: '樱丸', + baseColor: '#F7F7F7', pattern: 'solid', - patternColor: '#F5F5F5', - accessory: '招财猫铃铛', - expression: 'wink', + accessory: '铃铛', }, passport: { stampId: 'stamp_tokyo', stampLabel: '东京', }, elements: [ - { id: 'tokyo_01', name: '寿司', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_01.png', tags: ['fish', 'rice', 'fresh'] }, - { id: 'tokyo_02', name: '招财猫', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_02.png', tags: ['lucky', 'cat', 'golden'] }, - { id: 'tokyo_03', name: '富士山', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_03.png', tags: ['mountain', 'snow', 'iconic'] }, - { id: 'tokyo_04', name: '樱花', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_04.png', tags: ['flower', 'pink', 'spring'] }, - { id: 'tokyo_05', name: '鸟居', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_05.png', tags: ['gate', 'red', 'shrine'] }, - { id: 'tokyo_06', name: '拉面', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_06.png', tags: ['noodle', 'hot', 'bowl'] }, - { id: 'tokyo_07', name: '抹茶', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_07.png', tags: ['tea', 'green', 'powder'] }, - { id: 'tokyo_08', name: '浮世绘', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_08.png', tags: ['art', 'wave', 'woodblock'] }, - { id: 'tokyo_09', name: '新干线', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_09.png', tags: ['train', 'fast', 'white'] }, - { id: 'tokyo_10', name: '达摩', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_10.png', tags: ['doll', 'red', 'wish'] }, - { id: 'tokyo_11', name: '和服扇', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_11.png', tags: ['fan', 'elegant', 'pattern'] }, - { id: 'tokyo_12', name: '章鱼烧', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_12.png', tags: ['octopus', 'ball', 'street-food'] }, - { id: 'tokyo_13', name: '东京塔', category: 'landmark', rarity: 'accent', image: 'images/elements/tokyo/tokyo_13.png', tags: ['tower', 'orange', 'night'] }, - { id: 'tokyo_14', name: '柴犬', category: 'nature', rarity: 'accent', image: 'images/elements/tokyo/tokyo_14.png', tags: ['dog', 'cute', 'fluffy'] }, - ], - levelPresets: [ - { id: 1, difficultyTier: 'intro', seedBase: 12001, elementCount: 6, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.95, targetDurationSec: [60, 120] }, - { id: 2, difficultyTier: 'easy', seedBase: 12002, elementCount: 7, piecesPerElement: 3, layers: 2, density: 'low', targetPassRate: 0.90, targetDurationSec: [60, 120] }, - { id: 3, difficultyTier: 'normal', seedBase: 12003, elementCount: 8, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.80, targetDurationSec: [90, 150] }, - { id: 4, difficultyTier: 'normal', seedBase: 12004, elementCount: 9, piecesPerElement: 3, layers: 3, density: 'medium', targetPassRate: 0.70, targetDurationSec: [90, 150] }, - { id: 5, difficultyTier: 'hard', seedBase: 12005, elementCount: 10, piecesPerElement: 3, layers: 4, density: 'medium_high', targetPassRate: 0.60, targetDurationSec: [120, 180] }, - { id: 6, difficultyTier: 'boss', seedBase: 12006, elementCount: 10, piecesPerElement: [3,3,3,3,3,6,3,3,3,3], layers: 4, density: 'high', targetPassRate: 0.55, targetDurationSec: [120, 240] }, + { id: 'tokyo_01', name: '东京塔', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_01.png', tags: ['tower'] }, + { id: 'tokyo_02', name: '鸟居', category: 'landmark', rarity: 'core', image: 'images/elements/tokyo/tokyo_02.png', tags: ['gate'] }, + { id: 'tokyo_03', name: '寿司', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_03.png', tags: ['rice'] }, + { id: 'tokyo_04', name: '拉面', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_04.png', tags: ['noodle'] }, + { id: 'tokyo_05', name: '章鱼烧', category: 'food', rarity: 'core', image: 'images/elements/tokyo/tokyo_05.png', tags: ['snack'] }, + { id: 'tokyo_06', name: '招财猫', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_06.png', tags: ['lucky'] }, + { id: 'tokyo_07', name: '浮世绘', category: 'culture', rarity: 'core', image: 'images/elements/tokyo/tokyo_07.png', tags: ['art'] }, + { id: 'tokyo_08', name: '和服扇', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_08.png', tags: ['fan'] }, + { id: 'tokyo_09', name: '新干线票', category: 'item', rarity: 'core', image: 'images/elements/tokyo/tokyo_09.png', tags: ['ticket'] }, + { id: 'tokyo_10', name: '樱花', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_10.png', tags: ['flower'] }, + { id: 'tokyo_11', name: '富士山', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_11.png', tags: ['mountain'] }, + { id: 'tokyo_12', name: '锦鲤', category: 'nature', rarity: 'core', image: 'images/elements/tokyo/tokyo_12.png', tags: ['fish'] }, ], + levelPresets: createLevelPresets(12000), shareCard: { titleUnlocked: '我解锁了东京猫!', titlePassport: '东京护照盖章完成', }, } + +export default tokyo diff --git a/js/content/continents/asia.js b/js/content/continents/asia.js index 9ac6466..211d028 100644 --- a/js/content/continents/asia.js +++ b/js/content/continents/asia.js @@ -1,32 +1,5 @@ -// 亚洲国家列表 — 16 国,分 2 页 - -export const asiaCountries = [ - // Page 1 - { id: 'china', name: '中国', nameEn: 'China', sortOrder: 1, themeColor: '#CC2936', hasRegions: true, totalCities: 40 }, - { id: 'japan', name: '日本', nameEn: 'Japan', sortOrder: 2, themeColor: '#E84057', hasRegions: false, totalCities: 4 }, - { id: 'korea', name: '韩国', nameEn: 'South Korea', sortOrder: 3, themeColor: '#4A90D9', hasRegions: false, totalCities: 3 }, - { id: 'thailand', name: '泰国', nameEn: 'Thailand', sortOrder: 4, themeColor: '#FFB347', hasRegions: false, totalCities: 3 }, - { id: 'singapore', name: '新加坡', nameEn: 'Singapore', sortOrder: 5, themeColor: '#00A896', hasRegions: false, totalCities: 1 }, - { id: 'vietnam', name: '越南', nameEn: 'Vietnam', sortOrder: 6, themeColor: '#E74C3C', hasRegions: false, totalCities: 2 }, - { id: 'malaysia', name: '马来西亚', nameEn: 'Malaysia', sortOrder: 7, themeColor: '#3498DB', hasRegions: false, totalCities: 2 }, - { id: 'indonesia', name: '印度尼西亚', nameEn: 'Indonesia', sortOrder: 8, themeColor: '#E67E22', hasRegions: false, totalCities: 2 }, - { id: 'philippines', name: '菲律宾', nameEn: 'Philippines', sortOrder: 9, themeColor: '#2ECC71', hasRegions: false, totalCities: 1 }, - // Page 2 - { id: 'india', name: '印度', nameEn: 'India', sortOrder: 10, themeColor: '#FF9933', hasRegions: false, totalCities: 2 }, - { id: 'uae', name: '阿联酋', nameEn: 'UAE', sortOrder: 11, themeColor: '#C0A062', hasRegions: false, totalCities: 1 }, - { id: 'turkey', name: '土耳其', nameEn: 'Turkey', sortOrder: 12, themeColor: '#1A5276', hasRegions: false, totalCities: 1 }, - { id: 'israel', name: '以色列', nameEn: 'Israel', sortOrder: 13, themeColor: '#5DADE2', hasRegions: false, totalCities: 1 }, - { id: 'nepal', name: '尼泊尔', nameEn: 'Nepal', sortOrder: 14, themeColor: '#DC143C', hasRegions: false, totalCities: 1 }, - { id: 'cambodia', name: '柬埔寨', nameEn: 'Cambodia', sortOrder: 15, themeColor: '#1E8449', hasRegions: false, totalCities: 1 }, - { id: 'sri_lanka', name: '斯里兰卡', nameEn: 'Sri Lanka', sortOrder: 16, themeColor: '#8E44AD', hasRegions: false, totalCities: 1 }, - { id: 'myanmar', name: '缅甸', nameEn: 'Myanmar', sortOrder: 17, themeColor: '#F4D03F', hasRegions: false, totalCities: 1 }, - { id: 'mongolia', name: '蒙古', nameEn: 'Mongolia', sortOrder: 18, themeColor: '#2980B9', hasRegions: false, totalCities: 1 }, -] - -// 分页:2 页 -export const asiaCountryPages = [ - asiaCountries.slice(0, 9), - asiaCountries.slice(9), -] - -export default asiaCountries +/** + * @deprecated 亚洲国家列表已移入 navigation/future-catalog.js + * MVP 阶段亚洲直接列出城市,不经过国家层。 + */ +export { asiaCountryPlan as asiaCountries } from '../navigation/future-catalog.js' diff --git a/js/content/continents/index.js b/js/content/continents/index.js index 849736d..5913825 100644 --- a/js/content/continents/index.js +++ b/js/content/continents/index.js @@ -1,16 +1,5 @@ -// 洲索引 — 3x3 棋盘第 1 级 -// 每页 9 格,当前 6 个洲 + 3 空位 - -export const continents = [ - { id: 'asia', name: '亚洲', nameEn: 'Asia', sortOrder: 1, themeColor: '#FF6B6B', icon: '🏯', totalCities: 70 }, - { id: 'europe', name: '欧洲', nameEn: 'Europe', sortOrder: 2, themeColor: '#6B8CFF', icon: '🏰', totalCities: 28 }, - { id: 'north_america', name: '北美洲', nameEn: 'North America', sortOrder: 3, themeColor: '#FFB347', icon: '🗽', totalCities: 12 }, - { id: 'south_america', name: '南美洲', nameEn: 'South America', sortOrder: 4, themeColor: '#4ECDC4', icon: '🌴', totalCities: 7 }, - { id: 'africa', name: '非洲', nameEn: 'Africa', sortOrder: 5, themeColor: '#F7DC6F', icon: '🦁', totalCities: 5 }, - { id: 'oceania', name: '大洋洲', nameEn: 'Oceania', sortOrder: 6, themeColor: '#82E0AA', icon: '🦘', totalCities: 4 }, -] - -// 分页:1 页(6 项 ≤ 9) -export const continentPages = [continents] - -export default continents +/** + * @deprecated 请使用 js/content/navigation/runtime-nav.js + * 此文件保留仅为向后兼容,内容从 runtime-nav 导出。 + */ +export { runtimeNavNodes as continents, getEnabledRoots, getNavNode } from '../navigation/runtime-nav.js' diff --git a/js/content/countries/japan.js b/js/content/countries/japan.js index a94974e..6ed4cbe 100644 --- a/js/content/countries/japan.js +++ b/js/content/countries/japan.js @@ -1,7 +1,6 @@ -export const japanCities = { - countryId: 'japan', - cityIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'], - // 4 城市,1 页 -} - +/** + * @deprecated 国家城市列表已移入 navigation/future-catalog.js + */ +import { asiaCountryPlan } from '../navigation/future-catalog.js' +export const japanCities = asiaCountryPlan.find(c => c.id === 'japan') export default japanCities diff --git a/js/content/navigation/future-catalog.js b/js/content/navigation/future-catalog.js new file mode 100644 index 0000000..8f403cd --- /dev/null +++ b/js/content/navigation/future-catalog.js @@ -0,0 +1,50 @@ +/** + * Future Catalog — 未来版本导航规划 + * + * 这些节点不进入当前 runtime 消费链路。 + * 当新版本上线新城市时,将对应节点移入 runtime-nav.js 并设 isEnabled: true。 + * 引用的 cityId 可能尚不存在对应 CityManifest。 + */ + +// V1.1: 中国一线城市扩展 +export const v11_china_cities = ['shanghai', 'guangzhou', 'chengdu', 'shenzhen', 'hangzhou', 'chongqing'] + +// V1.2: 中国二线 A +export const v12_china_cities = ['wuhan', 'xian', 'changsha', 'nanjing', 'harbin', 'xiamen'] + +// V1.3: 欧洲热门 +export const v13_europe_cities = ['paris', 'london', 'rome', 'barcelona', 'prague', 'amsterdam'] + +// 中国地区划分(V1.1+ 启用国家→地区→城市导航时使用) +export const chinaRegionPlan = [ + { id: 'north_china', name: '华北', cityIds: ['beijing', 'tianjin', 'shijiazhuang', 'taiyuan', 'hohhot'] }, + { id: 'northeast', name: '东北', cityIds: ['shenyang', 'changchun', 'harbin', 'dalian'] }, + { id: 'east_china', name: '华东', cityIds: ['shanghai', 'nanjing', 'hangzhou', 'hefei', 'fuzhou', 'nanchang', 'jinan', 'suzhou', 'xiamen', 'qingdao', 'ningbo'] }, + { id: 'central_china', name: '华中', cityIds: ['zhengzhou', 'wuhan', 'changsha'] }, + { id: 'south_china', name: '华南', cityIds: ['guangzhou', 'shenzhen', 'nanning', 'haikou', 'sanya'] }, + { id: 'southwest', name: '西南', cityIds: ['chengdu', 'chongqing', 'guiyang', 'kunming', 'lhasa'] }, + { id: 'northwest', name: '西北', cityIds: ['xian', 'lanzhou', 'xining', 'yinchuan', 'urumqi'] }, + { id: 'hk_macao_tw', name: '港澳台', cityIds: ['hongkong', 'macao', 'taipei'] }, +] + +// 全部亚洲国家(V1.1+ 启用) +export const asiaCountryPlan = [ + { id: 'china', name: '中国', hasRegions: true, totalCities: 41 }, + { id: 'japan', name: '日本', cityIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'] }, + { id: 'korea', name: '韩国', cityIds: ['seoul', 'busan', 'jeju'] }, + { id: 'thailand', name: '泰国', cityIds: ['bangkok', 'chiangmai', 'phuket'] }, + { id: 'singapore_country', name: '新加坡', cityIds: ['singapore'] }, + { id: 'vietnam', name: '越南', cityIds: ['hanoi', 'hochiminh'] }, + { id: 'malaysia', name: '马来西亚', cityIds: ['kualalumpur', 'malacca'] }, + { id: 'indonesia', name: '印度尼西亚', cityIds: ['jakarta', 'bali'] }, + { id: 'philippines', name: '菲律宾', cityIds: ['manila'] }, + { id: 'india', name: '印度', cityIds: ['delhi', 'mumbai'] }, + { id: 'uae', name: '阿联酋', cityIds: ['dubai'] }, + { id: 'turkey', name: '土耳其', cityIds: ['istanbul'] }, + { id: 'israel', name: '以色列', cityIds: ['jerusalem'] }, + { id: 'nepal', name: '尼泊尔', cityIds: ['kathmandu'] }, + { id: 'cambodia', name: '柬埔寨', cityIds: ['siemreap'] }, + { id: 'sri_lanka', name: '斯里兰卡', cityIds: ['colombo'] }, + { id: 'myanmar', name: '缅甸', cityIds: ['yangon'] }, + { id: 'mongolia', name: '蒙古', cityIds: ['ulaanbaatar'] }, +] diff --git a/js/content/navigation/nav-schema.js b/js/content/navigation/nav-schema.js new file mode 100644 index 0000000..37937c5 --- /dev/null +++ b/js/content/navigation/nav-schema.js @@ -0,0 +1,37 @@ +/** + * NavNode — 统一导航节点 schema + * + * 所有导航层级(洲/国家/地区/城市组)共用同一 shape。 + * 分页通过 shared/pagination.js 的 paginate() 动态计算,不手写 page 常量。 + * + * @typedef {Object} NavNode + * @property {'continent'|'country'|'region'|'city-group'} type + * @property {string} id - 全局唯一标识 + * @property {string|null} parentId - 父节点 id,洲级为 null + * @property {string} name - 中文名 + * @property {string} nameEn - 英文名 + * @property {number} sortOrder - 同级排序 + * @property {string} themeColor - 主题色 HEX + * @property {'country'|'region'|'city'|null} childType - 子节点类型 + * @property {string[]} childIds - 当前可消费的子节点 id(仅已启用的) + * @property {number} pageSize - 每页格子数,默认 9 + * @property {boolean} isEnabled - 当前版本是否启用 + * @property {boolean} isUnlockedByDefault - 是否默认解锁 + */ + +// schema 验证函数 +export function validateNavNode(node) { + const required = ['type', 'id', 'name', 'nameEn', 'sortOrder', 'themeColor', 'childType', 'childIds', 'pageSize', 'isEnabled', 'isUnlockedByDefault'] + const validTypes = ['continent', 'country', 'region', 'city-group'] + const validChildTypes = ['country', 'region', 'city', null] + + const errors = [] + for (const key of required) { + if (!(key in node)) errors.push(`missing field: ${key}`) + } + if (!validTypes.includes(node.type)) errors.push(`invalid type: ${node.type}`) + if (!validChildTypes.includes(node.childType)) errors.push(`invalid childType: ${node.childType}`) + if (!Array.isArray(node.childIds)) errors.push('childIds must be array') + if (node.parentId !== null && typeof node.parentId !== 'string') errors.push('parentId must be string or null') + return errors +} diff --git a/js/content/navigation/runtime-nav.js b/js/content/navigation/runtime-nav.js new file mode 100644 index 0000000..76ee479 --- /dev/null +++ b/js/content/navigation/runtime-nav.js @@ -0,0 +1,110 @@ +/** + * Runtime Navigation — MVP + * + * 只包含当前版本已启用、childIds 均指向已存在实体的节点。 + * MVP 导航:直入亚洲城市页,6 城市平铺。 + * V1.1+ 启用完整洲→国家→地区→城市多级导航时,在此文件追加节点。 + */ + +export const runtimeNavNodes = [ + // ── MVP: 只有 1 个洲,直接列出 6 城市 ── + { + type: 'continent', + id: 'asia', + parentId: null, + name: '亚洲', + nameEn: 'Asia', + sortOrder: 1, + themeColor: '#FF6B6B', + childType: 'city', // MVP: 直接指向城市,跳过国家层 + childIds: ['beijing', 'tokyo', 'bangkok', 'seoul', 'singapore', 'istanbul'], + pageSize: 9, + isEnabled: true, + isUnlockedByDefault: true, + }, + + // ── 未启用的洲(占位,UI 显示锁定态) ── + { + type: 'continent', + id: 'europe', + parentId: null, + name: '欧洲', + nameEn: 'Europe', + sortOrder: 2, + themeColor: '#6B8CFF', + childType: 'country', + childIds: [], // 无可消费子节点 + pageSize: 9, + isEnabled: false, + isUnlockedByDefault: false, + }, + { + type: 'continent', + id: 'north_america', + parentId: null, + name: '北美洲', + nameEn: 'North America', + sortOrder: 3, + themeColor: '#FFB347', + childType: 'country', + childIds: [], + pageSize: 9, + isEnabled: false, + isUnlockedByDefault: false, + }, + { + type: 'continent', + id: 'south_america', + parentId: null, + name: '南美洲', + nameEn: 'South America', + sortOrder: 4, + themeColor: '#4ECDC4', + childType: 'country', + childIds: [], + pageSize: 9, + isEnabled: false, + isUnlockedByDefault: false, + }, + { + type: 'continent', + id: 'africa', + parentId: null, + name: '非洲', + nameEn: 'Africa', + sortOrder: 5, + themeColor: '#F7DC6F', + childType: 'country', + childIds: [], + pageSize: 9, + isEnabled: false, + isUnlockedByDefault: false, + }, + { + type: 'continent', + id: 'oceania', + parentId: null, + name: '大洋洲', + nameEn: 'Oceania', + sortOrder: 6, + themeColor: '#82E0AA', + childType: 'country', + childIds: [], + pageSize: 9, + isEnabled: false, + isUnlockedByDefault: false, + }, +] + +// ── 便捷查询 ── +export function getNavNode(id) { + return runtimeNavNodes.find(n => n.id === id) || null +} + +export function getEnabledRoots() { + return runtimeNavNodes.filter(n => n.parentId === null && n.isEnabled) +} + +export function getChildren(parentId) { + return runtimeNavNodes.filter(n => n.parentId === parentId && n.isEnabled) +} diff --git a/js/content/regions/china.js b/js/content/regions/china.js index 2117209..85d26e2 100644 --- a/js/content/regions/china.js +++ b/js/content/regions/china.js @@ -1,73 +1,5 @@ -// 中国地区列表 — 8 个地区,1 页 - -export const chinaRegions = [ - { - id: 'north_china', - name: '华北', - nameEn: 'North China', - sortOrder: 1, - themeColor: '#CC2936', - cityIds: ['beijing', 'tianjin', 'shijiazhuang', 'taiyuan', 'hohhot'], - }, - { - id: 'northeast', - name: '东北', - nameEn: 'Northeast', - sortOrder: 2, - themeColor: '#5DADE2', - cityIds: ['shenyang', 'changchun', 'harbin', 'dalian'], - }, - { - id: 'east_china', - name: '华东', - nameEn: 'East China', - sortOrder: 3, - themeColor: '#2ECC71', - cityIds: ['shanghai', 'nanjing', 'hangzhou', 'hefei', 'fuzhou', 'nanchang', 'jinan', 'suzhou', 'xiamen', 'qingdao', 'ningbo'], - }, - { - id: 'central_china', - name: '华中', - nameEn: 'Central China', - sortOrder: 4, - themeColor: '#E67E22', - cityIds: ['zhengzhou', 'wuhan', 'changsha'], - }, - { - id: 'south_china', - name: '华南', - nameEn: 'South China', - sortOrder: 5, - themeColor: '#FF6B6B', - cityIds: ['guangzhou', 'shenzhen', 'nanning', 'haikou', 'sanya'], - }, - { - id: 'southwest', - name: '西南', - nameEn: 'Southwest', - sortOrder: 6, - themeColor: '#9B59B6', - cityIds: ['chengdu', 'chongqing', 'guiyang', 'kunming', 'lhasa'], - }, - { - id: 'northwest', - name: '西北', - nameEn: 'Northwest', - sortOrder: 7, - themeColor: '#F4D03F', - cityIds: ['xian', 'lanzhou', 'xining', 'yinchuan', 'urumqi'], - }, - { - id: 'hk_macao_tw', - name: '港澳台', - nameEn: 'HK, Macao & Taiwan', - sortOrder: 8, - themeColor: '#E74C3C', - cityIds: ['hongkong', 'macao', 'taipei'], - }, -] - -// 分页:1 页(8 项 ≤ 9) -export const chinaRegionPages = [chinaRegions] - -export default chinaRegions +/** + * @deprecated 中国地区数据已移入 navigation/future-catalog.js + * 仅在 V1.1+ 启用国家→地区导航后使用。 + */ +export { chinaRegionPlan as chinaRegions } from '../navigation/future-catalog.js'