Release v0.1.6 2026-07-12
This commit is contained in:
@@ -1,25 +1,39 @@
|
||||
import { RUN_BUFFS, bossHealthMultiplier, effectiveRunBuffRank, formatRunBuffEffect } from "../game/roguelike";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ROGUE_TRIALS_TRIO_ROUND, RUN_BUFFS, bossHealthMultiplier, effectiveRunBuffRank, formatRunBuffEffect } from "../game/roguelike";
|
||||
import { HEALER_CLASSES } from "../game/healers";
|
||||
import { useGameStore } from "../game/store";
|
||||
import { isRunBuffInputLocked, useGameStore } from "../game/store";
|
||||
|
||||
export function BuffDraftPanel({ className = "" }: { className?: string }) {
|
||||
const round = useGameStore((state) => state.round);
|
||||
const runMode = useGameStore((state) => state.runMode);
|
||||
const healerClassId = useGameStore((state) => state.healerClassId);
|
||||
const runBuffRanks = useGameStore((state) => state.runBuffRanks);
|
||||
const passiveRunBuffId = useGameStore((state) => state.passiveRunBuffId);
|
||||
const choices = useGameStore((state) => state.draftBuffIds);
|
||||
const selected = useGameStore((state) => state.selectedRunBuffId);
|
||||
const inputUnlockAt = useGameStore((state) => state.runBuffInputUnlockAt);
|
||||
const setSelected = useGameStore((state) => state.setSelectedRunBuff);
|
||||
const choose = useGameStore((state) => state.chooseRunBuff);
|
||||
const continueRun = useGameStore((state) => state.continueRoguelikeRound);
|
||||
const nextRound = round + 1;
|
||||
const nextBossCount = runMode === "rogue-trials" && nextRound === ROGUE_TRIALS_TRIO_ROUND ? 3 : 2;
|
||||
const abilities = HEALER_CLASSES[healerClassId].abilities;
|
||||
const [inputLocked, setInputLocked] = useState(() => isRunBuffInputLocked(useGameStore.getState()));
|
||||
|
||||
useEffect(() => {
|
||||
const remaining = inputUnlockAt - Date.now();
|
||||
setInputLocked(remaining > 0);
|
||||
if (remaining <= 0) return;
|
||||
const timer = window.setTimeout(() => setInputLocked(false), remaining);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [inputUnlockAt]);
|
||||
|
||||
return (
|
||||
<div className={`buff-draft ${className}`.trim()} role="dialog" aria-modal="true" aria-label={`Choose a buff for round ${nextRound}`}>
|
||||
<div className={`buff-draft ${inputLocked ? "is-input-locked" : ""} ${className}`.trim()} role="dialog" aria-modal="true" aria-label={`Choose a buff for round ${nextRound}`} aria-busy={inputLocked}>
|
||||
<header>
|
||||
<span>Round {round} cleared</span>
|
||||
<h2>Choose one blessing</h2>
|
||||
<p>Claim required. Round {nextRound} begins with two new bosses at {Math.round(bossHealthMultiplier(nextRound) * 100)}% base HP.</p>
|
||||
<p>Claim required. Round {nextRound} begins with {nextBossCount === 3 ? "an unseen trio" : "two new bosses"} at {Math.round(bossHealthMultiplier(nextRound) * 100)}% base HP.</p>
|
||||
</header>
|
||||
<div className={`buff-choice-grid choice-count-${choices.length}`}>
|
||||
{choices.length > 0 ? choices.map((buffId) => {
|
||||
@@ -35,6 +49,7 @@ export function BuffDraftPanel({ className = "" }: { className?: string }) {
|
||||
onFocus={() => setSelected(buffId)}
|
||||
onPointerEnter={() => setSelected(buffId)}
|
||||
onClick={() => choose(buffId)}
|
||||
disabled={inputLocked}
|
||||
aria-pressed={selected === buffId}
|
||||
>
|
||||
<i>{buff.icon}</i>
|
||||
@@ -44,7 +59,7 @@ export function BuffDraftPanel({ className = "" }: { className?: string }) {
|
||||
</button>
|
||||
);
|
||||
}) : (
|
||||
<button className="buff-mastery-continue is-controller-focused" onClick={continueRun}>
|
||||
<button className="buff-mastery-continue is-controller-focused" onClick={continueRun} disabled={inputLocked}>
|
||||
<i>✦</i>
|
||||
<span><small>Full mastery</small><strong>Continue Without Buff</strong></span>
|
||||
<b>All 18 blessings reached maximum rank.</b>
|
||||
@@ -52,7 +67,7 @@ export function BuffDraftPanel({ className = "" }: { className?: string }) {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<footer>{choices.length > 0 && <><b>← / →</b> Choose <i /></>} <b>A / ENTER</b> {choices.length > 0 ? "Claim" : "Continue"}</footer>
|
||||
<footer>{inputLocked ? <b>Choices ready in a moment…</b> : <>{choices.length > 0 && <><b>← / →</b> Choose <i /></>} <b>A / ENTER</b> {choices.length > 0 ? "Claim" : "Continue"}</>}</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user