feat # 继续补全遗物系统

This commit is contained in:
2026-10-08 23:46:57 +08:00
parent 623f53a1d2
commit 1c93c77f4f
335 changed files with 11528 additions and 1797 deletions
+54 -20
View File
@@ -1,22 +1,56 @@
import { heldRelics } from "./definitions.js";
import { heldRelics, relicDefinitions, canAcquireRelic, grantRelic } from "./definitions.js";
import { relicState, healRun, changeMaxHp, grantGold, upgradeCards } from "./progression.js";
import { relicRandomGet } from "./random.js";
export const restRecovery = run => 8 + heldRelics(run)
.filter(relic => relic.effect === "restHeal")
.reduce((sum, relic) => sum + relic.amount, 0);
export function applyRoomRelics(run, node, action) {
const resting = node.type === "rest" && action === "heal";
if (node.type !== "shop" && !resting) return null;
const receipt = `${node.type}:${node.id}`;
const receipts = run.player.relicRoomReceipts || [];
if (receipts.includes(receipt)) return null;
const effect = resting ? "restHeal" : "shopHeal";
const relics = heldRelics(run).filter(relic => relic.effect === effect);
const amount = (resting ? 8 : 0) + relics.reduce((sum, relic) =>
sum + relic.amount, 0);
if (!amount) return null;
const before = run.player.hp;
run.player.hp = Math.min(run.player.maxHp, before + amount);
run.player.relicRoomReceipts = [...receipts, receipt];
return { recovered: run.player.hp - before, relics };
export const restRecovery = run => 8+heldRelics(run).reduce((n,r)=>n+(r.rule.restHeal || 0),0);
export const relicShopPrice = (run,base) => Math.max(1,Math.floor(heldRelics(run).reduce((n,r)=>n*(r.rule.shopDiscount || 1),base)));
export function recordRelicShopPurchase(run){relicState(run).mawBankBroken=true;}
export function relicRestChoices(run) {
const state=relicState(run),choices=[];
for(const relic of heldRelics(run)){
const action=relic.rule.restAction;
if(action==="dig")choices.push({id:"dig",name:"挖掘",description:"获得一件可实现的随机普通、罕见或稀有遗物"});
if(action==="lift")choices.push({id:"lift",name:"举重",description:`以后每场战斗获得额外力量(${state.giryaLevels || 0}/3)`,disabled:(state.giryaLevels || 0)>=3});
if(action==="rekindle")choices.push({id:"rekindle",name:"添火",description:"让南瓜蜡烛再次燃烧5场战斗",disabled:(state.candleRemaining || 0)>=5});
if(action==="cook")choices.push({id:"cook",name:"烹饪",description:"最大生命+5;梦三适配值"});
}
return choices;
}
export function applyRoomRelics(run,node,action) {
const receipt=`${run.actIndex}:${node.type}:${node.id}:${action}`,state=relicState(run);
const receipts=run.player.relicRoomReceipts ||= [];
if(receipts.includes(receipt))return null;
const relics=heldRelics(run),before=run.player.hp;
if(action==="enter"){
if(node.type==="shop")for(const relic of relics)healRun(run,relic.rule.shopHeal || 0);
if(node.type==="rest"){
state.afterRest=true;
for(const relic of relics)healRun(run,Math.floor(run.player.deck.length/5)*(relic.rule.restEntryHealPerFive || 0));
}
if(node.type==="event")for(const relic of relics)healRun(run,relic.rule.eventHeal || 0);
} else if(node.type==="rest"){
if(action==="heal"){
healRun(run,restRecovery(run));
for(const relic of relics){const r=relic.rule;if(r.restMaxHp)changeMaxHp(run,r.restMaxHp);if(r.restCardChoice)(state.choices ||= []).push({relic:relic.name,kind:"cardReward",count:1,offerCount:3});}
} else if(action==="upgrade")upgradeCards(run,1);
else if(action==="lift" && relics.some(r=>r.rule.restAction==="lift") && (state.giryaLevels || 0)<3)state.giryaLevels=(state.giryaLevels || 0)+1;
else if(action==="rekindle" && relics.some(r=>r.rule.restAction==="rekindle"))state.candleRemaining=5;
else if(action==="cook" && relics.some(r=>r.rule.restAction==="cook"))changeMaxHp(run,5);
else if(action==="dig" && relics.some(r=>r.rule.restAction==="dig")){
const candidates=Object.values(relicDefinitions).filter(r=>["普通","罕见","稀有"].includes(r.tier) && canAcquireRelic(run,r.id));
const relic=relicRandomGet(run,candidates);if(relic)grantRelic(run,relic.id);
} else return null;
} else return null;
run.player.relicRoomReceipts.push(receipt); return {recovered:run.player.hp-before,relics};
}
// Arrival precedes purchases and combat; retries on the same in-memory node
// cannot pay again. Battle rollback retains the existing checkpoint semantics.
export function applyRelicNodeEnter(run,node) {
if(!node || node.completed)return;
const state=relicState(run);
const key=`${run.actIndex}:${node.id}`;
const receipts=state.nodeReceipts ||= [];
if(receipts.includes(key))return;
receipts.push(key);
for(const relic of heldRelics(run))if(relic.rule.nodeGold && !state.mawBankBroken)grantGold(run,relic.rule.nodeGold);
}