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

95 lines
6.6 KiB
JavaScript

import { consumeArtifact } from "./artifact-status.js";
import { isActiveCardUse } from "../battle/combat-rules.js";
import { skipStunnedAction } from "../battle/stun-intent.js";
export const BEAST_CHARACTER = "mengsan_ceremonial_beast_shuying";
export const BEAST_MOVES = Object.freeze({
stamp: Object.freeze({ id: "stamp", name: "跺地", plow: 150 }),
plow: Object.freeze({ id: "plow", name: "横冲直撞", damage: 18, strength: 2 }),
cry: Object.freeze({ id: "cry", name: "野兽咆哮", ringing: true }),
stomp: Object.freeze({ id: "stomp", name: "踩踏", damage: 15 }),
crush: Object.freeze({ id: "crush", name: "碾碎", damage: 17, strength: 3 }),
});
export const isBeast = player => player?.name === BEAST_CHARACTER && player.storage?.mengsanCamp_shuying === "enemy";
function checked(state) {
const s = { phase: 1, step: 0, turnsTaken: 0, revision: 0, ...state };
if (![1, 2].includes(s.phase) || !Number.isSafeInteger(s.step) || s.step < 0 || s.step > (s.phase === 1 ? 1 : 2) ||
!Number.isSafeInteger(s.turnsTaken) || s.turnsTaken < 0 || !Number.isSafeInteger(s.revision) || s.revision < 0) throw new RangeError("仪式兽行动状态无效");
return s;
}
export function selectBeastMove(state = {}) {
const s = checked(state);
return s.phase === 1 ? (s.step === 0 ? BEAST_MOVES.stamp : BEAST_MOVES.plow) : [BEAST_MOVES.cry, BEAST_MOVES.stomp, BEAST_MOVES.crush][s.step];
}
export function recordBeastAction(state = {}, advance = true) {
const s = checked(state);
if (typeof advance !== "boolean" || advance && s.turnsTaken === Number.MAX_SAFE_INTEGER) throw new RangeError("仪式兽行动推进无效");
return { ...s, step: advance ? (s.phase === 1 ? 1 : (s.step + 1) % 3) : s.step, turnsTaken: s.turnsTaken + (advance ? 1 : 0) };
}
export function recoverBeastStun(player, choice, original) {
const s = checked(player.storage.mengsanBeastState_shuying);
if (s.justBroken) { player.storage.mengsanBeastState_shuying = { ...s, justBroken: false }; player.storage.mengsanBeastIntent_shuying = null; return null; }
player.storage.mengsanBeastState_shuying = recordBeastAction(s, choice !== "retry");
const next = choice === "retry" ? original : null;
player.storage.mengsanBeastIntent_shuying = next; return next;
}
const bindings = new WeakMap();
export function initializeBeast(player, current) {
if (!isBeast(player)) return false;
if (current?.session.active && bindings.get(player)?.battle !== current) {
const entry = { battle: current };
current.session.ownResource(entry, () => {
if (bindings.get(player) !== entry) return;
bindings.delete(player); delete player.storage.mengsanBeastState_shuying; delete player.storage.mengsanBeastIntent_shuying;
player.removeSkill("mengsan_plow_shuying"); player.removeSkill("mengsan_raider_strength_shuying");
}); bindings.set(player, entry);
}
player.storage.mengsanBeastState_shuying = checked({}); player.storage.mengsanBeastIntent_shuying = null;
player.removeSkill("mengsan_plow_shuying"); player.removeSkill("mengsan_raider_strength_shuying"); return true;
}
export function gainPlow(player) {
if (!isBeast(player) || !player.isAlive()) return false;
const max = player.maxHp;
if (!Number.isSafeInteger(max) || max <= 0 || !Number.isSafeInteger(max * 150)) throw new RangeError("仪式兽血量缩放无效");
player.storage.mengsanPlow_shuying = Math.max(1, Math.round(150 * max / 252));
player.addSkill("mengsan_plow_shuying"); player.markSkill("mengsan_plow_shuying"); return true;
}
export const canBreakPlow = (player, current, damage) => Boolean(current?.session.active && isBeast(player) && player.isAlive() &&
damage?.name === "damage" && damage.player === player && damage.num > 0 && player.storage.mengsanPlow_shuying > 0 &&
player.hp <= player.storage.mengsanPlow_shuying && player.storage.mengsanBeastState_shuying?.phase === 1);
export function breakPlow(game, current, player, damage) {
if (!canBreakPlow(player, current, damage)) return false;
const s = checked(player.storage.mengsanBeastState_shuying);
if (s.revision === Number.MAX_SAFE_INTEGER) throw new RangeError("仪式兽阶段版本溢出");
player.removeSkill("mengsan_plow_shuying"); player.removeSkill("mengsan_raider_strength_shuying");
player.storage.mengsanStrength_shuying = 0;
player.storage.mengsanBeastState_shuying = { ...s, phase: 2, step: 0, revision: s.revision + 1, justBroken: true };
player.storage.mengsanBeastIntent_shuying = null;
game.mengsanStunEnemy_shuying(player, { resume: "retry" });
const phaseUse = damage.getParent?.("phaseUse");
if (phaseUse?.player === player) skipStunnedAction(player, phaseUse);
return true;
}
const ringing = new WeakMap();
export function clearRinging(player) {
ringing.delete(player); delete player.storage.mengsanRinging_shuying; delete player.storage.mengsanRingingUsed_shuying;
}
export function applyRinging(current, player, phasePlayer = null) {
if (!current?.session.active || !player?.isAlive() || !current.players?.has(player)) return false;
if (consumeArtifact(player)) return false;
if (ringing.get(player)?.battle === current && player.storage.mengsanRinging_shuying) return true;
const entry = { battle: current, seen: new WeakSet() };
current.session.ownResource(entry, () => { if (ringing.get(player) !== entry) return; player.removeSkill("mengsan_ringing_shuying"); clearRinging(player); });
ringing.set(player, entry); player.storage.mengsanRinging_shuying = 1;
// Mid-turn applications use already played cards; ordinary Beast Cry arrives before the target's next turn.
player.storage.mengsanRingingUsed_shuying = phasePlayer === player ? (player.getHistory?.("useCard", e => !e.cancelled && !e._cancelled && isActiveCardUse(e, player)).length || 0) : 0;
player.addSkill("mengsan_ringing_shuying"); player.markSkill("mengsan_ringing_shuying"); current.handUI?.refresh(); return true;
}
export const ringingLimited = (player, event) => Boolean(player.storage?.mengsanRinging_shuying &&
player.storage.mengsanRingingUsed_shuying >= 1 && isActiveCardUse(event, player));
export function countRingingCard(player, current, event) {
const entry = ringing.get(player);
if (!current?.session.active || entry?.battle !== current || !player.isAlive() || !player.storage.mengsanRinging_shuying ||
event?.name !== "useCard" || !event.card || event.cancelled || event._cancelled || !isActiveCardUse(event, player) || entry.seen.has(event)) return false;
entry.seen.add(event); player.storage.mengsanRingingUsed_shuying = 1; player.markSkill("mengsan_ringing_shuying"); current.handUI?.refresh(); return true;
}