mirror of
https://github.com/Shu-Ying/noname-shuying.git
synced 2026-10-21 23:00:06 +08:00
fixed # 新增羁绊系统
fixed # 新增遗物系统 fixed # 梦三模式下移除护甲上限限制
This commit is contained in:
@@ -87,7 +87,7 @@ export function grantSupport(run, rewardId, spec) {
|
||||
supports.push({ id: `support_${number}`, rewardId, category: spec.category || "item", unit: copy(spec.unit), battles });
|
||||
}
|
||||
|
||||
export function buildBattlePlan(encounter, run) {
|
||||
export function buildBattlePlan(encounter, run, bondUnit = null) {
|
||||
const plan = copy(encounter.battlePlan || {
|
||||
units: [{ id: "enemy_1", character: encounter.enemy, camp: "enemy", tier: encounter.tier || "normal", hand: 4 }], rules: [],
|
||||
});
|
||||
@@ -98,6 +98,7 @@ export function buildBattlePlan(encounter, run) {
|
||||
assert(support.battles === -1 || (Number.isInteger(support.battles) && support.battles > 0), "支援持续场数无效");
|
||||
plan.units.push({ ...copy(support.unit), id: support.id });
|
||||
}
|
||||
if (bondUnit) plan.units.unshift(copy(bondUnit));
|
||||
validateBattlePlan(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ export function createBattleFlow({settlement, chooseReward, showMap, showEnding,
|
||||
if (job.pending.state !== "chosen") {
|
||||
state = "choosing";
|
||||
if (!job.hasChoice) {
|
||||
job.choiceId = job.pending.outcome === "defeat" || !job.pending.choices.length ? null : await chooseReward(copy(job.pending.choices), { boss: job.pending.boss });
|
||||
job.choiceId = job.pending.outcome === "defeat" ? null
|
||||
: await chooseReward(copy(job.pending.choices), {
|
||||
boss: job.pending.boss,
|
||||
fixedRewards: copy(job.pending.fixedRewards || []),
|
||||
});
|
||||
if (job.pending.outcome !== "defeat" && job.pending.choices.length && job.choiceId !== null && !job.pending.choices.some(c => c.id === job.choiceId)) throw new Error("Invalid reward selection");
|
||||
job.hasChoice = true;
|
||||
}
|
||||
@@ -37,7 +41,11 @@ export function createBattleFlow({settlement, chooseReward, showMap, showEnding,
|
||||
await quiesce();
|
||||
if (!job.delivered) {
|
||||
state = "presenting";
|
||||
if (job.committed.route === "map") job.nextNode = await showMap(copy(job.committed.run));
|
||||
if (job.committed.route === "map") {
|
||||
const mapRun = copy(job.committed.run);
|
||||
job.nextNode = await showMap(mapRun);
|
||||
job.committed.run = mapRun;
|
||||
}
|
||||
else await showEnding(job.committed.route, copy(job.committed.run));
|
||||
job.delivered = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// Candidate settlement adapter. Dependencies are the current mode/state reward functions.
|
||||
import { applyBattleEndRelics } from "../relics/battle.js";
|
||||
import { settleBondBattle } from "../bonds/state.js";
|
||||
|
||||
const copy = value => JSON.parse(JSON.stringify(value));
|
||||
const idFor = (run, node) => JSON.stringify([run.runId, run.actIndex, node.id]);
|
||||
export function createBattleSettlement({store, config, getRandomRewardChoices, applyReward, completeNode, enterNextAct, now = Date.now}) {
|
||||
@@ -34,16 +37,19 @@ export function createBattleSettlement({store, config, getRandomRewardChoices, a
|
||||
}
|
||||
// Battle may have changed RNG/card state since the last checkpoint.
|
||||
const base = copy(snapshot); delete base.battleFlow;
|
||||
const bondGrowth = settleBondBattle(base);
|
||||
if (outcome === "victory") {
|
||||
base.player.hp = Math.max(1, Math.min(hp, base.player.maxHp ?? hp));
|
||||
base.statistics.defeatedEnemies += encounter.defeatedEnemies ?? 1;
|
||||
}
|
||||
const relicRecovery = hp > 0
|
||||
? applyBattleEndRelics(base, outcome) : 0;
|
||||
const rewards = outcome === "victory" && !encounter.skipRandomReward ? getRandomRewardChoices(base, encounter.rewardPool).filter(c => c.name) : [];
|
||||
if (outcome === "victory" && !encounter.skipRandomReward && !rewards.length) throw new Error("Empty battle reward pool");
|
||||
// Persist the configured amount as a candidate, never as automatic victory income.
|
||||
const choices = outcome === "victory" && !encounter.skipRandomReward
|
||||
? [{id:"mengsan.reward.gold.shuying", kind:"gold", name:"金币", amount:encounter.gold}, ...rewards] : [];
|
||||
run.battleFlow.pending = {id, state:"awaitingChoice", nodeId:node.id, base, choices:copy(choices), fixedRewards:copy(encounter.fixedRewards || []), victoryDialogue:copy(encounter.victoryDialogue || []), boss:!!encounter.boss, outcome};
|
||||
run.battleFlow.pending = {id, state:"awaitingChoice", nodeId:node.id, base, choices:copy(choices), fixedRewards:copy(encounter.fixedRewards || []), victoryDialogue:copy(encounter.victoryDialogue || []), boss:!!encounter.boss, outcome, relicRecovery, bondGrowth};
|
||||
return run.battleFlow.pending;
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import { _status } from "../../../../noname.js";
|
||||
export const PLAYER_ENERGY = 3;
|
||||
export const PLAYER_HAND_LIMIT = 3;
|
||||
export const SHA_DAMAGE = 4;
|
||||
export const SHA_DAMAGE = 6;
|
||||
export const TRICK_DAMAGE = 4;
|
||||
|
||||
const COSTS = Object.freeze({ sha: 1, tao: 1, jiu: 1, shan: 0, wuxie: 0,
|
||||
const COSTS = Object.freeze({ sha: 1, mengsan_zhongsha: 2,
|
||||
tao: 1, jiu: 1, shan: 0, wuxie: 0,
|
||||
juedou: 2, nanman: 2, wanjian: 2, taoyuan: 2 });
|
||||
|
||||
export const cardCost = card => COSTS[card?.name] ?? 1;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { attackHitCount, outgoingAttackDamage } from "./intent-damage.js";
|
||||
import { beginDeathBlow, isDeathBlowIntent } from "./death-blow.js";
|
||||
|
||||
export const getIntentHostiles = (game, source) =>
|
||||
game.players.filter(target => target !== source && target.isAlive() &&
|
||||
target.storage?.mengsanCamp_shuying !==
|
||||
source.storage?.mengsanCamp_shuying);
|
||||
|
||||
export function createIntentExecutor(game, getActiveBattle) {
|
||||
const active = source => source.isAlive() &&
|
||||
Boolean(getActiveBattle()?.session.active);
|
||||
const applyDebuff = (target, move) => {
|
||||
if (!move.debuff) return;
|
||||
const frail = move.debuff === "脆弱";
|
||||
const key = frail ? "mengsanFrail_shuying" :
|
||||
"mengsanVulnerable_shuying";
|
||||
target.storage[key] = (target.storage[key] || 0) + move.stacks;
|
||||
target.addSkill(frail ? "mengsan_frail_shuying" :
|
||||
"mengsan_vulnerable_shuying");
|
||||
target.markSkill(frail ? "mengsan_frail_shuying" :
|
||||
"mengsan_vulnerable_shuying");
|
||||
};
|
||||
return async (source, move) => {
|
||||
for (const target of getIntentHostiles(game, source)) {
|
||||
if (!active(source)) break;
|
||||
if (!target.isAlive()) continue;
|
||||
if (isDeathBlowIntent(move)) {
|
||||
await beginDeathBlow(source, target, move);
|
||||
continue;
|
||||
}
|
||||
if (move.damage != null) {
|
||||
for (let hitIndex = 0; hitIndex < attackHitCount(move); hitIndex++) {
|
||||
if (!target.isAlive() || !active(source)) break;
|
||||
const hit = target.damage(
|
||||
outgoingAttackDamage(move, source), source);
|
||||
hit.mengsanAttack_shuying = true;
|
||||
hit.mengsanScriptedSkill_shuying = true;
|
||||
await hit;
|
||||
}
|
||||
}
|
||||
if (target.isAlive() && active(source)) applyDebuff(target, move);
|
||||
}
|
||||
if (!active(source)) return;
|
||||
if (move.block) await source.changeHujia(move.block);
|
||||
if (!active(source)) return;
|
||||
if (move.strength) {
|
||||
source.storage.mengsanStrength_shuying =
|
||||
(source.storage.mengsanStrength_shuying || 0) + move.strength;
|
||||
source.addSkill("mengsan_raider_strength_shuying");
|
||||
source.markSkill("mengsan_raider_strength_shuying");
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user