fixed # 补全部分卡牌

fixed # 修复部分已知BUG
This commit is contained in:
2026-10-04 19:32:38 +08:00
parent c448da39c2
commit 94a2a6724b
163 changed files with 14008 additions and 1537 deletions
+22 -18
View File
@@ -10,42 +10,46 @@ export function getBondDeck(definition) {
[...DEFAULT_BOND_DECK] : deck;
}
const emptyGrowth = () => Array.from({ length: 10 }, () => ({
maxHp: null,
hand: null,
draw: null,
handLimit: null,
energy: null,
skills: null,
// 当前关羽/张飞的可用基础配置;等级先只影响到场概率,数值曲线与新技能后续单独配置。
const baseGrowth = () => Array.from({ length: 10 }, () => ({
maxHp: 40,
hand: 4,
draw: 2,
handLimit: 5,
energy: 3,
skills: [],
}));
export const bondDefinitions = {
guanyu: {
name: "关羽", character: "guanyu", portrait: null,
deck: null, intents: null, growth: emptyGrowth(),
name: "关羽", character: "mengsan_guanyu_shuying",
portrait: "extension/术樱包/mengsan/assets/portraits/guanyu-portrait.jpg",
deck: null, intents: null, growth: baseGrowth(),
},
zhangfei: {
name: "张飞", character: "zhangfei", portrait: null,
deck: null, intents: null, growth: emptyGrowth(),
name: "张飞", character: "mengsan_zhangfei_shuying",
portrait: "extension/术樱包/mengsan/assets/portraits/zhangfei-portrait.jpg",
deck: null, intents: null, growth: baseGrowth(),
},
};
export function getBondCombat(id, level) {
const definition = bondDefinitions[id];
const definition = Object.hasOwn(bondDefinitions, id) ? bondDefinitions[id] : null;
if (!definition || !Number.isInteger(level) || level < 1 || level > 10) {
return null;
}
const stats = definition.growth[level - 1];
const unlocked = Array.isArray(definition.growth) ? Array.from(definition.growth.slice(0, level)) : [];
const stats = unlocked[level - 1];
if (!stats || !["maxHp", "hand", "draw", "handLimit", "energy"]
.every(key => Number.isInteger(stats[key]) && stats[key] >= 0)
.every(key => Number.isInteger(stats[key]) && stats[key] >= 0 && stats[key] <= 99)
|| stats.maxHp < 1 || stats.energy < 1
|| !Array.isArray(stats.skills)
|| !stats.skills.every(skill => typeof skill === "string" && skill)) {
|| unlocked.some(growth => !growth || !Array.isArray(growth.skills) ||
!growth.skills.every(skill => typeof skill === "string" && skill))) {
return null;
}
const skills = new Set();
for (const growth of definition.growth.slice(0, level)) {
for (const skill of growth.skills || []) skills.add(skill);
for (const growth of unlocked) {
for (const skill of growth.skills) skills.add(skill);
}
return {
id: `bond_${id}`, bondId: id, character: definition.character,
+38 -11
View File
@@ -1,9 +1,26 @@
import { bondDefinitions, getBondCombat } from "./definitions.js";
export const bondProbability = level => (level - 1) / 9;
export const bondProbability = level => {
if (!Number.isInteger(level) || level < 1 || level > 10) throw new RangeError("羁绊等级必须为1至10");
return (level - 1) / 9;
};
export function bondBattleStatus(battle) {
if (battle?.arrived) return "已到场";
return ({opening: "角色专属首战不触发助战", down: "濒死,尚未救助",
unconfigured: "本等级战斗配置未补齐", chance: "本场未通过助战概率判定",
ready: "本场助战就绪"})[battle?.reason] || "本场未到场";
}
export function ensureBonds(run) {
return run.player.bonds ||= { selected: null, npcs: {} };
const bonds = run.player.bonds ||= { selected: null, npcs: {} };
// 只设置一次默认值;以后主动取消助战或改选张飞均不会被覆盖。
if (run.player.character === "mengsan_liubei_shuying" && bonds.npcs.guanyu &&
!bonds.innateSelectionInitialized) {
if (bonds.selected == null) bonds.selected = "guanyu";
bonds.innateSelectionInitialized = true;
}
return bonds;
}
export function meetBond(run, id, level = 1) {
@@ -20,12 +37,13 @@ export function initializeInnateBonds(run) {
if (run.player.character === "mengsan_liubei_shuying") {
meetBond(run, "guanyu", 8);
meetBond(run, "zhangfei", 8);
ensureBonds(run);
}
}
export function selectBond(run, id) {
const bonds = ensureBonds(run);
if (id !== null && !Object.hasOwn(bonds.npcs, id)) {
if (id !== null && (!Object.hasOwn(bondDefinitions, id) || !Object.hasOwn(bonds.npcs, id))) {
throw new Error("尚未结识该 NPC");
}
bonds.selected = id;
@@ -48,17 +66,22 @@ const isCharacterOpeningBattle = (run, node) =>
run.actIndex === 0 && node?.floor === 0;
export function prepareBondBattle(run, random, node = null) {
if (isCharacterOpeningBattle(run, node)) {
run.bondBattle = null;
return null;
}
const bonds = ensureBonds(run);
const id = bonds.selected;
const npc = bonds.npcs[id];
run.bondBattle = npc ? { id, arrived: false } : null;
if (!npc || npc.down) return null;
const npc = Object.hasOwn(bondDefinitions, id) && Object.hasOwn(bonds.npcs, id) ? bonds.npcs[id] : null;
run.bondBattle = npc ? { id, arrived: false, reason: "ready" } : null;
if (isCharacterOpeningBattle(run, node)) {
if (run.bondBattle) run.bondBattle.reason = "opening";
return null;
}
if (!npc) return null;
if (npc.down) { run.bondBattle.reason = "down"; return null; }
const combat = getBondCombat(id, npc.level);
if (!combat || random() >= bondProbability(npc.level)) return null;
if (!combat) { run.bondBattle.reason = "unconfigured"; return null; }
if (typeof random !== "function") throw new TypeError("羁绊助战缺少随机数接口");
const roll = random();
if (!Number.isFinite(roll) || roll < 0 || roll >= 1) throw new RangeError("羁绊助战随机数必须在[0,1)内");
if (roll >= bondProbability(npc.level)) { run.bondBattle.reason = "chance"; return null; }
return combat;
}
@@ -71,6 +94,10 @@ export function settleBondBattle(run, node = null) {
if (!battle) return null;
const npc = ensureBonds(run).npcs[battle.id];
if (!npc) throw new Error("助战羁绊记录丢失");
if (!Number.isInteger(npc.level) || npc.level < 1 || npc.level > 10 ||
!Number.isInteger(npc.progress) || npc.progress < 0 || npc.progress >= 100) {
throw new Error("羁绊等级或成长进度无效,拒绝结算");
}
const gain = battle.arrived ? 50 : 25;
if (npc.level < 10) {
npc.progress += gain;