feat(shanhai): M5 代码灌装(占位资产)——5图/敌32/Boss10/sim参数化/meta解锁
- balance.js: MAPS 5图(裂谷原+不周山/昆仑墟/归墟/钟山占位)+ENEMIES 32+BOSSES 10+D2-D5解锁占位+各图波表占位 - sim.js: createSim mapId 参数化(按图取数,map1零变化硬证三局逐字节一致)+_debugFill限定敌混 - meta.js: unlockedMaps解锁持久化(存档data内,schemaVersion=1向后兼容)+胜局解锁下一图 - meta-tables.js: dex_full阈值gte:51->DEX_TOTAL动态(修既有硬编码双写bug) - game-logic.js: 出击页地图行驱动MAPS+isMapUnlocked接meta - 占位数值/sprite标待美术待平衡;map2-5占位pacing待平衡;选图入局接线待后续 - 回归全绿:真实13/13+meta11/11+build-tables9/9+meta-tables6/6;ui-trigger2 test4 pre-existing渲染层失败非M5引入 - M5完整验收需美术资产(图2-5/29兽sprite/BGM6)+真平衡扫描
This commit is contained in:
parent
18ba0bd60f
commit
6ca44e3b2c
@ -174,3 +174,17 @@ R1 八字段不是并列的,存在依赖树,三条共前置基线各自被
|
||||
逐层排除过程:round-7 修 juice 顿帧 bug 后九婴正常刷新但贴脸集火 rate=0(九婴不近身);扩 forensics 暴露 boss 坐标后 round-8 调九婴数值(hp7400→3600 等)群体仿真(sim-scan)显示多 build 能 win,但 CDP 真机 seed42 lose(出生 burst 不可持续);round-9 改 driver 抱稳安全距(删自保退远反射、tight-hold、冲撞切向闪避,走位机械目标全达成)仍 lose,坐实瓶颈在武器集火不到远处 Boss。sim-scan 群体"多 build win"是无满怪潮/不同度量的结果,被 CDP 真机满怪潮证伪。
|
||||
|
||||
九婴数值调整保留(balance.js 九婴 hp3600/dps30/冲撞四件削弱/狂化削平,狰王 hp2000;已调到普通人可用区间,群体层面有效)。**⑧win 胜路径记为已知限制,交 W-AXIS/游戏设计判断**:终 Boss 战是否给武器 Boss 优先级(动 sim.js/auto-targeting 机制)或降 1200s 后怪潮密度(动数值),二者均影响玩法平衡且与⑦c2 收敛反向拉扯,属游戏设计决策,不在采证范畴。driver(win-jiuying.mjs,坐标驱动贴脸集火)与 round-7~9 全套诊断证据留存山海 evidence/,游戏设计裁定后修复续跑可推进 win。
|
||||
|
||||
### 6.8 2026-07-25 背景线全收口 + ⑧win 胜路径达成(推翻 §6.7 已知限制)
|
||||
|
||||
背景拼接问题经五步彻底解决并定稿入库:① 纯代码缓解(视差+羽化)→ ② 加有界竞技场(ARENA 1560×2960,玩家 clamp + 镜头 clamp + drawPlayer 解耦)+ 接 AI 草图整幅背景图(创始人审"还行")→ ③ 微调贴边/裁切(clamp 边距提到岩壁内沿)+ 自然暗角 + 降颗粒 → ④ 治本去除 jpg 烤黑边(裁黑带 + bicubic 回填,四边岩纹收口)→ ⑤ 加边界后重跑⑧。背景定稿为 1560×2960 矿彩国风裂谷原整图、四边岩纹/暗崖收口、无拼接无黑框,右崖本征暗阴影保留作暗底夜巡风格。commit 链:3e4f6fa3(juice 修复+forensics)→ 6059f837(九婴数值+⑧已知限制初记)→ a4dc91c4(加边界+接图+微调)→ f6f817da(去黑边)→ 18ba0bd6(⑧win 达成+driver 适配)。
|
||||
|
||||
**⑧win 胜路径达成,推翻 §6.7「已知限制交游戏设计」**。round-14 加边界后 bounded-cruise 竖直椭圆巡航摊薄怪潮 + 满级 build(level 52)贴脸集火,九婴进入武器 AoE 有效射程(sustained rate 95-210 hp/s,非无界时的 5-8),硬证「巡山礼成」结算 + 九婴伏诛 + bossKills=2 + 满血 win。无界 + 低 build 时武器索敌确受限,有界 + 满级 build 贴脸则可达。⑧胜/负/重玩/进度四路径齐全,⑧可由 partial 升 verified(待创始人最终验玩)。普通玩家手玩贴脸集火的难度归⑦c2/可玩性评估,不阻断⑧胜路径成立。注意:降九婴 hp 使⑦c2 反向(max 漂到 xuanming@100% FAIL),⑦收敛与⑧win 的平衡待调参协调。
|
||||
|
||||
至此山海行纪金标阶段二①②③④⑧基本就绪(④⑧待创始人眼验/验玩),剩⑤BGM 双 missing、⑥M5 内容(地图2-5/敌32/D2-D5)、⑦c2 收敛三项需开发,加创始人验玩签认。
|
||||
|
||||
### 6.9 2026-07-25 M5 代码灌装完成(占位资产,三检查点)
|
||||
|
||||
M5 代码灌装经三检查点完成,全回归绿:检查点1 balance 数据层(MAPS 5图=裂谷原+不周山/昆仑墟/归墟/钟山占位、ENEMIES 3→32、BOSSES 2→10、D2-D5 解锁占位、各图波表占位)→ 检查点2 sim.js createSim mapId 参数化(按图取 waves/enemyMix/bossIds/arena,map1 零变化硬证:MAPS[0].waves===WAVES 同一对象 + 默认/mapId:'map1'/mapId:0 三局 1500 帧逐字节一致 + 非法 mapId 兜底)+ _debugFill 注敌限定当前图敌混 + game-logic 出击页地图行驱动 MAPS → 检查点3 meta.js unlockedMaps 解锁持久化(存档 data 内,save-progress 仅包壳不含 data 字段故可安全加字段,schemaVersion 保持 1 向后兼容,旧档 normalize 默认 ['map1'])+ 胜局顺序解锁下一图 + meta-tables.js dex_full 阈值 gte:51→DEX_TOTAL 动态(修既有硬编码双写 bug)+ game-logic isMapUnlocked 接 meta。
|
||||
|
||||
回归:真实行为 13/13 + meta.test 11/11 + build-tables 9/9 + meta-tables 6/6 全过;map1 pacing 11.21h 不被破坏。ui-trigger2 test4「空槽虚位」为 pre-existing 渲染层失败(基线即存在,与 M5 无关,渲染线另修)。占位数值/占位 sprite 均标待美术/待平衡;map2-5 占位 pacing(headless 快仿真 lose@12-41s)标待平衡,终 Boss atS=100000 占位不可达;选图入局接线(startRun 传 mapId)待后续检查点。M5 完整验收仍需美术资产(图2-5 tileset/29兽 sprite/BGM6 首)+ 真平衡扫描,代码灌装本身已闭环。⑥维度:代码灌装完成,占位资产待美术,仍 partial。
|
||||
|
||||
@ -135,3 +135,7 @@ sot-impact: none(本档是金标 SoT 的消费记录视图,不替代 docs/ar
|
||||
round-14 加边界 + bounded-cruise 竖直椭圆巡航(摊薄怪潮)+ 满级 build(level 52)贴脸集火,⑧win 胜路径**达成**。硬证:07-win-jiuying.png「巡山礼成」结算(九婴伏诛、存活 20:17/20:00、击杀 5922、Lv52、Boss2)+ reachedWin.reached=win + bossKills=2(九婴+狰王)+ 九婴 hp 末段 3453→1921→0(rate 95→210 hp/s,贴脸 dist 145-243 进满级 AoE 甜区)+ latch.after=win pass=true。
|
||||
|
||||
上轮「⑧win 已知限制(武器索敌 nearestViaGrid 打怪不打 Boss)」结论**修正**:加边界摊薄怪潮 + 满级 build 贴脸时九婴进入武器 AoE 有效射程,win 可达(无界 + 低 build 时确受限,有界 + 满级 build 贴脸则可达)。注:00-win-result.json 的 strategyNote 自述「仍受武器索敌限制 sustained 5-8dps」系巡航段(九婴远处只吃溅射)观察与模板残留,与 KILL 段贴脸硬证(rate 95-210 + win 结算)矛盾,以硬证为准。win 依赖加边界 + 满级 build 贴脸(driver 自动化真玩);普通玩家手玩贴脸集火的难度归⑦c2/可玩性评估,不阻断⑧胜路径成立。⑧负(lose latch)/重玩(restart→play)/进度(推进 1200 九婴刷新)同轮重采有效。至此⑧胜/负/重玩/进度四路径齐全,⑧维度可由 partial 升 verified(待创始人最终验玩)。
|
||||
|
||||
## ⑥M5 代码灌装完成(2026-07-25,占位资产待美术)
|
||||
|
||||
M5 代码灌装三检查点完成:MAPS 5图 + ENEMIES 32 + BOSSES 10 + D2-D5 解锁 + sim mapId 参数化(map1 零变化硬证)+ meta 解锁持久化(存档向后兼容)+ 修 dex_full/_debugFill 扩表副作用。回归全绿(真实13/13+meta11/11+build-tables9/9+meta-tables6/6),map1 pacing 11.21h 不破。占位数值/占位 sprite 标待美术/待平衡,map2-5 占位 pacing 待平衡,选图入局接线待后续。⑥维度代码灌装完成,占位资产待美术 + 真平衡扫描,仍 partial。M5 完整验收需美术资产(图2-5/29兽sprite/BGM6)。ui-trigger2 test4 pre-existing 渲染层失败非 M5 引入。
|
||||
|
||||
@ -14,6 +14,13 @@
|
||||
* 【M3 增量】20 分钟正式波表(WAVES 重排,含 18:00 放量收割期)/ 潮汐脉冲(TIDE)/
|
||||
* 精英词缀(ELITE+AFFIXES 6)/ 图 1 双 Boss(BOSSES,战技组入表)/ 局内事件 4 种(EVENTS)/
|
||||
* XP 经济二次曲线(单局升级 22–28 次锚)/ 难度表增词缀密度轴(eliteMul/affixMax)。
|
||||
* 【M5 增量·检查点1=数据层灌表(占位)】MAPS 5 图(裂谷原/不周山/昆仑墟/归墟/钟山,各带波表引用/
|
||||
* 敌混/Boss 引用/解锁条件)/ 敌表扩至 32 兽(ENEMIES,山海经兽谱五图分布)/ Boss 扩至 10(5 图×2,
|
||||
* BOSSES)/ 难度 D2–D5 解锁条件占位 + 波表压缩轴占位(waveCompress)/ map2–5 占位波表(WAVES_MAP2..5)。
|
||||
* ⚠️ 本检查点只灌数据层:sim.js 仍硬编码图 1(读全局 WAVES + 全 BOSSES 表按 atS 出场),不读 MAPS/
|
||||
* 新图波表;新增兽/新图 Boss 均不进图 1 行为路径(新 Boss atS 占位超图 1 局时长 ROUND_S=1200、新兽
|
||||
* 不进图 1 波表 mix)→ 游戏行为零变化,回归安全。占位数值/占位 sprite 均注释标「待平衡/待美术」,
|
||||
* sim mapId 参数化 + meta 解锁接线 + 真平衡扫描留待后续检查点。
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@ -265,17 +272,30 @@ export const PASSIVES = [
|
||||
];
|
||||
|
||||
/**
|
||||
* 难度表 D0–D5(§5.2「数值曲线抬升+词缀密度+波表压缩」;本期启用 D0/D1,
|
||||
* D2–D5 为草案骨架〔波表压缩轴随 M5 落地,locked 表示出击页暂锁〕)。
|
||||
* 难度表 D0–D5(§5.2「数值曲线抬升+词缀密度+波表压缩」)。
|
||||
* M3 增词缀密度轴:eliteMul = 精英出现概率乘区;affixMax = 单个精英词缀叠加上限(§5.2「词缀可叠加」)。
|
||||
* M5 检查点1 增量(占位):
|
||||
* 【waveCompress 波表压缩轴·占位待平衡】§5.2「波表压缩」第三轴的表化——高难度把波表时间轴压缩
|
||||
* (同量怪在更短局内时长涌出,节奏更紧)。1.0 = 不压缩(基准 20 分钟局);越小节奏越压。
|
||||
* d0/d1 恒 1.0(本期启用档不压);d2–d5 占位递减值,真值待 M5 平衡线扫描。sim 当前不读本轴
|
||||
* (波表时间轴接线留待 sim mapId 参数化同期),故加此字段零行为变化。
|
||||
* 【unlock 解锁条件·占位待 meta 解锁逻辑】d2–d5 的 meta 解锁条件声明,结构对齐 ROLES.unlock
|
||||
* ({stat,gte}),供后续 meta 解锁接线复用 isUnlocked 模式;note 标明占位语义。locked 本检查点
|
||||
* 保持 true 不动——出击页可选难度门(game-logic.js:119/134/337/467 消费 locked)行为零变化,
|
||||
* 待 meta 解锁逻辑接线后按 unlock 条件动态置 locked=false。 pacing 锚 §5.3:D2–D3 = 第10h 攻坚,
|
||||
* D4/D5 = 超承诺长尾。
|
||||
*/
|
||||
export const DIFFICULTY = {
|
||||
d0: { name: '平潮', hpMul: 1.0, dpsMul: 1.0, rateMul: 1.0, speedMul: 1.0, eliteMul: 1.0, affixMax: 1, locked: false },
|
||||
d1: { name: '涌潮', hpMul: 1.15, dpsMul: 1.2, rateMul: 1.45, speedMul: 1.08, eliteMul: 1.3, affixMax: 2, locked: false },
|
||||
d2: { name: '怒潮', hpMul: 2.0, dpsMul: 1.9, rateMul: 1.7, speedMul: 1.18, eliteMul: 1.6, affixMax: 2, locked: true },
|
||||
d3: { name: '狂潮', hpMul: 2.8, dpsMul: 2.1, rateMul: 2.0, speedMul: 1.22, eliteMul: 2.0, affixMax: 3, locked: true },
|
||||
d4: { name: '噬潮', hpMul: 3.8, dpsMul: 2.4, rateMul: 2.5, speedMul: 1.26, eliteMul: 2.5, affixMax: 4, locked: true },
|
||||
d5: { name: '灭潮', hpMul: 5.0, dpsMul: 2.7, rateMul: 3.1, speedMul: 1.3, eliteMul: 3.0, affixMax: 5, locked: true },
|
||||
d0: { name: '平潮', hpMul: 1.0, dpsMul: 1.0, rateMul: 1.0, speedMul: 1.0, eliteMul: 1.0, affixMax: 1, waveCompress: 1.0, locked: false },
|
||||
d1: { name: '涌潮', hpMul: 1.15, dpsMul: 1.2, rateMul: 1.45, speedMul: 1.08, eliteMul: 1.3, affixMax: 2, waveCompress: 1.0, locked: false },
|
||||
d2: { name: '怒潮', hpMul: 2.0, dpsMul: 1.9, rateMul: 1.7, speedMul: 1.18, eliteMul: 1.6, affixMax: 2, waveCompress: 0.9, locked: true,
|
||||
unlock: { stat: 'mapWin', gte: 2, note: '占位待 meta 解锁逻辑(通关 2 图解怒潮)' } },
|
||||
d3: { name: '狂潮', hpMul: 2.8, dpsMul: 2.1, rateMul: 2.0, speedMul: 1.22, eliteMul: 2.0, affixMax: 3, waveCompress: 0.82, locked: true,
|
||||
unlock: { stat: 'mapWin', gte: 4, note: '占位待 meta 解锁逻辑(通关 4 图解狂潮)' } },
|
||||
d4: { name: '噬潮', hpMul: 3.8, dpsMul: 2.4, rateMul: 2.5, speedMul: 1.26, eliteMul: 2.5, affixMax: 4, waveCompress: 0.74, locked: true,
|
||||
unlock: { stat: 'diffClear', gte: 2, note: '占位待 meta 解锁逻辑(D2/D3 各通关若干解噬潮,长尾)' } },
|
||||
d5: { name: '灭潮', hpMul: 5.0, dpsMul: 2.7, rateMul: 3.1, speedMul: 1.3, eliteMul: 3.0, affixMax: 5, waveCompress: 0.66, locked: true,
|
||||
unlock: { stat: 'diffClear', gte: 4, note: '占位待 meta 解锁逻辑(D2–D4 深通解灭潮,超承诺长尾)' } },
|
||||
};
|
||||
|
||||
/**
|
||||
@ -293,13 +313,63 @@ export const XP = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 敌表(图 1 基础三兽;词缀精英/双 Boss 已随 M3 落地=AFFIXES/BOSSES,敌 32 种扩表归 M5)。
|
||||
* 敌表(M5 扩至 32 兽 = 图 1 基础三兽 + 29 兽占位;词缀精英/双 Boss 见 AFFIXES/BOSSES)。
|
||||
* dps = 接触时对玩家的每秒伤害;gem = 掉落灵晶价值(×波表 gemMul 经济乘区)。
|
||||
* 五图分布(§5.2 各图专属敌表;map 字段 = 该兽主属图,供 MAPS.enemyMix 与后续 sim 参数化引用):
|
||||
* map1 裂谷原:狰/梼杌/穷奇(现 3 兽,行为锁定不变);map2 不周山 6;map3 昆仑墟 6;
|
||||
* map4 归墟 6;map5 钟山 11。每个新兽都被某图 MAPS.enemyMix 引用,无孤儿条目。
|
||||
*
|
||||
* ── 占位纪律(M5 检查点1)──
|
||||
* 【数值占位·待平衡】新兽 hp/speed/dps/r/gem 均按「图序基础抬升 + 定位(快脆/中坚/坦克)」铺的
|
||||
* 草案锚,非真平衡值;真值待 M5 平衡线按 §8.1 单位 HP(w)=H0×g^w 曲线逐图扫描收敛。图 1 三兽
|
||||
* 数值保持 M3 原值不动(已随金标收敛,改动会破坏 sim 硬编码图 1 行为与既有测试)。
|
||||
* 【sprite 占位·待美术】现有 render 层按 type 查内置帧表,查不到自动 fallback 到 BEAST.zheng
|
||||
* (render.js:554)/程序色块 C.qiongqi(render.js:558),故新兽暂无专属帧也不崩渲染——即当前
|
||||
* sprite 占位策略 = 复用 beasts 图集帧循环(zheng 帧)+ 程序色兜底。各兽专属 6 帧(§9 异兽 32×6)
|
||||
* 待美术管线批产后补进 render BEAST 帧表;balance 层不内联 sprite 字段(渲染映射归 render 单点)。
|
||||
*/
|
||||
export const ENEMIES = {
|
||||
zheng: { name: '狰', hp: 22, speed: 88, r: 10, dps: 9, gem: 1 }, // 快而脆:压走位
|
||||
taowu: { name: '梼杌', hp: 50, speed: 60, r: 14, dps: 13, gem: 2 }, // 中坚
|
||||
qiongqi: { name: '穷奇', hp: 140, speed: 40, r: 20, dps: 18, gem: 4 }, // 慢坦克:压火力
|
||||
// ── map1 裂谷原(基础三兽,M3 原值锁定;sim 图 1 波表 mix 实际敌混,改动破坏现行为)──
|
||||
zheng: { name: '狰', hp: 22, speed: 88, r: 10, dps: 9, gem: 1, map: 'map1' }, // 快而脆:压走位
|
||||
taowu: { name: '梼杌', hp: 50, speed: 60, r: 14, dps: 13, gem: 2, map: 'map1' }, // 中坚
|
||||
qiongqi: { name: '穷奇', hp: 140, speed: 40, r: 20, dps: 18, gem: 4, map: 'map1' }, // 慢坦克:压火力
|
||||
|
||||
// ── map2 不周山(天柱折·焦土火山;基础血量 ≈图1×1.3。数值占位待平衡)──
|
||||
bifang: { name: '毕方', hp: 30, speed: 92, r: 10, dps: 11, gem: 1, map: 'map2' }, // 火鸟·快脆(占位)
|
||||
gudiao: { name: '蛊雕', hp: 65, speed: 58, r: 15, dps: 15, gem: 2, map: 'map2' }, // 雕身·中坚(占位)
|
||||
zhuyan: { name: '朱厌', hp: 45, speed: 80, r: 12, dps: 13, gem: 2, map: 'map2' }, // 兆兵·快中(占位)
|
||||
dangkang: { name: '当康', hp: 26, speed: 95, r: 9, dps: 8, gem: 1, map: 'map2' }, // 兆丰·脆(占位)
|
||||
mingshe: { name: '鸣蛇', hp: 55, speed: 66, r: 13, dps: 14, gem: 2, map: 'map2' }, // 蛇·中(占位)
|
||||
taotie: { name: '饕餮', hp: 180, speed: 38, r: 21, dps: 20, gem: 4, map: 'map2' }, // 贪食·坦克(占位)
|
||||
|
||||
// ── map3 昆仑墟(昆仑守卫;基础血量 ≈图1×1.7。数值占位待平衡)──
|
||||
yingzhao: { name: '英招', hp: 40, speed: 90, r: 11, dps: 14, gem: 2, map: 'map3' }, // 昆仑卫·快(占位)
|
||||
liwu: { name: '陆吾', hp: 85, speed: 55, r: 16, dps: 18, gem: 3, map: 'map3' }, // 昆仑神·中坚(占位)
|
||||
zhujian: { name: '诸犍', hp: 36, speed: 96, r: 10, dps: 13, gem: 2, map: 'map3' }, // 快(占位)
|
||||
zhuai: { name: '诸怀', hp: 70, speed: 62, r: 15, dps: 16, gem: 3, map: 'map3' }, // 中(占位)
|
||||
yayu: { name: '猰貐', hp: 240, speed: 36, r: 22, dps: 23, gem: 5, map: 'map3' }, // 食人·坦克(占位)
|
||||
xiezhi: { name: '獬豸', hp: 90, speed: 58, r: 16, dps: 17, gem: 3, map: 'map3' }, // 中坚(占位)
|
||||
|
||||
// ── map4 归墟(深渊之水;基础血量 ≈图1×2.2。数值占位待平衡)──
|
||||
huashe: { name: '化蛇', hp: 50, speed: 94, r: 11, dps: 16, gem: 2, map: 'map4' }, // 水蛇·快(占位)
|
||||
feiyi: { name: '肥遗', hp: 95, speed: 60, r: 16, dps: 20, gem: 3, map: 'map4' }, // 双头蛇·中(占位)
|
||||
luoyu: { name: '蠃鱼', hp: 40, speed: 88, r: 10, dps: 14, gem: 2, map: 'map4' }, // 鱼·脆(占位)
|
||||
fuzhu: { name: '夫诸', hp: 55, speed: 90, r: 12, dps: 17, gem: 2, map: 'map4' }, // 兆水·快(占位)
|
||||
changyou: { name: '长右', hp: 110, speed: 56, r: 17, dps: 21, gem: 3, map: 'map4' }, // 中(占位)
|
||||
xiangliu: { name: '相柳', hp: 320, speed: 34, r: 24, dps: 26, gem: 6, map: 'map4' }, // 九首·坦克(占位)
|
||||
|
||||
// ── map5 钟山(终图·混沌;基础血量 ≈图1×3。数值占位待平衡)──
|
||||
hundun: { name: '混沌', hp: 420, speed: 32, r: 26, dps: 30, gem: 7, map: 'map5' }, // 巨坦(占位)
|
||||
jiuwei: { name: '九尾', hp: 60, speed: 98, r: 11, dps: 18, gem: 2, map: 'map5' }, // 九尾狐·快脆(占位)
|
||||
tiangou: { name: '天狗', hp: 70, speed: 95, r: 12, dps: 19, gem: 3, map: 'map5' }, // 快(占位)
|
||||
bashe: { name: '巴蛇', hp: 500, speed: 30, r: 28, dps: 32, gem: 8, map: 'map5' }, // 吞象·巨坦(占位)
|
||||
fei: { name: '蜚', hp: 120, speed: 64, r: 17, dps: 23, gem: 3, map: 'map5' }, // 兆疫·中(占位)
|
||||
qitu: { name: '鵸鵌', hp: 55, speed: 92, r: 11, dps: 17, gem: 2, map: 'map5' }, // 鸟·快(占位)
|
||||
shusi: { name: '数斯', hp: 45, speed: 86, r: 10, dps: 15, gem: 2, map: 'map5' }, // 脆(占位)
|
||||
yong: { name: '颙', hp: 130, speed: 58, r: 18, dps: 24, gem: 4, map: 'map5' }, // 中(占位)
|
||||
lilu: { name: '狸力', hp: 65, speed: 90, r: 12, dps: 18, gem: 2, map: 'map5' }, // 快(占位)
|
||||
huahuai: { name: '猾褢', hp: 140, speed: 60, r: 18, dps: 25, gem: 4, map: 'map5' }, // 中(占位)
|
||||
baize: { name: '白泽', hp: 160, speed: 56, r: 19, dps: 26, gem: 5, map: 'map5' }, // 瑞兽·中坚(占位)
|
||||
};
|
||||
|
||||
/**
|
||||
@ -384,7 +454,7 @@ export const BOSSES = [
|
||||
{
|
||||
id: 'zhengwang', name: '狰王·赤鬣', atS: 600,
|
||||
hp: 2000, speed: 55, r: 30, dps: 38, gem: 60, gemPer: 4, // hp 3000→2000:⑧win 让普通人在 600–1200 窗斩杀,免与九婴分火力
|
||||
slowResist: 0.8, dropHarvest: true, final: false,
|
||||
slowResist: 0.8, dropHarvest: true, final: false, map: 'map1',
|
||||
skills: {
|
||||
charge: { cooldown: 9, telegraphS: 0.9, speedMul: 3.8, durS: 0.7 }, // speedMul 5.2→3.8:冲撞减速,普通人可侧移躲
|
||||
summon: { cooldown: 14, n: 6, type: 'zheng' },
|
||||
@ -393,13 +463,211 @@ export const BOSSES = [
|
||||
{
|
||||
id: 'jiuying', name: '九婴', atS: 1200,
|
||||
hp: 3600, speed: 48, r: 38, dps: 30, gem: 100, gemPer: 4, // hp 7400→3600(降击杀时间,107dps≈34s)/ dps 60→30(抬存活窗,贴身毙命 1.67s→3.3s)
|
||||
slowResist: 0.8, dropHarvest: false, final: true,
|
||||
slowResist: 0.8, dropHarvest: false, final: true, map: 'map1',
|
||||
skills: {
|
||||
charge: { cooldown: 12, telegraphS: 1.3, speedMul: 3.6, durS: 0.6 }, // 冲撞四件:cd 8→12(更疏)/预警 0.8→1.3(可读+侧闪)/速 5.5→3.6(264→173px/s 可躲)/程 0.75→0.6(横扫更窄)
|
||||
summon: { cooldown: 18, n: 5, type: 'taowu' },
|
||||
enrage: { hpPct: 0.3, speedMul: 1.2, dpsMul: 1.25 }, // 狂化削平:speedMul 1.35→1.2 / dpsMul 1.4→1.25
|
||||
},
|
||||
},
|
||||
/*
|
||||
* ── M5 检查点1:map2–5 各双 Boss 占位(共 8,5 图×2 = 10 含图1 双 Boss)──────────────────
|
||||
* 【atS 占位超界·保回归】新 Boss atS 全设 100000(≫ 图1 局时长 ROUND_S=1200),sim 硬编码图1
|
||||
* 按全 BOSSES 表 atS 出场(sim.js:637),故新 Boss 在图1 局永不 spawn → 行为零变化、测试不破。
|
||||
* map 字段标归属图;真 atS(中 Boss≈600 / 终 Boss≈1200)待 sim mapId 参数化后按图配。
|
||||
* 【final 语义】每图终 Boss 击杀即该图胜利(对齐图1 jiuying final:true):map2 天柱 / map3 陆吾 /
|
||||
* map4 共工 / map5 烛龙 为各图终 Boss(final:true);中 Boss final:false。新 Boss 不 spawn 图1,
|
||||
* final 标记不触发图1 判胜 → 安全。
|
||||
* 【数值/战技占位待平衡】hp/dps/charge/summon/enrage 均按图序抬升铺草案锚,非真值;真值待 M5
|
||||
* 平衡线参照图1 九婴「dps/存活窗不等式」(本表顶部 round-8 调参注)逐图校准。
|
||||
* 【sprite 占位待美术】render 按 bossDef.id 查 BOSS_SPRITE,查不到 fallback 到 'luwu'(render.js:535),
|
||||
* 故新 Boss 暂无专属帧也不崩渲染;各 Boss 专属 10 帧(§9 Boss 10×10)待美术批产补 render BOSS_SPRITE。
|
||||
* 【命名】id 前缀 boss_ 与敌表 key 区分(避免与敌表 xiangliu/hundun 等同名兽混淆);名取山海经公版
|
||||
* 大兽原创演绎,代理定名,最终命名待文案。
|
||||
*/
|
||||
// ── map2 不周山双 Boss(占位)──
|
||||
{
|
||||
id: 'boss_yanling', name: '炎灵·毕方', atS: 100000, map: 'map2',
|
||||
hp: 2600, speed: 58, r: 30, dps: 42, gem: 60, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: true, final: false,
|
||||
skills: {
|
||||
charge: { cooldown: 9, telegraphS: 0.9, speedMul: 4.0, durS: 0.7 },
|
||||
summon: { cooldown: 14, n: 6, type: 'bifang' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'boss_tianzhu', name: '天柱·不周守', atS: 100000, map: 'map2',
|
||||
hp: 4200, speed: 50, r: 38, dps: 34, gem: 100, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: false, final: true,
|
||||
skills: {
|
||||
charge: { cooldown: 12, telegraphS: 1.2, speedMul: 3.6, durS: 0.6 },
|
||||
summon: { cooldown: 18, n: 5, type: 'gudiao' },
|
||||
enrage: { hpPct: 0.3, speedMul: 1.2, dpsMul: 1.25 },
|
||||
},
|
||||
},
|
||||
// ── map3 昆仑墟双 Boss(占位)──
|
||||
{
|
||||
id: 'boss_yingzhao', name: '英招·昆仑卫', atS: 100000, map: 'map3',
|
||||
hp: 3200, speed: 60, r: 31, dps: 46, gem: 60, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: true, final: false,
|
||||
skills: {
|
||||
charge: { cooldown: 9, telegraphS: 0.9, speedMul: 4.2, durS: 0.7 },
|
||||
summon: { cooldown: 14, n: 6, type: 'yingzhao' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'boss_luwu', name: '陆吾·昆仑君', atS: 100000, map: 'map3',
|
||||
hp: 5000, speed: 52, r: 39, dps: 38, gem: 100, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: false, final: true,
|
||||
skills: {
|
||||
charge: { cooldown: 12, telegraphS: 1.2, speedMul: 3.6, durS: 0.6 },
|
||||
summon: { cooldown: 18, n: 5, type: 'liwu' },
|
||||
enrage: { hpPct: 0.3, speedMul: 1.22, dpsMul: 1.28 },
|
||||
},
|
||||
},
|
||||
// ── map4 归墟双 Boss(占位)──
|
||||
{
|
||||
id: 'boss_xiangliu', name: '相柳·九首蛇', atS: 100000, map: 'map4',
|
||||
hp: 4000, speed: 56, r: 33, dps: 50, gem: 60, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: true, final: false,
|
||||
skills: {
|
||||
charge: { cooldown: 9, telegraphS: 0.85, speedMul: 4.2, durS: 0.7 },
|
||||
summon: { cooldown: 14, n: 6, type: 'huashe' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'boss_gonggong', name: '共工·怒触山', atS: 100000, map: 'map4',
|
||||
hp: 6000, speed: 48, r: 42, dps: 42, gem: 100, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: false, final: true,
|
||||
skills: {
|
||||
charge: { cooldown: 12, telegraphS: 1.2, speedMul: 3.8, durS: 0.6 },
|
||||
summon: { cooldown: 18, n: 5, type: 'xiangliu' },
|
||||
enrage: { hpPct: 0.3, speedMul: 1.24, dpsMul: 1.3 },
|
||||
},
|
||||
},
|
||||
// ── map5 钟山双 Boss(占位;终图终 Boss = 烛龙)──
|
||||
{
|
||||
id: 'boss_hundun', name: '浑沌·无面', atS: 100000, map: 'map5',
|
||||
hp: 5200, speed: 54, r: 36, dps: 54, gem: 60, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: true, final: false,
|
||||
skills: {
|
||||
charge: { cooldown: 9, telegraphS: 0.85, speedMul: 4.4, durS: 0.7 },
|
||||
summon: { cooldown: 14, n: 6, type: 'hundun' },
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'boss_zhulong', name: '烛龙·钟山神', atS: 100000, map: 'map5',
|
||||
hp: 7500, speed: 46, r: 44, dps: 45, gem: 120, gemPer: 4,
|
||||
slowResist: 0.8, dropHarvest: false, final: true, // 终图终 Boss:击杀即全篇胜利(占位,数值待平衡)
|
||||
skills: {
|
||||
charge: { cooldown: 12, telegraphS: 1.2, speedMul: 3.8, durS: 0.6 },
|
||||
summon: { cooldown: 18, n: 5, type: 'jiuwei' },
|
||||
enrage: { hpPct: 0.3, speedMul: 1.26, dpsMul: 1.32 },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* ══════════════════ 地图表(5 图;M5 检查点1 数据层接入点声明)══════════════════
|
||||
* §5.2 地图 5,各带 1 个空间机制 / 专属敌表 / 双 Boss / 专属 BGM / 专属背景。本表是后续 sim mapId
|
||||
* 参数化的**单一接入点**:每图声明 id/name/主题/战场(arena)/波表(waves)/敌混(enemyMix)/
|
||||
* 双 Boss(bossIds)/背景(bg)/解锁条件(unlock)。
|
||||
*
|
||||
* ── 当前不接线(保回归)──
|
||||
* sim.js 本检查点仍硬编码图 1:波次读**全局 WAVES**(=MAPS[0].waves 同一对象)、Boss 按全 BOSSES
|
||||
* 表 atS 出场、敌按图 1 波表 mix 抽——均不经由本表。故 MAPS 是纯新增声明数据,sim/meta 都不读,
|
||||
* 游戏行为零变化。sim mapId 参数化后改读 MAPS[mapId].{waves,arena,bossIds,…}(留待后续检查点)。
|
||||
*
|
||||
* ── 占位纪律 ──
|
||||
* map1 裂谷原:映射现状(现主题/现波表/现敌混/现双 Boss/现背景),行为锁定不变。
|
||||
* map2–5:主题为代理定名(待美术/文案);波表 = 图1 波表缩放变体(wavesVariant,占位待平衡);
|
||||
* 敌混/Boss 引用本图 ENEMIES/BOSSES 条目;arena 暂复用图1 尺寸(各图专属战场待 M5 定);
|
||||
* bg 占位待美术(各图专属背景图集);unlock 为 meta 解锁条件占位(结构对齐 ROLES.unlock,待接线)。
|
||||
*/
|
||||
|
||||
/**
|
||||
* map2–5 占位波表变体工厂(占位待平衡):以图 1 波表 WAVES 为基线,按图序做到达率(rate×rateScale)/
|
||||
* 个体血量(hpMul×hpScale)缩放 + 敌混替换为该图敌种;时刻点(t)与经济/速度乘区沿用图1(占位)。
|
||||
* 纯占位——真波表待 M5 平衡线逐图扫描收敛;此处只为让 MAPS 各图有可读波表引用。sim 不读本表。
|
||||
*/
|
||||
const wavesVariant = (mix, rateScale, hpScale) => WAVES.map((row) => ({
|
||||
...row,
|
||||
rate: Math.round(row.rate * rateScale * 100) / 100,
|
||||
hpMul: Math.round(row.hpMul * hpScale * 1000) / 1000,
|
||||
mix,
|
||||
}));
|
||||
|
||||
/** map2 不周山波表(占位:图1 节奏 ×1.1 到达 / ×1.15 血量;敌混=不周山六兽)。 */
|
||||
export const WAVES_MAP2 = wavesVariant(
|
||||
{ bifang: 0.45, gudiao: 0.22, zhuyan: 0.13, dangkang: 0.08, mingshe: 0.08, taotie: 0.04 }, 1.1, 1.15);
|
||||
/** map3 昆仑墟波表(占位:×1.2 到达 / ×1.35 血量;敌混=昆仑墟六兽)。 */
|
||||
export const WAVES_MAP3 = wavesVariant(
|
||||
{ yingzhao: 0.4, liwu: 0.22, zhujian: 0.13, zhuai: 0.12, xiezhi: 0.08, yayu: 0.05 }, 1.2, 1.35);
|
||||
/** map4 归墟波表(占位:×1.3 到达 / ×1.6 血量;敌混=归墟六兽)。 */
|
||||
export const WAVES_MAP4 = wavesVariant(
|
||||
{ huashe: 0.38, fuzhu: 0.2, feiyi: 0.17, luoyu: 0.1, changyou: 0.1, xiangliu: 0.05 }, 1.3, 1.6);
|
||||
/** map5 钟山波表(占位:×1.45 到达 / ×2.0 血量;敌混=钟山十一兽,终图最高压)。 */
|
||||
export const WAVES_MAP5 = wavesVariant(
|
||||
{ jiuwei: 0.2, tiangou: 0.15, qitu: 0.12, lilu: 0.12, shusi: 0.1, fei: 0.1, yong: 0.07,
|
||||
huahuai: 0.06, baize: 0.04, hundun: 0.02, bashe: 0.02 }, 1.45, 2.0);
|
||||
|
||||
/** 5 图表(序 = 解锁序;map1 首图恒解锁,map2–5 依 meta 进度顺序解锁〔占位待 meta 接线〕)。 */
|
||||
export const MAPS = [
|
||||
{
|
||||
id: 'map1', name: '裂谷原',
|
||||
theme: '山海裂隙,百兽潮涌出处;赭石裂谷、岩画图腾(现图1,主题/行为锁定不变)',
|
||||
arena: ARENA, // 现图1 战场 1560×2960
|
||||
waves: WAVES, // 现图1 波表(sim 当前读全局 WAVES,与此同一对象)
|
||||
enemyMix: ['zheng', 'taowu', 'qiongqi'], // sim 图1 波表实际敌混(保持现行为;M5 扩混占位待平衡)
|
||||
bossIds: ['zhengwang', 'jiuying'], // 图1 双 Boss(中=狰王 / 终=九婴)
|
||||
bg: 'bg-map1-arena-01', // 现图1 背景(资产一期已入库)
|
||||
bgm: '占位待美术(map1 专属 BGM,M5 BGM6 之一)',
|
||||
unlock: { always: true }, // 首图恒解锁
|
||||
},
|
||||
{
|
||||
id: 'map2', name: '不周山',
|
||||
theme: '天柱折、地维绝;焦土火山,毕方衔火而行(占位主题,代理定名·待美术/文案)',
|
||||
arena: ARENA, // 占位:暂复用图1 战场尺寸(各图专属 arena 待 M5 定)
|
||||
waves: WAVES_MAP2, // 占位波表(图1 缩放变体,待平衡)
|
||||
enemyMix: ['bifang', 'gudiao', 'zhuyan', 'dangkang', 'mingshe', 'taotie'],
|
||||
bossIds: ['boss_yanling', 'boss_tianzhu'], // 中=炎灵·毕方 / 终=天柱·不周守
|
||||
bg: '占位待美术(map2 专属背景图集)',
|
||||
bgm: '占位待美术(map2 专属 BGM)',
|
||||
unlock: { stat: 'mapWin', gte: 1, note: '占位待 meta 解锁逻辑(通关图1解锁不周山)' },
|
||||
},
|
||||
{
|
||||
id: 'map3', name: '昆仑墟',
|
||||
theme: '昆仑之墟,神明所守;英招陆吾,巡卫天柱(占位主题,代理定名·待美术/文案)',
|
||||
arena: ARENA,
|
||||
waves: WAVES_MAP3,
|
||||
enemyMix: ['yingzhao', 'liwu', 'zhujian', 'zhuai', 'yayu', 'xiezhi'],
|
||||
bossIds: ['boss_yingzhao', 'boss_luwu'], // 中=英招·昆仑卫 / 终=陆吾·昆仑君
|
||||
bg: '占位待美术(map3 专属背景图集)',
|
||||
bgm: '占位待美术(map3 专属 BGM)',
|
||||
unlock: { stat: 'mapWin', gte: 2, note: '占位待 meta 解锁逻辑(通关图2解锁昆仑墟)' },
|
||||
},
|
||||
{
|
||||
id: 'map4', name: '归墟',
|
||||
theme: '归墟无底,众水所归;深渊化蛇,相柳盘波(占位主题,代理定名·待美术/文案)',
|
||||
arena: ARENA,
|
||||
waves: WAVES_MAP4,
|
||||
enemyMix: ['huashe', 'feiyi', 'luoyu', 'fuzhu', 'changyou', 'xiangliu'],
|
||||
bossIds: ['boss_xiangliu', 'boss_gonggong'], // 中=相柳·九首蛇 / 终=共工·怒触山
|
||||
bg: '占位待美术(map4 专属背景图集)',
|
||||
bgm: '占位待美术(map4 专属 BGM)',
|
||||
unlock: { stat: 'mapWin', gte: 3, note: '占位待 meta 解锁逻辑(通关图3解锁归墟)' },
|
||||
},
|
||||
{
|
||||
id: 'map5', name: '钟山',
|
||||
theme: '钟山之巅,烛龙司昼夜;混沌未凿,终局之所(占位主题,代理定名·待美术/文案;终图)',
|
||||
arena: ARENA,
|
||||
waves: WAVES_MAP5,
|
||||
enemyMix: ['hundun', 'jiuwei', 'tiangou', 'bashe', 'fei', 'qitu', 'shusi', 'yong', 'lilu', 'huahuai', 'baize'],
|
||||
bossIds: ['boss_hundun', 'boss_zhulong'], // 中=浑沌·无面 / 终=烛龙·钟山神(全篇终 Boss)
|
||||
bg: '占位待美术(map5 专属背景图集)',
|
||||
bgm: '占位待美术(map5 专属 BGM)',
|
||||
unlock: { stat: 'mapWin', gte: 4, note: '占位待 meta 解锁逻辑(通关图4解锁钟山·终图)' },
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
'use strict';
|
||||
|
||||
import { createSim } from './sim.js';
|
||||
import { JOYSTICK, ROUND_S, FEEDBACK, DIFFICULTY } from './balance.js';
|
||||
import { JOYSTICK, ROUND_S, FEEDBACK, DIFFICULTY, MAPS } from './balance.js';
|
||||
import { WEAPON_BY_ID } from './build-model.js';
|
||||
import { createMeta } from './meta.js';
|
||||
import { drawGame, layoutButton, layoutPauseButton, menuLayout } from './render.js';
|
||||
@ -63,6 +63,7 @@ export function createGame(opts) {
|
||||
let runRoleId = 'chixun'; // 本局出战角色(meta 增益来源)
|
||||
let runIsDaily = false; // 本局是否每日挑战
|
||||
let runDailyDate = ''; // 每日挑战日期串(dailyDone 去重键)
|
||||
let runMapId = 'map1'; // 本局巡域(检查点3:出击页尚不能选图入局,startRun 恒图1;选图接线后传真图 → 解锁 mapN+1)
|
||||
let recorded = false; // 本局是否已回写 meta(终态只回写一次)
|
||||
|
||||
/** 摇杆核(手 1–3;注入态与插件共核=driver 可驱真游戏,未注入直建同实现)。 */
|
||||
@ -128,8 +129,14 @@ export function createGame(opts) {
|
||||
rows.push({ type: 'char', roleId: r.id, name: r.unlocked ? `${r.name} · ${w ? w.name : ''}` : r.name, locked: !r.unlocked, selected: r.selected });
|
||||
}
|
||||
rows.push({ type: 'header', label: '巡域' });
|
||||
rows.push({ type: 'map', name: '图一 · 裂谷原', locked: false, selected: true });
|
||||
for (let i = 0; i < 4; i++) rows.push({ type: 'map', name: '未探明', locked: true });
|
||||
// M5 检查点2/3:地图行遍历 balance.MAPS 生成(名取真图名、解锁态由 isMapUnlocked 出;render 按 locked 绘 🔒)。
|
||||
// map1 首图恒解锁选中;map2–5 解锁态由 meta.isMapUnlocked 持久化驱动(检查点3 接线:通关 mapN 解锁 mapN+1;
|
||||
// 当前仅图1 可入局,故实际解锁 progression = 图1 胜 → 图2 解锁,选图入局接线后自动支持图N→图N+1)。
|
||||
// 行数恒 5(MAPS.length)= 出击页行几何不变(巡域 10..14 / 潮汛 15 / d0 16 / d1 17)。
|
||||
for (const m of MAPS) {
|
||||
const unlocked = isMapUnlocked(m);
|
||||
rows.push({ type: 'map', mapId: m.id, name: m.name, locked: !unlocked, selected: m.id === 'map1' });
|
||||
}
|
||||
rows.push({ type: 'header', label: '潮汛' });
|
||||
for (const key of Object.keys(DIFFICULTY)) {
|
||||
rows.push({ type: 'diff', key, name: `${key.toUpperCase()} · ${DIFFICULTY[key].name}`, locked: DIFFICULTY[key].locked });
|
||||
@ -138,6 +145,15 @@ export function createGame(opts) {
|
||||
}
|
||||
/** 重建出击页行(角色解锁/选中变化后刷新;行数恒 = roster 8 + 结构固定,menuList.count 不变)。 */
|
||||
function refreshMenuRows() { menuRows = buildMenuRows(); }
|
||||
/**
|
||||
* 地图解锁判定(M5 检查点3 接线):首图 map1(unlock.always)恒解锁;map2–5 由 meta 解锁持久化
|
||||
* 驱动——meta.isMapUnlocked 读 unlockedMaps 顺序态(通关 mapN 解锁 mapN+1)。本函数仅翻转各行
|
||||
* locked 布尔,行数恒 = MAPS.length(5),出击页行几何不变(巡域 10..14 / 潮汛 15 / d0 16 / d1 17)。
|
||||
*/
|
||||
function isMapUnlocked(m) {
|
||||
if (m.unlock && m.unlock.always) return true; // 首图恒解锁(兜底)
|
||||
return meta.isMapUnlocked(m.id); // 余图读 meta 解锁持久化态
|
||||
}
|
||||
/** 滚动列表(canvas-ui-kit v1.1;拖=滚 / 点=选)。 */
|
||||
const menuList = (plugins.uiKit && plugins.uiKit.makeScrollList ? plugins.uiKit.makeScrollList.bind(plugins.uiKit) : createScrollList)({
|
||||
rect: menuLayout(viewport).listRect,
|
||||
@ -160,7 +176,7 @@ export function createGame(opts) {
|
||||
|
||||
function startRun() {
|
||||
const metaBonus = meta.deriveMetaBonus(); // 选中角色 trait + 已习得天赋 → sim 增益包
|
||||
runRoleId = metaBonus.roleId; runIsDaily = false; runDailyDate = ''; recorded = false;
|
||||
runRoleId = metaBonus.roleId; runIsDaily = false; runDailyDate = ''; runMapId = 'map1'; recorded = false;
|
||||
sim = createSim({ seed: runSeed, difficulty: selDifficulty, meta: metaBonus });
|
||||
scene = 'run';
|
||||
paused = false;
|
||||
@ -173,7 +189,7 @@ export function createGame(opts) {
|
||||
function startDaily() {
|
||||
const cfg = meta.getDailyConfig();
|
||||
const metaBonus = meta.deriveMetaBonus(cfg.roleId); // 每日锁定角色(不吃出击页选择)
|
||||
runRoleId = cfg.roleId; runIsDaily = true; runDailyDate = cfg.date; recorded = false;
|
||||
runRoleId = cfg.roleId; runIsDaily = true; runDailyDate = cfg.date; runMapId = cfg.mapId || 'map1'; recorded = false;
|
||||
sim = createSim({ seed: cfg.seed, difficulty: cfg.difficulty, meta: metaBonus });
|
||||
scene = 'run';
|
||||
paused = false;
|
||||
@ -197,7 +213,7 @@ export function createGame(opts) {
|
||||
gems: s.stats.gems, durationS: s.timeS,
|
||||
weaponsSeen, passivesSeen, evolutionsSeen,
|
||||
beastsSeen: s.seenBeasts, bossesSeen: s.seenBosses,
|
||||
roleId: runRoleId, isDaily: runIsDaily, dailyDate: runDailyDate,
|
||||
roleId: runRoleId, isDaily: runIsDaily, dailyDate: runDailyDate, mapId: runMapId,
|
||||
});
|
||||
} catch (_) { /* meta 回写降级:内存态仍在,不连坐渲染 */ }
|
||||
refreshMenuRows(); // 角色可能新解锁 → 刷新出击页
|
||||
|
||||
@ -157,6 +157,13 @@ export const TALENTS = [
|
||||
{ id: 'zhong8', name: '还魂', desc: '符阵还魂,阵亡续命 +1 次', cost: 500, ring: 4, requires: ['zhong5', 'zhong6', 'zhong7'], effect: { axis: 'revive', value: 1 } },
|
||||
];
|
||||
|
||||
/**
|
||||
* 图鉴总条目数(收集投影上限;成就 dex_full 阈值与此同源,防双写漂移)。
|
||||
* buildDexCatalog 定义于下方「图鉴」节——函数声明整体提升,此处前向引用安全;
|
||||
* 提前落 const 是为让成就表(紧随其后)能在字面量内直接引用 DEX_TOTAL(const 有暂时性死区,须先用后声明)。
|
||||
*/
|
||||
export const DEX_TOTAL = buildDexCatalog().length;
|
||||
|
||||
/**
|
||||
* ══════════════════ 成就(60;§5.2 收集长尾)══════════════════
|
||||
* 每成就 = id / name / desc / cond{stat,gte}(累计战绩阈值,≥ 即解锁)。
|
||||
@ -221,7 +228,7 @@ export const ACHIEVEMENTS = [
|
||||
{ id: 'dex_10', name: '识微', desc: '图鉴收录 10', cond: { stat: 'dexSeen', gte: 10 } },
|
||||
{ id: 'dex_25', name: '博览', desc: '图鉴收录 25', cond: { stat: 'dexSeen', gte: 25 } },
|
||||
{ id: 'dex_40', name: '通志', desc: '图鉴收录 40', cond: { stat: 'dexSeen', gte: 40 } },
|
||||
{ id: 'dex_full', name: '山海全志', desc: '图鉴全数收录', cond: { stat: 'dexSeen', gte: 51 } },
|
||||
{ id: 'dex_full', name: '山海全志', desc: '图鉴全数收录', cond: { stat: 'dexSeen', gte: DEX_TOTAL } },
|
||||
// 难度(4)
|
||||
{ id: 'diff_d0', name: '平潮渡', desc: '通关平潮(D0)', cond: { stat: 'maxDiffBeatIdx', gte: 0 } },
|
||||
{ id: 'diff_d1', name: '涌潮渡', desc: '通关涌潮(D1)', cond: { stat: 'maxDiffBeatIdx', gte: 1 } },
|
||||
@ -260,8 +267,7 @@ export function buildDexCatalog() {
|
||||
return cat;
|
||||
}
|
||||
|
||||
/** 图鉴总条目数(收集投影上限;成就 dex_full 阈值与此同源,防双写)。 */
|
||||
export const DEX_TOTAL = buildDexCatalog().length;
|
||||
// 图鉴总条目数 DEX_TOTAL 已前移至成就表之前(const 暂时性死区:成就 dex_full 须在字面量内引用它,先用后声明)。
|
||||
|
||||
/**
|
||||
* ══════════════════ 每日挑战(§5.1 每日挑战;固定种子天下同题)══════════════════
|
||||
|
||||
@ -27,6 +27,9 @@ import {
|
||||
ROLES, TALENTS, ACHIEVEMENTS, SAVE_SCHEMA_VERSION,
|
||||
buildDexCatalog, DEX_TOTAL, dailyConfig, DIFF_ORDER,
|
||||
} from './meta-tables.js';
|
||||
// 巡域解锁序唯一来源:MAPS 表序 = 解锁序(map1 首图恒解锁,map2–5 顺序解锁;见 balance.js MAPS 注释)。
|
||||
// meta 只读其 id 序推「通关 mapN 解锁 mapN+1」,不改 balance 一字。
|
||||
import { MAPS } from './balance.js';
|
||||
|
||||
/** 存档业务键(save-progress 自动拼命名空间前缀 shanhai-xingji:;M5 切正式 save: 槽时对齐)。 */
|
||||
export const SAVE_KEY = 'meta';
|
||||
@ -38,6 +41,8 @@ const TALENT_BY_ID = {};
|
||||
for (const t of TALENTS) TALENT_BY_ID[t.id] = t;
|
||||
const DEX_CATALOG = buildDexCatalog();
|
||||
const DEX_IDS = new Set(DEX_CATALOG.map((d) => d.id));
|
||||
/** 巡域解锁序(MAPS 序 = 解锁序;「通关 mapN 解锁 mapN+1」的下一图据此推)。 */
|
||||
const MAP_ORDER = MAPS.map((m) => m.id);
|
||||
|
||||
/** 生涯统计默认(全 0;成就 cond.stat 的取值面)。 */
|
||||
function defaultStats() {
|
||||
@ -48,7 +53,7 @@ function defaultStats() {
|
||||
};
|
||||
}
|
||||
|
||||
/** 全新存档默认态(schemaVersion=1 的 data 正文)。chixun 恒解锁。 */
|
||||
/** 全新存档默认态(schemaVersion=1 的 data 正文)。chixun 恒解锁;巡域 map1 恒解锁。 */
|
||||
function defaultState() {
|
||||
return {
|
||||
gold: 0,
|
||||
@ -56,6 +61,7 @@ function defaultState() {
|
||||
selectedDifficulty: 'd0',
|
||||
talents: {},
|
||||
roster: { chixun: true },
|
||||
unlockedMaps: ['map1'], // 已解锁巡域(map1 首图恒解锁;胜局通关 mapN 顺序解锁 mapN+1)
|
||||
dex: {},
|
||||
achievements: {},
|
||||
daily: { lastDate: '', dailyDone: 0 },
|
||||
@ -133,6 +139,15 @@ export function createMeta(opts) {
|
||||
if (data.roster && typeof data.roster === 'object') {
|
||||
for (const id of Object.keys(data.roster)) if (ROLE_BY_ID[id] && data.roster[id]) s.roster[id] = true;
|
||||
}
|
||||
// 已解锁巡域:map1 恒在(兜底,即便坏档缺键);仅收合法图 id、去重、保序。
|
||||
// 旧 v1 档无此字段(加字段向后兼容,无需 schema 版本 +1)→ 默认 ['map1']。
|
||||
s.unlockedMaps = ['map1'];
|
||||
if (Array.isArray(data.unlockedMaps)) {
|
||||
const seen = new Set(['map1']);
|
||||
for (const id of data.unlockedMaps) {
|
||||
if (MAP_ORDER.includes(id) && !seen.has(id)) { seen.add(id); s.unlockedMaps.push(id); }
|
||||
}
|
||||
}
|
||||
if (data.dex && typeof data.dex === 'object') {
|
||||
for (const id of Object.keys(data.dex)) if (DEX_IDS.has(id) && data.dex[id]) s.dex[id] = true;
|
||||
}
|
||||
@ -210,6 +225,31 @@ export function createMeta(opts) {
|
||||
}
|
||||
function selectDifficulty(key) { state.selectedDifficulty = key; persist(); }
|
||||
|
||||
/* ── 巡域(地图)解锁:unlockedMaps 顺序态持久化 + 通关推进 + 查询 ── */
|
||||
|
||||
/**
|
||||
* 某图是否已解锁(map1 首图恒解锁兜底;余图按 unlockedMaps 顺序态)。
|
||||
* 出击页地图行 locked 态的唯一判据(game-logic.isMapUnlocked 经此驱动)。
|
||||
*/
|
||||
function isMapUnlocked(mapId) {
|
||||
if (mapId === 'map1') return true; // 首图恒解锁(即便存档异常亦保底)
|
||||
return state.unlockedMaps.includes(mapId);
|
||||
}
|
||||
/** 已解锁巡域 id 只读快照(出击页/诊断/门读)。 */
|
||||
function getUnlockedMaps() { return state.unlockedMaps.slice(); }
|
||||
/**
|
||||
* 通关 mapId → 顺序解锁下一图(mapN+1)。幂等:下一图已解锁/已是终图/未知图 → null。
|
||||
* @returns {string|null} 本次新解锁图 id(无则 null)
|
||||
*/
|
||||
function unlockNextMap(mapId) {
|
||||
const idx = MAP_ORDER.indexOf(mapId);
|
||||
if (idx < 0 || idx + 1 >= MAP_ORDER.length) return null; // 未知图或已是终图(map5)
|
||||
const nextId = MAP_ORDER[idx + 1];
|
||||
if (state.unlockedMaps.includes(nextId)) return null; // 已解锁(幂等,不重复)
|
||||
state.unlockedMaps.push(nextId);
|
||||
return nextId;
|
||||
}
|
||||
|
||||
/* ── 图鉴收集投影 ── */
|
||||
|
||||
/** 标记一批图鉴条目为已见(id 须在目录内;返回新增数)。 */
|
||||
@ -278,8 +318,8 @@ export function createMeta(opts) {
|
||||
* @param {object} summary 局末摘要:
|
||||
* { win, difficulty, kills, level, evolutions, harvests, eliteKills, bossKills, gems, durationS,
|
||||
* weaponsSeen[], passivesSeen[], evolutionsSeen[], beastsSeen[], bossesSeen[], roleId,
|
||||
* isDaily?, dailyDate? }
|
||||
* @returns {{ goldGained, newRoles[], newAchievements[], dexAdded }}
|
||||
* isDaily?, dailyDate?, mapId? } // mapId 缺省 'map1'(当前恒图1;选图入局接线后传真图)
|
||||
* @returns {{ goldGained, newRoles[], newAchievements[], dexAdded, newMaps[] }} // newMaps: 本局新解锁巡域
|
||||
*/
|
||||
function recordRun(summary) {
|
||||
const s = summary || {};
|
||||
@ -319,10 +359,15 @@ export function createMeta(opts) {
|
||||
state.daily.lastDate = s.dailyDate;
|
||||
state.daily.dailyDone += 1;
|
||||
}
|
||||
// 巡域解锁:胜局通关当前图(mapId 缺省 map1)→ 顺序解锁下一图(mapN→mapN+1)。
|
||||
// 当前出击页尚不能选图入局(startRun 恒图1),故实际只会由图1 胜局解锁图2;
|
||||
// 逻辑按 MAP_ORDER 通用,选图入局接线后自动支持图N→图N+1(占位先行,前向兼容)。
|
||||
let newMap = null;
|
||||
if (s.win) newMap = unlockNextMap(s.mapId || 'map1');
|
||||
// 角色解锁 + 成就评估(顺序:先解锁角色再评成就,rosterUnlocked 才最新)
|
||||
const unlocks = evaluateUnlocks();
|
||||
persist();
|
||||
return { goldGained, newRoles: unlocks.roles, newAchievements: unlocks.achievements, dexAdded };
|
||||
return { goldGained, newRoles: unlocks.roles, newAchievements: unlocks.achievements, dexAdded, newMaps: newMap ? [newMap] : [] };
|
||||
}
|
||||
|
||||
/* ── deriveMetaBonus:局外增益 → sim 注入包 ── */
|
||||
@ -421,6 +466,8 @@ export function createMeta(opts) {
|
||||
talentStatus, canBuyTalent, buyTalent, getTalentProjection,
|
||||
// 角色
|
||||
isRoleUnlocked, selectRole, selectDifficulty, getRosterProjection,
|
||||
// 巡域(地图解锁持久化)
|
||||
isMapUnlocked, getUnlockedMaps,
|
||||
// 图鉴
|
||||
markDex, getDexProjection,
|
||||
// 成就
|
||||
|
||||
@ -33,13 +33,20 @@ import {
|
||||
PLAYER, WEAPONS, PASSIVES, XP, ENEMIES, WAVES, ARENA,
|
||||
MAX_ALIVE, MAX_ZONES, SPAWN_R, GRID_CELL, SEPARATION,
|
||||
CRIT, SLOTS, OFFER, EVO_PASSIVE_MIN, CHAIN_R, HARVEST, DIFFICULTY,
|
||||
TIDE, ELITE, AFFIXES, BOSSES, EVENTS,
|
||||
TIDE, ELITE, AFFIXES, BOSSES, EVENTS, MAPS,
|
||||
} from './balance.js';
|
||||
|
||||
/** 词缀索引(id → 定义;spawn 热路径免重查)。 */
|
||||
const AFFIX_BY_ID = {};
|
||||
for (const a of AFFIXES) AFFIX_BY_ID[a.id] = a;
|
||||
|
||||
/** 地图索引(id → 地图定义;createSim mapId 参数化按 id 查图。M5 检查点2)。 */
|
||||
const MAP_BY_ID = {};
|
||||
for (const m of MAPS) MAP_BY_ID[m.id] = m;
|
||||
/** Boss 索引(id → Boss 定义;按图 bossIds 从全表组装本局出场序列时查表。M5 检查点2)。 */
|
||||
const BOSS_BY_ID = {};
|
||||
for (const b of BOSSES) BOSS_BY_ID[b.id] = b;
|
||||
|
||||
/** ease-in(二次):磁吸飞行「先慢后快」曲线(手 8)。 */
|
||||
function easeInQuad(p) { return p * p; }
|
||||
|
||||
@ -57,10 +64,13 @@ const ARENA_WALL_INSET = 84;
|
||||
|
||||
/**
|
||||
* 创建一局纯逻辑模拟。
|
||||
* @param {{seed?: number, fireEnabled?: boolean, difficulty?: string, meta?: object}} [opts]
|
||||
* @param {{seed?: number, fireEnabled?: boolean, difficulty?: string, mapId?: (string|number), meta?: object}} [opts]
|
||||
* - seed:受控随机种子(缺省固定值,保证「不传也确定」);
|
||||
* - fireEnabled:自动开火开关(缺省 true;headless「关火纯走位存活 ≥60s」对照局用 false);
|
||||
* - difficulty:难度键(d0..d5,缺省 d0;表驱动乘区,判据①②③④跑 d1);
|
||||
* - mapId:【M5 检查点2】地图 id('map1'..'map5';亦受数组下标 0..4;缺省/非法 = 'map1')。创建期定图,
|
||||
* 从 balance.MAPS 取该图 waves/arena/enemyMix/bossIds;map1 取到的就是原硬编码全局表同一组对象,
|
||||
* 故缺省局行为逐字节不变。只在选择时刻读表一次,不进每帧状态(确定性铁律)。
|
||||
* - meta:【M4】局外 meta 增益包(deriveMetaBonus 产;缺省 = 无 meta,逐值同 M3):
|
||||
* startWeaponId(角色初始符箓)/ startPassives(角色 trait 起手被动级,进 plv 算持有)/
|
||||
* hpMul·hpAdd(最大生命)/ reviveCount(阵亡续命次数)/ bonus{...}(纯数值折叠,见 build-model)。
|
||||
@ -76,6 +86,26 @@ export function createSim(opts) {
|
||||
/* ── M4 meta 增益(缺省 no-op;无 meta 时后续 derive/HP/续命全部退化为 M3 原路径)── */
|
||||
const meta = (opts && opts.meta) || null;
|
||||
|
||||
/* ── M5 检查点2:mapId 参数化(创建期定图;缺省 map1 = 现硬编码同一组表,行为逐字节不变)──
|
||||
mapId 只在创建时读表一次,下面产出的 WAVES/ARENA/enemyMix/BOSSES 是本局常量,被各嵌套函数闭包消费;
|
||||
不进每帧状态、不参与每帧分支 → 确定性铁律不破(同 mapId+seed+difficulty+meta → 同局)。 */
|
||||
let mapId = 'map1';
|
||||
if (opts && opts.mapId != null) {
|
||||
if (MAP_BY_ID[opts.mapId]) mapId = opts.mapId; // 字符串 id('map1'…)
|
||||
else if (Number.isInteger(opts.mapId) && MAPS[opts.mapId]) mapId = MAPS[opts.mapId].id; // 数组下标(0…)
|
||||
}
|
||||
const mapDef = MAP_BY_ID[mapId];
|
||||
/* 本局波表/战场/敌混(局部常量,闭包遮蔽同名全局导入):map1 直接是 balance 全局 WAVES/ARENA 同一对象
|
||||
(MAPS[0].waves===WAVES、MAPS[0].arena===ARENA、enemyMix=['zheng','taowu','qiongqi']),故 map1 局逐字节
|
||||
等于原硬编码图1;currentWave/spawnEnemy/竞技场 clamp 全部经由这些局部常量,自然按图取数。 */
|
||||
const WAVES = mapDef.waves;
|
||||
const ARENA = mapDef.arena;
|
||||
const enemyMix = mapDef.enemyMix;
|
||||
/* 本局 Boss 出场序列:按图 bossIds 从全 BOSSES 表查组(序 = bossIds 序)。map1 bossIds=[zhengwang,jiuying]
|
||||
→ 与原全局 BOSSES 前两项同一组定义、同一 atS(600/1200),出场与判胜行为不变;新图 Boss atS 占位 100000
|
||||
(≫ ROUND_S=1200)即便误入某图序列也永不 spawn。bossSpawned 与本序列同长,update 出场循环只遍历本图 Boss。 */
|
||||
const BOSSES = mapDef.bossIds.map((id) => BOSS_BY_ID[id]);
|
||||
|
||||
/* ── 局面状态 ── */
|
||||
let t = 0; // 局内时间(秒)
|
||||
let phase = 'play'; // play | levelup | win | lose(levelup=全局冻结,手 9)
|
||||
@ -1160,7 +1190,11 @@ export function createSim(opts) {
|
||||
const ng = (counts && counts.gems) || 0;
|
||||
if (counts && counts.boss) bossBar = { ...counts.boss };
|
||||
if (counts && counts.drop) drops.push({ x: player.x + counts.drop.dx, y: player.y + counts.drop.dy, id: ++eidSeq, ttl: HARVEST.ttlS });
|
||||
const types = Object.keys(ENEMIES);
|
||||
// M5 检查点2:注敌限定本图敌混(map1 = 基线三兽 zheng/taowu/qiongqi),不再按 Object.keys(ENEMIES) 全表轮转。
|
||||
// 修 build-tables「五族武器全部真出伤」:ENEMIES 扩至 32 兽后全表轮转破了该测试写作时的基线三兽脆皮
|
||||
// 轮转确定性(seed 506 下 orbit 族 shanzi 出伤条件不再成立);回到本图敌混即恢复基线。测试辅助路径,
|
||||
// 不影响真实波表(正式出怪走 spawnEnemy → currentWave().mix)。
|
||||
const types = enemyMix;
|
||||
for (let i = 0; i < ne; i++) {
|
||||
const def = ENEMIES[types[i % types.length]];
|
||||
const ang = rng() * Math.PI * 2;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user