mirror of
https://github.com/Shu-Ying/noname-shuying.git
synced 2026-10-18 05:10:00 +08:00
fixed # 新增羁绊系统
fixed # 新增遗物系统 fixed # 梦三模式下移除护甲上限限制
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import { createCardData } from "../cards/card-data.js";
|
||||
import { bondDefinitions } from "./definitions.js";
|
||||
import { ensureBonds, markBondDown } from "./state.js";
|
||||
import { getBondIntents } from "./intents.js";
|
||||
|
||||
export function initializeBondUnit(player, spec, run) {
|
||||
if (!spec.bondId) return null;
|
||||
const portrait = bondDefinitions[spec.bondId].portrait;
|
||||
if (player.node?.avatar) {
|
||||
player.node.avatar.style.backgroundImage = portrait ?
|
||||
`url("${portrait}")` : "none";
|
||||
}
|
||||
player.storage.mengsanBond_shuying = {
|
||||
id: spec.bondId, draw: spec.draw, handLimit: spec.handLimit,
|
||||
intents: getBondIntents(spec).map(move => ({ ...move })),
|
||||
turnsTaken: 0, intent: null,
|
||||
};
|
||||
player.storage.mengsanMaxEnergy_shuying = spec.energy;
|
||||
player.storage.mengsanEnergy_shuying = spec.energy;
|
||||
if (run.bondBattle?.id === spec.bondId) run.bondBattle.arrived = true;
|
||||
return {
|
||||
createDeck(prefix) {
|
||||
return spec.deck.map((entry, index) => {
|
||||
const card = typeof entry === "string" ? { name: entry } : entry;
|
||||
return createCardData({
|
||||
...card, id: `${prefix}_${index}`,
|
||||
suit: card.suit ||
|
||||
["spade", "heart", "club", "diamond"][index % 4],
|
||||
number: card.number || index % 13 + 1,
|
||||
}, { enemy: true });
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function recordBondDeath(player, run, log) {
|
||||
const id = player.storage?.mengsanBond_shuying?.id;
|
||||
if (!id || !run) return;
|
||||
if (ensureBonds(run).npcs[id]?.down) return;
|
||||
markBondDown(run, id);
|
||||
log(`${bondDefinitions[id].name}已退出战斗,羁绊状态:濒死`);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { getBondIntents } from "./intents.js";
|
||||
|
||||
export const DEFAULT_BOND_DECK = Object.freeze([
|
||||
"sha", "sha", "sha", "shan", "shan", "tao", "wuzhong",
|
||||
]);
|
||||
|
||||
export function getBondDeck(definition) {
|
||||
const deck = definition.deck;
|
||||
return deck == null || Array.isArray(deck) && !deck.length ?
|
||||
[...DEFAULT_BOND_DECK] : deck;
|
||||
}
|
||||
|
||||
const emptyGrowth = () => Array.from({ length: 10 }, () => ({
|
||||
maxHp: null,
|
||||
hand: null,
|
||||
draw: null,
|
||||
handLimit: null,
|
||||
energy: null,
|
||||
skills: null,
|
||||
}));
|
||||
|
||||
export const bondDefinitions = {
|
||||
guanyu: {
|
||||
name: "关羽", character: "guanyu", portrait: null,
|
||||
deck: null, intents: null, growth: emptyGrowth(),
|
||||
},
|
||||
zhangfei: {
|
||||
name: "张飞", character: "zhangfei", portrait: null,
|
||||
deck: null, intents: null, growth: emptyGrowth(),
|
||||
},
|
||||
};
|
||||
|
||||
export function getBondCombat(id, level) {
|
||||
const definition = bondDefinitions[id];
|
||||
if (!definition || !Number.isInteger(level) || level < 1 || level > 10) {
|
||||
return null;
|
||||
}
|
||||
const stats = definition.growth[level - 1];
|
||||
if (!stats || !["maxHp", "hand", "draw", "handLimit", "energy"]
|
||||
.every(key => Number.isInteger(stats[key]) && stats[key] >= 0)
|
||||
|| stats.maxHp < 1 || stats.energy < 1
|
||||
|| !Array.isArray(stats.skills)
|
||||
|| !stats.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);
|
||||
}
|
||||
return {
|
||||
id: `bond_${id}`, bondId: id, character: definition.character,
|
||||
camp: "ally", after: "player", inheritSkills: false,
|
||||
hp: stats.maxHp, maxHp: stats.maxHp, hand: stats.hand,
|
||||
draw: stats.draw, handLimit: stats.handLimit, energy: stats.energy,
|
||||
skills: [...skills],
|
||||
deck: getBondDeck(definition),
|
||||
intents: getBondIntents(definition).map(move => ({ ...move })),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { createIntentExecutor } from "../battle/intent-effects.js";
|
||||
import { isStunned } from "../battle/stun-intent.js";
|
||||
|
||||
export const DEFAULT_BOND_INTENTS = Object.freeze([
|
||||
Object.freeze({ id: "assist_strike", name: "援击", damage: 6 }),
|
||||
Object.freeze({ id: "assist_combo", name: "连击", damage: 3, hits: 2 }),
|
||||
Object.freeze({ id: "assist_guard", name: "整备", block: 5 }),
|
||||
]);
|
||||
|
||||
export function getBondIntents(definition) {
|
||||
const moves = definition.intents;
|
||||
if (moves == null || Array.isArray(moves) && !moves.length) {
|
||||
return DEFAULT_BOND_INTENTS;
|
||||
}
|
||||
if (!Array.isArray(moves) || moves.some(move =>
|
||||
!move || typeof move.id !== "string" || !move.id ||
|
||||
typeof move.name !== "string" || !move.name ||
|
||||
!["damage", "block", "strength"].every(key => move[key] == null ||
|
||||
Number.isInteger(move[key]) && move[key] >= 0) ||
|
||||
move.hits != null && (!Number.isInteger(move.hits) || move.hits < 1) ||
|
||||
move.debuff != null && (!["脆弱", "易伤"].includes(move.debuff) ||
|
||||
!Number.isInteger(move.stacks) || move.stacks < 1) ||
|
||||
move.intentType != null && move.intentType !== "death_blow" ||
|
||||
move.intentType === "death_blow" &&
|
||||
(move.damage == null || (move.hits ?? 1) !== 1) ||
|
||||
move.damage == null && !move.block && !move.strength && !move.debuff)) {
|
||||
throw new Error("羁绊 NPC 意图组配置无效");
|
||||
}
|
||||
return moves;
|
||||
}
|
||||
|
||||
export function selectBondIntent(combat) {
|
||||
return combat.intents[combat.turnsTaken % combat.intents.length];
|
||||
}
|
||||
|
||||
export function createBondIntentActions(game, getActiveBattle) {
|
||||
const executeEffects = createIntentExecutor(game, getActiveBattle);
|
||||
const planBondIntent = player => {
|
||||
const combat = player.storage?.mengsanBond_shuying;
|
||||
if (!combat || !player.isAlive() || isStunned(player) || combat.intent) {
|
||||
return;
|
||||
}
|
||||
combat.intent = selectBondIntent(combat);
|
||||
game.mengsanSetEnemyIntent_shuying(player, combat.intent);
|
||||
};
|
||||
const executeBondIntent = async player => {
|
||||
const combat = player.storage?.mengsanBond_shuying;
|
||||
if (!combat?.intent || !player.isAlive() || isStunned(player) ||
|
||||
!getActiveBattle()?.session.active) return;
|
||||
const move = combat.intent;
|
||||
combat.intent = null;
|
||||
combat.turnsTaken++;
|
||||
game.mengsanSetEnemyIntent_shuying(player, null);
|
||||
if (player.storage.mengsanEnergy_shuying < 1) {
|
||||
game.log(player, "费用不足,未发动羁绊意图", move.name);
|
||||
return;
|
||||
}
|
||||
player.storage.mengsanEnergy_shuying--;
|
||||
getActiveBattle()?.energyUI.get(player)?.();
|
||||
game.log(player, "消耗1费用发动羁绊意图", move.name);
|
||||
await executeEffects(player, move);
|
||||
};
|
||||
return { planBondIntent, executeBondIntent };
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { bondDefinitions, getBondCombat } from "./definitions.js";
|
||||
|
||||
export const bondProbability = level => (level - 1) / 9;
|
||||
|
||||
export function ensureBonds(run) {
|
||||
return run.player.bonds ||= { selected: null, npcs: {} };
|
||||
}
|
||||
|
||||
export function meetBond(run, id, level = 1) {
|
||||
if (!Object.hasOwn(bondDefinitions, id) || !Number.isInteger(level)
|
||||
|| level < 1 || level > 10) throw new Error("羁绊 NPC 或等级无效");
|
||||
const bonds = ensureBonds(run);
|
||||
if (bonds.npcs[id]) return false;
|
||||
bonds.npcs[id] = { level, progress: 0, down: false };
|
||||
return true;
|
||||
}
|
||||
|
||||
export function initializeInnateBonds(run) {
|
||||
ensureBonds(run);
|
||||
if (run.player.character === "mengsan_liubei_shuying") {
|
||||
meetBond(run, "guanyu", 8);
|
||||
meetBond(run, "zhangfei", 8);
|
||||
}
|
||||
}
|
||||
|
||||
export function selectBond(run, id) {
|
||||
const bonds = ensureBonds(run);
|
||||
if (id !== null && !Object.hasOwn(bonds.npcs, id)) {
|
||||
throw new Error("尚未结识该 NPC");
|
||||
}
|
||||
bonds.selected = id;
|
||||
}
|
||||
|
||||
export function rescueBond(run, id) {
|
||||
const npc = ensureBonds(run).npcs[id];
|
||||
if (!npc?.down) return false;
|
||||
npc.down = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function markBondDown(run, id) {
|
||||
const npc = ensureBonds(run).npcs[id];
|
||||
if (npc) npc.down = true;
|
||||
}
|
||||
|
||||
export function prepareBondBattle(run, random) {
|
||||
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 combat = getBondCombat(id, npc.level);
|
||||
if (!combat || random() >= bondProbability(npc.level)) return null;
|
||||
return combat;
|
||||
}
|
||||
|
||||
export function settleBondBattle(run) {
|
||||
const battle = run.bondBattle;
|
||||
if (!battle) return null;
|
||||
const npc = ensureBonds(run).npcs[battle.id];
|
||||
if (!npc) throw new Error("助战羁绊记录丢失");
|
||||
const gain = battle.arrived ? 50 : 25;
|
||||
if (npc.level < 10) {
|
||||
npc.progress += gain;
|
||||
while (npc.progress >= 100 && npc.level < 10) {
|
||||
npc.progress -= 100;
|
||||
npc.level++;
|
||||
}
|
||||
}
|
||||
if (npc.level === 10) npc.progress = 0;
|
||||
run.bondBattle = null;
|
||||
return { id: battle.id, gain, level: npc.level, progress: npc.progress };
|
||||
}
|
||||
Reference in New Issue
Block a user