mirror of
https://github.com/ershisan99/www.git
synced 2026-01-19 12:35:30 +00:00
add vouchers display
This commit is contained in:
@@ -34,6 +34,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { jokers } from '@/shared/jokers'
|
||||
import { vouchers } from '@/shared/vouchers'
|
||||
import { useFormatter } from 'next-intl'
|
||||
import Image from 'next/image'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
@@ -107,7 +108,12 @@ type Game = {
|
||||
logOwnerFinalJokers: string[] // Log owner's final jokers
|
||||
opponentFinalJokers: string[] // Opponent's final jokers
|
||||
events: LogEvent[]
|
||||
rerolls: number
|
||||
rerolls: number // Log owner's reroll count
|
||||
rerollCostTotal: number // Log owner's total reroll cost
|
||||
logOwnerVouchers: string[] // Log owner's vouchers
|
||||
opponentRerolls: number // Opponent's reroll count
|
||||
opponentRerollCostTotal: number // Opponent's total reroll cost
|
||||
opponentVouchers: string[] // Opponent's vouchers
|
||||
winner: 'logOwner' | 'opponent' | null // Who won the game
|
||||
pvpBlinds: PvpBlind[] // PVP blind data
|
||||
currentPvpBlind: number | null // Current PVP blind number
|
||||
@@ -140,6 +146,11 @@ const initGame = (id: number, startDate: Date): Game => ({
|
||||
opponentFinalJokers: [],
|
||||
events: [],
|
||||
rerolls: 0,
|
||||
rerollCostTotal: 0,
|
||||
logOwnerVouchers: [],
|
||||
opponentRerolls: 0,
|
||||
opponentRerollCostTotal: 0,
|
||||
opponentVouchers: [],
|
||||
winner: null,
|
||||
pvpBlinds: [],
|
||||
currentPvpBlind: null,
|
||||
@@ -347,6 +358,34 @@ export default function LogParser() {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (line.includes('Client got nemesisEndGameStats message')) {
|
||||
if (currentGame) {
|
||||
// Extract Opponent Reroll Count
|
||||
const rerollCountMatch = line.match(/\(reroll_count: (\d+)\)/)
|
||||
if (rerollCountMatch?.[1]) {
|
||||
currentGame.opponentRerolls = Number.parseInt(
|
||||
rerollCountMatch[1],
|
||||
10
|
||||
)
|
||||
}
|
||||
|
||||
// Extract Opponent Reroll Cost Total
|
||||
const rerollCostMatch = line.match(/\(reroll_cost_total: (\d+)\)/)
|
||||
if (rerollCostMatch?.[1]) {
|
||||
currentGame.opponentRerollCostTotal = Number.parseInt(
|
||||
rerollCostMatch[1],
|
||||
10
|
||||
)
|
||||
}
|
||||
|
||||
// Extract Opponent Vouchers
|
||||
const vouchersMatch = line.match(/\(vouchers: ([^)]+)\)/)
|
||||
if (vouchersMatch?.[1]) {
|
||||
currentGame.opponentVouchers = vouchersMatch[1].split('-')
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (line.includes('Client sent message: action:receiveEndGameJokers')) {
|
||||
if (currentGame) {
|
||||
// Mark end date if not already set (might happen slightly before 'got')
|
||||
@@ -362,6 +401,31 @@ export default function LogParser() {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (line.includes('Client sent message: action:nemesisEndGameStats')) {
|
||||
if (currentGame) {
|
||||
// Extract Log Owner Reroll Count
|
||||
const rerollCountMatch = line.match(/reroll_count:(\d+)/)
|
||||
if (rerollCountMatch?.[1]) {
|
||||
currentGame.rerolls = Number.parseInt(rerollCountMatch[1], 10)
|
||||
}
|
||||
|
||||
// Extract Log Owner Reroll Cost Total
|
||||
const rerollCostMatch = line.match(/reroll_cost_total:(\d+)/)
|
||||
if (rerollCostMatch?.[1]) {
|
||||
currentGame.rerollCostTotal = Number.parseInt(
|
||||
rerollCostMatch[1],
|
||||
10
|
||||
)
|
||||
}
|
||||
|
||||
// Extract Log Owner Vouchers
|
||||
const vouchersMatch = line.match(/vouchers:(.+)$/)
|
||||
if (vouchersMatch?.[1]) {
|
||||
currentGame.logOwnerVouchers = vouchersMatch[1].split('-')
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (lineLower.includes('startgame message')) {
|
||||
if (currentGame) {
|
||||
if (!currentGame.endDate) currentGame.endDate = timestamp
|
||||
@@ -855,7 +919,8 @@ export default function LogParser() {
|
||||
? ((currentGame.endDate instanceof Date
|
||||
? currentGame.endDate.getTime()
|
||||
: new Date(currentGame.endDate).getTime()) -
|
||||
currentGame.startDate.getTime()) / 1000
|
||||
currentGame.startDate.getTime()) /
|
||||
1000
|
||||
: null
|
||||
games.push(currentGame)
|
||||
}
|
||||
@@ -1049,8 +1114,18 @@ export default function LogParser() {
|
||||
: opponentLabel}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Rerolls:</strong>{' '}
|
||||
<strong>{ownerLabel} Rerolls:</strong>{' '}
|
||||
{game.rerolls || 'Unknown'}
|
||||
{game.rerollCostTotal
|
||||
? ` (Cost: ${game.rerollCostTotal})`
|
||||
: ''}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{opponentLabel} Rerolls:</strong>{' '}
|
||||
{game.opponentRerolls || 'Unknown'}
|
||||
{game.opponentRerollCostTotal
|
||||
? ` (Cost: ${game.opponentRerollCostTotal})`
|
||||
: ''}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Deck:</strong> {game.deck || 'Unknown'}
|
||||
@@ -1277,6 +1352,93 @@ export default function LogParser() {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className='text-lg'>Vouchers</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-3 text-sm'>
|
||||
<div>
|
||||
<strong>
|
||||
{ownerLabel}
|
||||
{game.winner === 'logOwner' ? ' 🏆' : ''}:
|
||||
</strong>
|
||||
{game.logOwnerVouchers.length > 0 ? (
|
||||
<ul className='mt-3 ml-4 flex list-inside gap-3'>
|
||||
{game.logOwnerVouchers.map((voucher, i) => {
|
||||
if (!voucher) {
|
||||
return null
|
||||
}
|
||||
const cleanName =
|
||||
vouchers[voucher]?.name ??
|
||||
cleanVoucherKey(voucher)
|
||||
return (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: Simple list
|
||||
<li key={i} className={'list-none'}>
|
||||
<div
|
||||
className={
|
||||
'flex flex-col items-center justify-center gap-2'
|
||||
}
|
||||
>
|
||||
<Image
|
||||
src={`/cards/${voucher}.png`}
|
||||
alt={cleanName}
|
||||
width={142}
|
||||
height={190}
|
||||
/>
|
||||
<span>{cleanName}</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<p className='text-gray-500 italic'>
|
||||
No data found.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<strong>
|
||||
{opponentLabel}
|
||||
{game.winner === 'opponent' ? ' 🏆' : ''}:
|
||||
</strong>
|
||||
{game.opponentVouchers.length > 0 ? (
|
||||
<ul className='mt-3 ml-4 flex list-inside gap-3'>
|
||||
{game.opponentVouchers.map((voucher, i) => {
|
||||
if (!voucher) {
|
||||
return null
|
||||
}
|
||||
const cleanName =
|
||||
vouchers[voucher]?.name ??
|
||||
cleanVoucherKey(voucher)
|
||||
return (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: Simple list
|
||||
<li key={i} className={'list-none'}>
|
||||
<div
|
||||
className={
|
||||
'flex flex-col items-center justify-center gap-2'
|
||||
}
|
||||
>
|
||||
<Image
|
||||
src={`/cards/${voucher}.png`}
|
||||
alt={cleanName}
|
||||
width={142}
|
||||
height={190}
|
||||
/>
|
||||
<span>{cleanName}</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<p className='text-gray-500 italic'>
|
||||
No data found.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className='text-lg'>Mods</CardTitle>
|
||||
@@ -1637,6 +1799,18 @@ function cleanJokerKey(key: string): string {
|
||||
)
|
||||
}
|
||||
|
||||
function cleanVoucherKey(key: string): string {
|
||||
if (!key) return ''
|
||||
return key
|
||||
.trim()
|
||||
.replace(/^v_/, '') // Remove prefix v_
|
||||
.replace(/_/g, ' ') // Replace underscores with spaces
|
||||
.replace(
|
||||
/\w\S*/g, // Capitalize each word (Title Case)
|
||||
(txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
||||
)
|
||||
}
|
||||
|
||||
type JsonValue =
|
||||
| string
|
||||
| number
|
||||
|
||||
41
src/shared/vouchers.ts
Normal file
41
src/shared/vouchers.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
export interface VoucherInfo {
|
||||
name: string
|
||||
file: string
|
||||
}
|
||||
|
||||
export const vouchers: { [key: string]: VoucherInfo } = {
|
||||
v_antimatter: { name: 'Antimatter', file: 'v_antimatter.png' },
|
||||
v_blank: { name: 'Blank', file: 'v_blank.png' },
|
||||
v_clearance_sale: { name: 'Clearance Sale', file: 'v_clearance_sale.png' },
|
||||
v_crystal_ball: { name: 'Crystal Ball', file: 'v_crystal_ball.png' },
|
||||
v_directors_cut: { name: "Director's Cut", file: 'v_directors_cut.png' },
|
||||
v_glow_up: { name: 'Glow Up', file: 'v_glow_up.png' },
|
||||
v_grabber: { name: 'Grabber', file: 'v_grabber.png' },
|
||||
v_hieroglyph: { name: 'Hieroglyph', file: 'v_hieroglyph.png' },
|
||||
v_hone: { name: 'Hone', file: 'v_hone.png' },
|
||||
v_illusion: { name: 'Illusion', file: 'v_illusion.png' },
|
||||
v_liquidation: { name: 'Liquidation', file: 'v_liquidation.png' },
|
||||
v_locked: { name: 'Locked', file: 'v_locked.png' },
|
||||
v_magic_trick: { name: 'Magic Trick', file: 'v_magic_trick.png' },
|
||||
v_money_tree: { name: 'Money Tree', file: 'v_money_tree.png' },
|
||||
v_nacho_tong: { name: 'Nacho Tong', file: 'v_nacho_tong.png' },
|
||||
v_observatory: { name: 'Observatory', file: 'v_observatory.png' },
|
||||
v_omen_globe: { name: 'Omen Globe', file: 'v_omen_globe.png' },
|
||||
v_overstock_norm: { name: 'Overstock', file: 'v_overstock_norm.png' },
|
||||
v_overstock_plus: { name: 'Overstock Plus', file: 'v_overstock_plus.png' },
|
||||
v_paint_brush: { name: 'Paint Brush', file: 'v_paint_brush.png' },
|
||||
v_palette: { name: 'Palette', file: 'v_palette.png' },
|
||||
v_petroglyph: { name: 'Petroglyph', file: 'v_petroglyph.png' },
|
||||
v_planet_merchant: { name: 'Planet Merchant', file: 'v_planet_merchant.png' },
|
||||
v_planet_tycoon: { name: 'Planet Tycoon', file: 'v_planet_tycoon.png' },
|
||||
v_recyclomancy: { name: 'Recyclomancy', file: 'v_recyclomancy.png' },
|
||||
v_reroll_glut: { name: 'Reroll Glut', file: 'v_reroll_glut.png' },
|
||||
v_reroll_surplus: { name: 'Reroll Surplus', file: 'v_reroll_surplus.png' },
|
||||
v_retcon: { name: 'Retcon', file: 'v_retcon.png' },
|
||||
v_seed_money: { name: 'Seed Money', file: 'v_seed_money.png' },
|
||||
v_tarot_merchant: { name: 'Tarot Merchant', file: 'v_tarot_merchant.png' },
|
||||
v_tarot_tycoon: { name: 'Tarot Tycoon', file: 'v_tarot_tycoon.png' },
|
||||
v_telescope: { name: 'Telescope', file: 'v_telescope.png' },
|
||||
v_undiscovered: { name: 'Locked', file: 'v_undiscovered.png' },
|
||||
v_wasteful: { name: 'Wasteful', file: 'v_wasteful.png' },
|
||||
}
|
||||
Reference in New Issue
Block a user