Use website for berry dash leaderboards

This commit is contained in:
Lncvrt committed 2026-05-07 11:56:41 -07:00
1 parent f6b027d3b2
commit ca6d85e23c
5 files changed
+18 -392

No files matched your search

@@ -1,358 +0,0 @@
'use client'
import './styles.css'
import { useCallback, useEffect, useState } from 'react'
import { BirdColor } from '@/types/BerryDash/BirdColor'
import { GetIconForUser } from '@/lib/berrydashutil'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import { arch, platform } from '@tauri-apps/plugin-os'
import { fetch } from '@tauri-apps/plugin-http'
import { verifySignature } from '@/lib/util'
import Dropdown from '@/components/Dropdown'
import { app } from '@tauri-apps/api'
interface BaseEntry {
id: number
username: string
value: number
icon: number
overlay: number
birdColor: BirdColor
overlayColor: BirdColor
customIcon: string | null
}
interface Stats {
highScore: string
totalNormalBerries: string
totalPoisonBerries: string
totalSlowBerries: string
totalUltraBerries: string
totalSpeedyBerries: string
totalCoinBerries: string
totalRandomBerries: string
totalAntiBerries: string
totalGoldenBerries: string
coins: string
}
interface LeaderboardEntry extends BaseEntry {
type: 'leaderboard'
value: number
}
interface Account extends BaseEntry {
type: 'account'
stats: Stats
xp: bigint
}
export function calculateXP (
normalBerries: bigint,
poisonBerries: bigint,
slowBerries: bigint,
ultraBerries: bigint,
speedyBerries: bigint,
coinBerries: bigint,
randomBerries: bigint,
antiBerries: bigint,
goldenBerries: bigint
): bigint {
let totalXp = 0n
totalXp += normalBerries
totalXp -= poisonBerries
totalXp -= slowBerries
totalXp += ultraBerries * 5n
totalXp += speedyBerries * 10n
totalXp += coinBerries * 10n
totalXp += randomBerries
totalXp -= antiBerries
totalXp += goldenBerries * 4n
if (totalXp < 0n) totalXp = 0n
return totalXp
}
export function calculateLevel (xp: bigint): number {
const levelDivisor = 50.0
const xpNumber = Number(xp)
const discriminant = 95 * 95 + levelDivisor * 2 * xpNumber
const level = (-95 + Math.sqrt(discriminant)) / levelDivisor
return Math.floor(level) + 1
}
export default function BerryDashLeaderboards () {
const [selected, setSelected] = useState<number>(-1)
const [selectedBerryOption, setSelectedBerryOption] = useState<number>(0)
const [entries, setEntries] = useState<(LeaderboardEntry | Account)[]>([])
const [loading, setLoading] = useState<boolean>(false)
const router = useRouter()
const Refresh = useCallback(async () => {
setLoading(true)
try {
if (selected == 3 || selected == 4) {
const response = await fetch(
'https://games.lncvrt.xyz/api/berrydash/account?username=',
{
headers: {
Requester: 'LncvrtGamesLauncherClient',
ClientVersion: await app.getVersion(),
ClientPlatform: platform() + '-' + arch()
}
}
)
const signature = response.headers.get('x-signature') ?? ''
const data = await response.json()
if (
(await verifySignature(JSON.stringify(data), signature)) &&
data.success
) {
let accounts = data.data as Account[]
accounts = accounts.map(acc => {
const xp = calculateXP(
BigInt(acc.stats.totalNormalBerries),
BigInt(acc.stats.totalPoisonBerries),
BigInt(acc.stats.totalSlowBerries),
BigInt(acc.stats.totalUltraBerries),
BigInt(acc.stats.totalSpeedyBerries),
BigInt(acc.stats.totalCoinBerries),
BigInt(acc.stats.totalRandomBerries),
BigInt(acc.stats.totalAntiBerries),
BigInt(acc.stats.totalGoldenBerries)
)
return { ...acc, xp }
})
accounts.sort((a, b) => (b.xp > a.xp ? 1 : b.xp < a.xp ? -1 : 0))
setEntries(accounts)
} else console.log('Failed')
} else {
const response = await fetch(
'https://games.lncvrt.xyz/api/berrydash/leaderboard/' +
(selected == 0
? 'score'
: selected == 1
? 'berry?berry=' + selectedBerryOption
: selected == 2
? 'coin'
: selected == 5
? 'legacy'
: 'total'),
{
headers: {
Requester: 'LncvrtGamesLauncherClient',
ClientVersion: await app.getVersion(),
ClientPlatform: platform() + '-' + arch()
}
}
)
const signature = response.headers.get('x-signature') ?? ''
const data = await response.json()
if (
(await verifySignature(JSON.stringify(data), signature)) &&
data.success
)
setEntries(data.data as LeaderboardEntry[])
}
} catch {
setEntries([])
}
setLoading(false)
}, [selected, selectedBerryOption])
useEffect(() => {
document.title = 'Lncvrt Games - Berry Dash Leaderboards'
}, [])
useEffect(() => {
if (selected != -1) setTimeout(() => Refresh(), 0)
}, [selected, Refresh])
return (
<div className='mx-4 mt-4'>
<div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Berry Dash Leaderboards</p>
<div className='flex gap-2'>
{selected == 1 && (
<div className='flex justify-center'>
<Dropdown
value={selectedBerryOption}
options={[
{ label: 'Normal Berry', value: 0 },
{ label: 'Poison Berry', value: 1 },
{ label: 'Slow Berry', value: 2 },
{ label: 'Ultra Berry', value: 3 },
{ label: 'Speedy Berry', value: 4 },
{ label: 'Coin Berry', value: 5 },
{ label: 'Random Berry', value: 6 },
{ label: 'Anti Berry', value: 7 },
{ label: 'Golden Berry', value: 8 }
]}
onChange={e => setSelectedBerryOption(Number(e))}
/>
</div>
)}
<button
className='button btntheme1'
onClick={() => {
setEntries([])
Refresh()
}}
title='Click to refresh the leaderboards.'
hidden={selected == -1}
>
Refresh
</button>
<button
className='button btntheme1'
onClick={() => {
if (selected == -1) router.push('/main/game?id=1')
else {
setSelected(-1)
setSelectedBerryOption(0)
setEntries([])
}
}}
title='Click to go up a level.'
>
Back
</button>
</div>
</div>
<div
className={`box ${
platform() == 'windows'
? 'h-[calc(100vh-116px)]'
: 'h-[calc(100vh-84px)]'
}`}
>
{selected == -1 ? (
<>
<p className='text-center mt-2 text-xl'>Select a Leaderboard</p>
<div className='flex flex-col gap-2 mt-2 items-center justify-center'>
<button
className='leaderboard-button'
onClick={() => setSelected(0)}
>
Score Leaderboard
</button>
<button
className='leaderboard-button'
onClick={() => setSelected(1)}
>
Berry Leaderboard
</button>
<button
className='leaderboard-button'
onClick={() => setSelected(2)}
>
Coins Leaderboard
</button>
<button
className='leaderboard-button'
onClick={() => setSelected(3)}
>
Level Leaderboard
</button>
<button
className='leaderboard-button'
onClick={() => setSelected(4)}
>
Total XP Leaderboard
</button>
<button
className='leaderboard-button'
onClick={() => setSelected(5)}
>
Legacy Leaderboard
</button>
<button
className='leaderboard-button'
onClick={() => setSelected(6)}
>
Total Berries Leaderboard
</button>
</div>
</>
) : loading ? (
<p className='flex justify-center items-center text-6xl h-full'>
Loading
</p>
) : (
<>
<div
className={`flex flex-col gap-2 overflow-y-auto ${
platform() == 'windows'
? 'h-[calc(100vh-128px)]'
: 'h-[calc(100vh-96px)]'
} px-1`}
>
{entries.map((item, index) => {
const isAccount = 'stats' in item
const isLeaderboard = 'value' in item && !isAccount
return (
<div
key={item.id}
className='leaderboard-entry flex justify-between items-center'
>
<div className='flex items-center gap-1'>
<Image
src={
!item.customIcon
? `https://games.lncvrt.xyz/api/berrydash/render-icon?icon=${
item.icon == 1
? GetIconForUser(item.id)
: item.icon
}&overlay=${item.overlay}&birdR=${
item.birdColor[0]
}&birdG=${item.birdColor[1]}&birdB=${
item.birdColor[2]
}&overlayR=${item.overlayColor[0]}&overlayG=${
item.overlayColor[1]
}&overlayB=${item.overlayColor[2]}`
: `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?id=${item.customIcon}&raw=true`
}
width={48}
height={48}
alt=''
className='pointer-events-none scale-x-[-1]'
/>
<p>
{item.username} (#{index + 1})
</p>
</div>
{isLeaderboard ? (
<p>
{selected === 1 || selected === 6
? 'Berries'
: selected === 2
? 'Coins'
: 'Score'}
: {item.value.toLocaleString()}
</p>
) : isAccount ? (
<p>
{selected === 3 ? 'Level' : 'XP'}:{' '}
{selected === 3
? calculateLevel(item.xp).toLocaleString()
: item.xp.toLocaleString()}
</p>
) : null}
</div>
)
})}
</div>
</>
)}
</div>
</div>
)
}
@@ -1,13 +0,0 @@
@import "tailwindcss";
.box {
@apply bg-(--col1) border border-(--col3) rounded-lg w-auto p-1;
}
.leaderboard-button {
@apply bg-(--col2) hover:bg-(--col4) border border-(--col4) hover:border-(--col6) rounded-lg px-4 py-2 inline-block transition-all cursor-pointer;
}
.leaderboard-entry {
@apply rounded-lg text-gray-200 text-lg px-1 py-0.5 transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5) cursor-pointer;
}
+14 -6
View File
@@ -4,7 +4,7 @@ import '../Installs.css'
import { useEffect } from 'react'
import { invoke } from '@tauri-apps/api/core'
import { useGlobal } from '@/providers/GlobalProvider'
import { useRouter, useSearchParams } from 'next/navigation'
import { useSearchParams } from 'next/navigation'
import { platform } from '@tauri-apps/plugin-os'
import { faWarning } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
@@ -30,11 +30,11 @@ export default function Installs () {
customDataLocation,
needsRevisionUpdate,
launchGame,
developerMode
developerMode,
theme
} = useGlobal()
const params = useSearchParams()
const router = useRouter()
const id = Number(params.get('id') || 0)
const game = serverVersionList?.games.find(g => g.id === id)
@@ -73,9 +73,17 @@ export default function Installs () {
<div className='flex gap-2'>
<button
className='button btntheme1'
onClick={() => {
router.push('/main/game/berrydash/leaderboards')
}}
onClick={() =>
invoke('open_new_window', {
title: 'Berry Dash Leaderboards',
name: 'berrydashleaderboards',
url:
'https://games.lncvrt.xyz/game/berry-dash/leaderboards?launcher=1&theme=' +
theme,
width: 800,
height: 600
})
}
title='View the leaderboards for this game.'
hidden={game.id != 1}
>
+4 -8
View File
@@ -132,10 +132,8 @@ export default function Sidebar () {
<div
draggable={false}
className={`link ${
(pathname === '/main/game' &&
Number(params.get('id') || 0) == i.id) ||
(i.id == 1 &&
pathname === '/main/game/berrydash/leaderboards')
pathname === '/main/game' &&
Number(params.get('id') || 0) == i.id
? 'active'
: ''
} ml-auto w-50 ${
@@ -191,10 +189,8 @@ export default function Sidebar () {
key={`${i.id}-${key}`}
draggable={false}
className={`link ${
((pathname === '/main/game' &&
Number(params.get('id') || 0) == i.id) ||
(i.id == 1 &&
pathname === '/main/game/berrydash/leaderboards')) &&
pathname === '/main/game' &&
Number(params.get('id') || 0) == i.id &&
category == Number(key)
? 'active'
: ''
-7
View File
@@ -1,7 +0,0 @@
export const GetIconForUser = (user: number) => {
if (user == 1) return -1
else if (user == 2) return -2
else if (user == 4) return -3
else if (user == 3) return -4
else return 1
}