Files
i-want-to-heal/src/components/RewardPanels.tsx
T
2026-07-05 22:48:56 -04:00

192 lines
4.7 KiB
TypeScript

import type { CombatLogEntry } from '../game'
import type { RunBossCoinAward } from '../combat/rewardSummaries'
import type { DungeonReward, LootRoll } from '../profile'
type BonusItem = NonNullable<DungeonReward['bonusItem']>
export function LevelGain({
previousLevel,
newLevel,
talentPointsGained,
}: {
previousLevel: number | null
newLevel: number | null
talentPointsGained: number
}) {
if (!previousLevel || !newLevel || talentPointsGained <= 0) return null
return (
<p className="level-gain">
Level {previousLevel} to {newLevel}
<small>+{talentPointsGained} talent point</small>
</p>
)
}
export function AbilityUnlocks({
abilities,
}: {
abilities: DungeonReward['unlockedAbilities']
}) {
return (
<>
{abilities.map((ability) => (
<p className="ability-unlock" key={ability.id}>
<span>{ability.glyph}</span>
Ability Unlocked: {ability.name}
</p>
))}
</>
)
}
export function RewardXpSummary({
reward,
}: {
reward: {
experienceGained: number
previousLevel: number | null
newLevel: number | null
talentPointsGained: number
unlockedAbilities: DungeonReward['unlockedAbilities']
}
}) {
return (
<>
<p>+{reward.experienceGained} XP</p>
<LevelGain previousLevel={reward.previousLevel} newLevel={reward.newLevel} talentPointsGained={reward.talentPointsGained} />
<AbilityUnlocks abilities={reward.unlockedAbilities} />
</>
)
}
export function BonusItemReward({
item,
eyebrow,
compact = false,
}: {
item?: BonusItem | null
eyebrow?: string
compact?: boolean
}) {
if (!item) return null
if (compact) {
return (
<p className="ability-unlock">
<span>{item.glyph}</span>
{item.name} x{item.quantity}
{item.duplicate ? ` (owned x${item.quantityAfter})` : ''}
</p>
)
}
return (
<div className="bonus-item">
{eyebrow && <p className="eyebrow">{eyebrow}</p>}
<div className="bonus-item-detail">
<span>{item.glyph}</span>
<strong className={`rarity-${item.rarity}`}>{item.name}</strong>
<small>Item Level {item.itemLevel} x{item.quantity}</small>
{item.duplicate && <small> (owned x{item.quantityAfter})</small>}
</div>
</div>
)
}
export function LootRollList({
rolls,
expectedRolls,
}: {
rolls: LootRoll[]
expectedRolls: number
}) {
return (
<div className="run-loot-rolls">
{rolls.map((roll) => (
<div className={roll.dropped || roll.petAwarded ? 'dropped' : 'empty'} key={roll.encounterId}>
<strong>{roll.encounterName}</strong>
<span>
{[
...roll.items.map((item) => `${item.glyph} ${item.name} x${item.quantity}${item.duplicate ? ` (owned x${item.quantityAfter})` : ''}`),
...(roll.petAwarded
? [`* ${roll.petAwarded.petName}${roll.petAwarded.duplicate ? ` (owned x${roll.petAwarded.quantityAfter})` : ''}`]
: []),
].join(', ') || 'No components dropped'}
</span>
</div>
))}
{rolls.length < expectedRolls && <small>Finishing loot rolls...</small>}
</div>
)
}
export function PvpRunLootList({
loot,
emptyLabel = 'No boss coins awarded',
}: {
loot: RunBossCoinAward[]
emptyLabel?: string
}) {
return (
<div className="run-loot-rolls">
{loot.length > 0 ? loot.map((item, index) => (
<div className="dropped" key={`${item.id}-${index}`}>
<strong>{item.sourceLabel}</strong>
<span>
{item.glyph} {item.name} x{item.quantity}
{item.duplicate ? ` (owned x${item.quantityAfter})` : ''}
</span>
</div>
)) : (
<div>
<strong>Boss Coins</strong>
<span>{emptyLabel}</span>
</div>
)}
</div>
)
}
export function ResultLogToggle({
log,
showEndLog,
onToggle,
}: {
log: CombatLogEntry[]
showEndLog: boolean
onToggle: () => void
}) {
if (log.length === 0) return null
return (
<>
<button className="secondary-result-button" onClick={onToggle} type="button">
{showEndLog ? 'Hide Combat Log' : 'View Combat Log'}
</button>
{showEndLog && (
<div className="result-log">
{log.slice().reverse().map((entry) => (
<div className={`log-entry ${entry.tone}`} key={entry.id}>{entry.text}</div>
))}
</div>
)}
</>
)
}
export function RematchControls({
requested,
message,
onRematch,
}: {
requested: boolean
message: string
onRematch: () => void
}) {
return (
<>
<button disabled={requested} onClick={onRematch} type="button">
{requested ? 'Waiting for Rematch' : 'Rematch'}
</button>
{message && <p>{message}</p>}
</>
)
}