Files
noname-shuying/mengsan/cards/rampage.js
T
Diuse 9e1732d2f8 fixed # 补全部分卡牌
fixed # 修复部分已知BUG
2026-10-04 19:32:38 +08:00

105 lines
5.1 KiB
JavaScript

// 累积仅属于真实牌对象和当前会话;不写Card.storage或永久牌组。
const NAME = "mengsan_baozou";
const growth = new WeakMap();
const validEntry = (card, entry) => card?.name === NAME && entry?.session.active &&
entry.battle.session === entry.session;
export function snapshotRampage(card) {
const entry = growth.get(card);
return validEntry(card, entry) ? {
mengsanRampageBonus_shuying: entry.bonus,
mengsanRampageSession_shuying: entry.session.id,
} : {};
}
export function copyRampageGrowth(source,target,battle) {
if(source?.cards?.length===1)source=source.cards[0];
const from=growth.get(source);
if(!validEntry(source,from) || from.battle!==battle || target?.name!==NAME)return;
const entry={battle,session:battle.session,bonus:from.bonus};
growth.set(target,entry);
battle.session.ownResource(entry,()=>{if(growth.get(target)===entry)growth.delete(target);});
}
function nearestUse(event) {
const seen = new Set();
for (let current = event; current && !seen.has(current); current = current.parent) {
seen.add(current);
if (current.name === "useCard") return current;
}
return null;
}
function original(event) {
const use = nearestUse(event);
if(use?.card?.name===NAME && use.mengsanIcRepeatOriginal_shuying?.name===NAME)return use.mengsanIcRepeatOriginal_shuying;
return use?.card?.name === NAME && use.cards?.length === 1 &&
use.cards[0]?.name === NAME ? use.cards[0] : null;
}
function cardOf(card) {
return card?.name === NAME && card.cards?.length === 1 && card.cards[0]?.name === NAME
? card.cards[0] : card;
}
function belongsTo(event, root) {
const seen = new Set();
for (let current = event; current && !seen.has(current); current = current.parent) {
if (current === root) return true;
seen.add(current);
}
return false;
}
export function createRampage(game, getActiveBattle) {
const used = new WeakSet();
const getPile = (player, battle) => player === game.me
? battle.personalPiles : battle.monsterPiles?.get(player);
function damage(card) {
const node = cardOf(card), battle = getActiveBattle(), entry = growth.get(node);
let bonus = validEntry(node, entry) && entry.battle === battle ? entry.bonus : 0;
// Only plain, current-session UI snapshots may expose serialized growth.
if (!node?.storage && battle?.session.active &&
node?.mengsanRampageSession_shuying === battle.session.id &&
Number.isSafeInteger(node.mengsanRampageBonus_shuying) && node.mengsanRampageBonus_shuying >= 0)
bonus = node.mengsanRampageBonus_shuying;
if (bonus > Number.MAX_SAFE_INTEGER - 9) throw new RangeError("暴走伤害越界");
return 9 + bonus;
}
return {
card: cardOf, original, damage,
async apply(player, event, baseDamage, increase) {
if (baseDamage !== 9 || !Number.isSafeInteger(increase) || increase <= 0)
throw new RangeError("暴走攻击参数无效");
const use = nearestUse(event), card = original(event);
const battle = getActiveBattle(), session = battle?.session, root = battle?.root;
if (!battle || !session?.active || !root || !card || use.player !== player ||
event.card?.name !== NAME || used.has(use) || !belongsTo(use, root)) return;
const pile = getPile(player, battle), target = event.target;
const valid = () => getActiveBattle() === battle && battle.session === session &&
session.active && battle.root === root && battle.players?.has(player) &&
player.isAlive() && card.name === NAME && getPile(player, battle) === pile &&
typeof pile?.battleCards === "function" && pile.battleCards(use.cards).includes(card);
if (!valid() || !battle.players.has(target) || !target?.isAlive?.() ||
target === player || !player.isEnemyOf(target)) return;
const amount = damage(card), bonus = amount - baseDamage;
if (increase > Number.MAX_SAFE_INTEGER - baseDamage - bonus)
throw new RangeError("暴走累积伤害越界");
used.add(use);
player.line(target, "green");
const hit = target.damage(amount, player);
hit.mengsanAttack_shuying = true;
await hit;
if (!valid()) return;
let entry = growth.get(card);
if (!validEntry(card, entry) || entry.battle !== battle) {
entry = { battle, session, bonus: 0 };
const record = entry;
session.ownResource(record, () => {
if (growth.get(card) === record) growth.delete(card);
});
growth.set(card, entry);
}
// Damage hooks may initiate nested uses; retain their increments without overwrite.
if (increase > Number.MAX_SAFE_INTEGER - baseDamage - entry.bonus)
throw new RangeError("暴走累积伤害越界");
entry.bonus += increase;
battle.handUI?.refresh();
battle.pilesUI?.refresh();
},
};
}