From faeb453a648b5cf6a16fcd5b1591145004c07a22 Mon Sep 17 00:00:00 2001 From: manpengan Date: Sat, 28 Mar 2026 23:20:58 +0800 Subject: [PATCH] =?UTF-8?q?nav:=20=E4=B8=89=E7=BA=A7=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E5=88=86=E9=A1=B5=E7=BB=93=E6=9E=84=20+=20=E4=B8=AD=E5=9B=BD?= =?UTF-8?q?=E5=9C=B0=E5=8C=BA=E7=B4=A2=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 02-game-design 地图结构重写:洲→国家→城市,3x3棋盘分页 - 中国 40 城按 8 地区分组(华北/东北/华东/华中/华南/西南/西北/港澳) - 导航索引数据:continents/index + asia + regions/china + pagination工具 Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/02-game-design.md | 164 +++++++++++++++++++++++++---- js/content/continents/asia.js | 32 ++++++ js/content/continents/index.js | 16 +++ js/content/countries/japan.js | 7 ++ js/content/regions/china.js | 73 +++++++++++++ js/content/shared/level-presets.js | 70 ++++++++++++ js/content/shared/pagination.js | 14 +++ 7 files changed, 355 insertions(+), 21 deletions(-) create mode 100644 js/content/continents/asia.js create mode 100644 js/content/continents/index.js create mode 100644 js/content/countries/japan.js create mode 100644 js/content/regions/china.js create mode 100644 js/content/shared/level-presets.js create mode 100644 js/content/shared/pagination.js diff --git a/docs/02-game-design.md b/docs/02-game-design.md index 46601d3..b85da25 100644 --- a/docs/02-game-design.md +++ b/docs/02-game-design.md @@ -75,38 +75,160 @@ ## 2. 地图结构 -### 2.1 导航层级(MVP 简化为二级) +### 2.1 导航层级 ``` -MVP 首版:直接进入亚洲城市页(跳过洲选择页) - 城市选择(3x3 棋盘) → 城市关卡(单局游戏) - -V1.1 扩展第二个洲后: - 洲选择(3x3 棋盘) → 城市选择(3x3 棋盘) → 城市关卡 +洲(3x3 棋盘) + └── 国家(3x3 棋盘,超过 9 国分页) + └── 城市(3x3 棋盘,超过 9 城分页) + └── 关卡(单局游戏) ``` -预留洲选择页的代码入口但 MVP 不显示。 +中国因城市数量多(40 城),额外增加"地区"层级: -### 2.2 主页面设计 +``` +亚洲 → 中国 → 地区(3x3)→ 城市(3x3)→ 关卡 +亚洲 → 日本 → 城市(3x3)→ 关卡 +``` -- MVP 直接显示亚洲城市 3x3 棋盘,顶部标题"亚洲" -- V1.1 扩展第二个洲后再开启洲选择页 +### 2.2 洲选择页(第 1 级) -### 2.3 洲内页面设计 +1 页,3×3 棋盘: -- 3x3 棋盘布局,每格代表一个城市 -- 城市封面:特色猫猫头像 + 城市名 -- 已通关城市:猫猫头像点亮 + 通关星级 -- 未解锁城市:灰色剪影 + 锁图标 -- 解锁规则:顺序解锁(通关前一个解锁下一个),第 1 个城市默认解锁 +``` +┌────────┬────────┬────────┐ +│ 亚洲 │ 欧洲 │ 北美洲 │ +├────────┼────────┼────────┤ +│ 南美洲 │ 非洲 │ 大洋洲 │ +├────────┼────────┼────────┤ +│ │ │ │ +└────────┴────────┴────────┘ +``` -### 2.4 城市关卡结构 +6 个洲,3 个空位留给未来扩展。MVP 只开放亚洲,其余显示锁定 + "即将开放"。 -每个城市包含 **6 个基础关卡**,全部通关才算"通关该城市"。 +每个洲的封面:洲标志性图案 + 解锁进度(如"6/70 城市已解锁")。 -- 每关使用该城市 12-15 个特色元素中的 8-12 个 -- 难度递增:关 1 用 6 种元素各 3 个,关 6 用 10 种元素,各 3 或 6 个(混合配置) + 更深堆叠 -- 通关后获得该城市的猫猫收集卡 +### 2.3 国家选择页(第 2 级) + +#### 亚洲 — 2 页 + +Page 1: +``` +┌────────┬────────┬────────┐ +│ 中国 │ 日本 │ 韩国 │ +├────────┼────────┼────────┤ +│ 泰国 │ 新加坡 │ 越南 │ +├────────┼────────┼────────┤ +│马来西亚│印度尼西亚│ 菲律宾│ +└────────┴────────┴────────┘ +``` + +Page 2: +``` +┌────────┬────────┬────────┐ +│ 印度 │ 阿联酋 │ 土耳其 │ +├────────┼────────┼────────┤ +│ 以色列 │ 尼泊尔 │ 柬埔寨 │ +├────────┼────────┼────────┤ +│斯里兰卡│ 缅甸 │ 蒙古 │ +└────────┴────────┴────────┘ +``` + +#### 欧洲 — 2 页 + +Page 1: +``` +法国、英国、意大利、西班牙、德国、荷兰、瑞士、奥地利、捷克 +``` + +Page 2: +``` +希腊、俄罗斯、葡萄牙、挪威、瑞典、丹麦、芬兰、冰岛、匈牙利 +``` + +#### 北美洲 — 1 页 +``` +美国、加拿大、墨西哥 + 6 空位 +``` + +#### 南美洲 — 1 页 +``` +巴西、阿根廷、秘鲁、智利、哥伦比亚 + 4 空位 +``` + +#### 非洲 — 1 页 +``` +埃及、南非、摩洛哥、肯尼亚 + 5 空位 +``` + +#### 大洋洲 — 1 页 +``` +澳大利亚、新西兰 + 7 空位 +``` + +### 2.4 中国地区选择页(第 3 级,仅中国) + +中国 40 个城市按地区分组,1 页 3×3: + +``` +┌────────┬────────┬────────┐ +│ 华北 │ 东北 │ 华东 │ +├────────┼────────┼────────┤ +│ 华中 │ 华南 │ 西南 │ +├────────┼────────┼────────┤ +│ 西北 │ 港澳 │ │ +└────────┴────────┴────────┘ +``` + +各地区包含城市: + +| 地区 | 城市 | 数量 | 页数 | +|------|------|------|------| +| 华北 | 北京、天津、石家庄、太原、呼和浩特 | 5 | 1 | +| 东北 | 沈阳、长春、哈尔滨、大连 | 4 | 1 | +| 华东 | 上海、南京、杭州、合肥、福州、南昌、济南、苏州、厦门、青岛、宁波 | 11 | 2 | +| 华中 | 郑州、武汉、长沙 | 3 | 1 | +| 华南 | 广州、深圳、南宁、海口、三亚 | 5 | 1 | +| 西南 | 成都、重庆、贵阳、昆明、拉萨 | 5 | 1 | +| 西北 | 西安、兰州、西宁、银川、乌鲁木齐 | 5 | 1 | +| 港澳 | 香港、澳门 | 2 | 1 | + +### 2.5 城市选择页(第 3/4 级) + +#### 其他国家(非中国)— 直接展示城市 + +| 国家 | 城市数 | 页数 | +|------|--------|------| +| 日本 | 4(东京、大阪、京都、札幌) | 1 | +| 韩国 | 3(首尔、釜山、济州) | 1 | +| 泰国 | 3(曼谷、清迈、普吉) | 1 | +| 美国 | 7(纽约、洛杉矶、旧金山、拉斯维加斯、华盛顿、芝加哥、迈阿密) | 1 | +| 意大利 | 4(罗马、米兰、威尼斯、佛罗伦萨) | 1 | +| 其他国家 | 1-3 | 1 | + +所有国家的城市数均 ≤ 9,无需分页(除华东 11 城需 2 页外)。 + +### 2.6 城市封面设计 + +每个格子显示: +- 该城市/国家/洲的特色猫猫头像(已解锁:彩色,未解锁:灰色剪影) +- 名称(中文) +- 进度信息(已解锁:"X/6 关",未解锁:"🔒") + +### 2.7 分页交互 + +- 左右滑动翻页,底部圆点指示器 +- 首次进入默认第 1 页 +- 翻页动画 0.3s 弹性过渡 + +### 2.8 解锁规则 + +- 同一层级内顺序解锁(通关前一个解锁下一个) +- 第一个项目默认解锁 +- 跨层级:通关一个国家所有城市后,解锁下一个国家 +- 中国地区内:通关华北所有城市后解锁东北,以此类推 +- MVP 只开放亚洲洲,亚洲内只开放中国(北京)和已有 MVP 城市 --- diff --git a/js/content/continents/asia.js b/js/content/continents/asia.js new file mode 100644 index 0000000..9ac6466 --- /dev/null +++ b/js/content/continents/asia.js @@ -0,0 +1,32 @@ +// 亚洲国家列表 — 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 diff --git a/js/content/continents/index.js b/js/content/continents/index.js new file mode 100644 index 0000000..849736d --- /dev/null +++ b/js/content/continents/index.js @@ -0,0 +1,16 @@ +// 洲索引 — 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 diff --git a/js/content/countries/japan.js b/js/content/countries/japan.js new file mode 100644 index 0000000..a94974e --- /dev/null +++ b/js/content/countries/japan.js @@ -0,0 +1,7 @@ +export const japanCities = { + countryId: 'japan', + cityIds: ['tokyo', 'osaka', 'kyoto', 'sapporo'], + // 4 城市,1 页 +} + +export default japanCities diff --git a/js/content/regions/china.js b/js/content/regions/china.js new file mode 100644 index 0000000..bfb7117 --- /dev/null +++ b/js/content/regions/china.js @@ -0,0 +1,73 @@ +// 中国地区列表 — 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', + name: '港澳', + nameEn: 'HK & Macao', + sortOrder: 8, + themeColor: '#E74C3C', + cityIds: ['hongkong', 'macao'], + }, +] + +// 分页:1 页(8 项 ≤ 9) +export const chinaRegionPages = [chinaRegions] + +export default chinaRegions diff --git a/js/content/shared/level-presets.js b/js/content/shared/level-presets.js new file mode 100644 index 0000000..28ad96b --- /dev/null +++ b/js/content/shared/level-presets.js @@ -0,0 +1,70 @@ +export function createLevelPresets(seedBase) { + return [ + { + id: 1, + difficultyTier: 'intro', + seedBase: seedBase + 1, + elementCount: 6, + piecesPerElement: 3, + layers: 2, + density: 'low', + targetPassRate: 0.95, + targetDurationSec: [90, 150], + }, + { + id: 2, + difficultyTier: 'warmup', + seedBase: seedBase + 2, + elementCount: 7, + piecesPerElement: 3, + layers: 2, + density: 'low', + targetPassRate: 0.9, + targetDurationSec: [100, 160], + }, + { + id: 3, + difficultyTier: 'ramp', + seedBase: seedBase + 3, + elementCount: 8, + piecesPerElement: 3, + layers: 3, + density: 'medium', + targetPassRate: 0.8, + targetDurationSec: [120, 180], + }, + { + id: 4, + difficultyTier: 'mid', + seedBase: seedBase + 4, + elementCount: 9, + piecesPerElement: 3, + layers: 3, + density: 'medium', + targetPassRate: 0.7, + targetDurationSec: [130, 190], + }, + { + id: 5, + difficultyTier: 'hard', + seedBase: seedBase + 5, + elementCount: 10, + piecesPerElement: 3, + layers: 4, + density: 'medium_high', + targetPassRate: 0.6, + targetDurationSec: [150, 220], + }, + { + id: 6, + difficultyTier: 'boss', + seedBase: seedBase + 6, + elementCount: 10, + piecesPerElement: [3, 3, 3, 3, 3, 6, 3, 3, 3, 3], + layers: 4, + density: 'high', + targetPassRate: 0.55, + targetDurationSec: [180, 260], + }, + ] +} diff --git a/js/content/shared/pagination.js b/js/content/shared/pagination.js new file mode 100644 index 0000000..c0fa6ff --- /dev/null +++ b/js/content/shared/pagination.js @@ -0,0 +1,14 @@ +// 通用分页工具 — 将数组按 pageSize 分页 +// 默认 pageSize = 9(3x3 棋盘) + +export function paginate(items, pageSize = 9) { + const pages = [] + for (let i = 0; i < items.length; i += pageSize) { + pages.push(items.slice(i, i + pageSize)) + } + return pages +} + +export function getPageCount(totalItems, pageSize = 9) { + return Math.ceil(totalItems / pageSize) +}