Release v0.1.17 2026-07-19
This commit is contained in:
@@ -10,9 +10,10 @@ import type {
|
||||
RpgRoguelikeRunState,
|
||||
RunGearItem,
|
||||
} from "../../game/rpgRoguelike";
|
||||
import { BOSSES_PER_ACT, TOTAL_BOSS_COUNT } from "../../game/rpgRoguelike";
|
||||
import { BOSSES_PER_ACT, TOTAL_BOSS_COUNT, compareRunGear } from "../../game/rpgRoguelike";
|
||||
import type { RpgUiCommand } from "../../game/rpgRoguelike";
|
||||
import { normalizeRpgFocusId } from "../../game/rpgRoguelike";
|
||||
import { PartyRoleBadge } from "./PartyRoleBadge";
|
||||
|
||||
export interface RpgRunUiProps {
|
||||
readonly run: RpgRoguelikeRunState;
|
||||
@@ -94,11 +95,14 @@ export function FocusButton({
|
||||
while (parent && (parent.closest(".rpg-run-overlay") || parent.closest(".rpg-run-tactical"))) {
|
||||
const childRect = button.getBoundingClientRect();
|
||||
const parentRect = parent.getBoundingClientRect();
|
||||
if (parent.scrollHeight > parent.clientHeight) {
|
||||
const overflow = getComputedStyle(parent);
|
||||
const canScrollY = /^(auto|scroll|overlay)$/.test(overflow.overflowY);
|
||||
const canScrollX = /^(auto|scroll|overlay)$/.test(overflow.overflowX);
|
||||
if (canScrollY && parent.scrollHeight > parent.clientHeight) {
|
||||
if (childRect.top < parentRect.top) parent.scrollTop -= parentRect.top - childRect.top;
|
||||
else if (childRect.bottom > parentRect.bottom) parent.scrollTop += childRect.bottom - parentRect.bottom;
|
||||
}
|
||||
if (parent.scrollWidth > parent.clientWidth) {
|
||||
if (canScrollX && parent.scrollWidth > parent.clientWidth) {
|
||||
if (childRect.left < parentRect.left) parent.scrollLeft -= parentRect.left - childRect.left;
|
||||
else if (childRect.right > parentRect.right) parent.scrollLeft += childRect.right - parentRect.right;
|
||||
}
|
||||
@@ -207,7 +211,7 @@ export function PartyCard({
|
||||
className={`rpg-party-card rarity-${entry.rarity} ${selected ? "is-picked" : ""} ${compact ? "is-compact" : ""}`.trim()}
|
||||
style={runAccentStyle(entry.color)}
|
||||
>
|
||||
<div className="rpg-card-kicker"><span>{entry.rarity}</span><b>{entry.role}</b></div>
|
||||
<div className="rpg-card-kicker"><span>{entry.rarity}</span><PartyRoleBadge role={entry.role} /></div>
|
||||
<h3>{entry.name}</h3>
|
||||
<p>{entry.className}</p>
|
||||
{!compact && (
|
||||
@@ -265,6 +269,39 @@ export function gearOwnerName(run: RpgRoguelikeRunState, item: RunGearItem): str
|
||||
return run.roster.find((member) => member.instanceId === item.ownerId)?.name ?? "Companion";
|
||||
}
|
||||
|
||||
export function gearComparisonLabel(run: RpgRoguelikeRunState, item: RunGearItem): string {
|
||||
const comparison = compareRunGear(run.equipment, item);
|
||||
const ownerName = gearOwnerName(run, item);
|
||||
const currentRank = comparison.currentItem ? `plus ${comparison.currentItem.enhancement}` : "none";
|
||||
const change = comparison.delta >= 0
|
||||
? `Gain ${comparison.delta} percentage points`
|
||||
: `Lose ${Math.abs(comparison.delta)} percentage points`;
|
||||
return `${ownerName} ${comparison.effectLabel}. Current gear: ${currentRank}, ${comparison.currentValue} percent. Replacement: plus ${item.enhancement}, ${comparison.replacementValue} percent. ${change}.`;
|
||||
}
|
||||
|
||||
export function GearStatComparison({ run, item }: {
|
||||
readonly run: RpgRoguelikeRunState;
|
||||
readonly item: RunGearItem;
|
||||
}) {
|
||||
const ownerName = gearOwnerName(run, item);
|
||||
const comparison = compareRunGear(run.equipment, item);
|
||||
const currentRank = comparison.currentItem ? `+${comparison.currentItem.enhancement}` : "none";
|
||||
const delta = `${comparison.delta >= 0 ? "+" : ""}${comparison.delta} pts`;
|
||||
return (
|
||||
<span
|
||||
className="rpg-gear-comparison"
|
||||
aria-label={gearComparisonLabel(run, item)}
|
||||
>
|
||||
<strong>{ownerName} · {comparison.effectLabel}<em>{delta}</em></strong>
|
||||
<span>
|
||||
<span><small>Current · {currentRank}</small><b>+{comparison.currentValue}%</b></span>
|
||||
<i aria-hidden="true">→</i>
|
||||
<span><small>Replacement · +{item.enhancement}</small><b>+{comparison.replacementValue}%</b></span>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function GearCard({ run, item, price, action }: {
|
||||
readonly run: RpgRoguelikeRunState;
|
||||
readonly item: RunGearItem;
|
||||
@@ -277,7 +314,8 @@ export function GearCard({ run, item, price, action }: {
|
||||
<div>
|
||||
<small>{gearOwnerName(run, item)} · {item.slotId}</small>
|
||||
<h3>{item.name}</h3>
|
||||
<p>+{item.statValue} {titleCase(item.statId)}{price !== undefined ? ` · ◆ ${price}` : ""}</p>
|
||||
{price !== undefined && <p className="rpg-gear-price">◆ {price}</p>}
|
||||
<GearStatComparison run={run} item={item} />
|
||||
</div>
|
||||
{action}
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user