diff --git a/src/components/Select.svelte b/src/components/Select.svelte index d7e6b0fc..c5b159d9 100644 --- a/src/components/Select.svelte +++ b/src/components/Select.svelte @@ -13,6 +13,7 @@ export let multiselect = false; export let image = false; export let disabled = false; + export let maxItemRow = -1; export let selected = null; export let selectedMulti = new Set(); @@ -123,6 +124,7 @@
{#each options as option, index} - import { t } from 'svelte-i18n'; + import { mdiLoading, mdiStar } from '@mdi/js'; + import { onMount, tick } from 'svelte'; + import Chart from 'chart.js'; + + import { number, t } from 'svelte-i18n'; import Ad from '../../../components/Ad.svelte'; import Button from '../../../components/Button.svelte'; + import Icon from '../../../components/Icon.svelte'; + import Select from '../../../components/Select.svelte'; import { banners } from '../../../data/banners'; - import Item from './_item.svelte'; - import ItemNew from './_itemNew.svelte'; + import { bannersDual } from '../../../data/bannersDual'; + import { characters } from '../../../data/characters'; + import { weaponList } from '../../../data/weaponList'; - let showOld = banners.characters.slice(7, banners.characters.length - 3).map((e) => false); + const numberFormat = Intl.NumberFormat('en', { + maximumFractionDigits: 2, + minimumFractionDigits: 0, + }); - function showOldTally(index) { - showOld[index] = true; + const numberFormatSecondary = Intl.NumberFormat('en', { + maximumFractionDigits: 0, + minimumFractionDigits: 0, + }); + + const types = [ + { label: 'Character Event', value: 'characters' }, + { label: 'Weapon Event', value: 'weapons' }, + { label: 'Standard', value: 'standard' }, + ]; + + const typeNumber = { + characters: 300000, + weapons: 400000, + standard: 200000, + }; + + let selectedType = types[0]; + let type = selectedType.value; + let banner = banners.characters[banners.characters.length - 1]; + let selectedBanner = { label: `${banner.name} ${banner.image}`, value: banners.characters.length - 1 }; + let selectedIndex = 0; + let featured = { + type, + items: banner.featured, + }; + let id = typeNumber[type] + selectedBanner.value + 1; + + let loading = true; + let featuredValues = []; + let legendary = { + total: 0, + percentage: 0, + }; + let rare = { + total: 0, + percentage: 0, + }; + let median = 0; + let totalUser = 0; + let totalWish = 0; + let worth = 0; + let legendaryList = []; + let rareList = []; + + let chart; + let chart2; + + function onChangeType() { + type = selectedType.value; + banner = banners[type][banners[type].length - 1]; + selectedBanner = { label: `${banner.name} ${banner.image}`, value: banners[type].length - 1 }; + selectedIndex = 0; + id = typeNumber[type] + selectedBanner.value + 1; + featured = { + type, + items: banner.featured, + }; + + getData(); } + + function onChangeBanner() { + banner = banners[type][selectedBanner.value]; + id = typeNumber[type] + selectedBanner.value + 1; + featured = { + type, + items: banner.featured, + }; + + getData(); + } + + async function getData() { + loading = true; + + const url = new URL(`${__paimon.env.API_HOST}/wish`); + const query = new URLSearchParams({ banner: id }); + url.search = query.toString(); + + try { + const res = await fetch(url, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + + const data = await res.json(); + + const values = [0, 0]; + + legendaryList = []; + rareList = []; + for (let i = 0; i < data.list.length; i++) { + const feat = data.list[i]; + if (feat.type === 'character') { + const character = characters[feat.name]; + feat.fullname = character.name; + if (character.rarity === 5) { + legendaryList = [...legendaryList, feat]; + values[0] += feat.count; + } else { + rareList = [...rareList, feat]; + } + } else if (feat.type === 'weapon') { + const weapon = weaponList[feat.name]; + feat.fullname = weapon.name; + if (weapon.rarity === 5) { + legendaryList = [...legendaryList, feat]; + values[1] += feat.count; + } else { + rareList = [...rareList, feat]; + } + } + } + + legendaryList.sort((a, b) => b.count - a.count); + rareList.sort((a, b) => b.count - a.count); + legendaryList = legendaryList; + rareList = rareList; + + if (type !== 'standard') { + for (let i = 0; i < featured.items.length; i++) { + const feat = featured.items[i]; + const item = data.list.find((e) => e.name === feat); + console.log(feat, item); + featuredValues[i] = { + total: item.count, + guaranteed: ((item.count - item.guaranteed) / (data.total.legendary - item.guaranteed)) * 100, + percentage: (item.count / data.total.legendary) * 100, + }; + } + } else { + featuredValues = [ + { + total: values[0], + percentage: (values[0] / data.total.legendary) * 100, + }, + { + total: values[1], + percentage: (values[1] / data.total.legendary) * 100, + }, + ]; + } + + legendary = { + total: data.total.legendary, + percentage: (data.total.legendary / data.total.all) * 100, + }; + + rare = { + total: data.total.rare, + percentage: (data.total.rare / data.total.all) * 100, + }; + + median = data.median.legendary; + totalUser = data.total.users; + totalWish = data.total.all; + worth = data.total.all * 160; + + const legendaryPityChance = data.pityCount.legendary + .slice(1, type === 'weapons' ? 81 : 91) + .map((e, i) => Math.min((e / data.countEachPity[i]) * 100, 100).toFixed(2)); + const legendaryTotalPull = data.pityCount.legendary.slice(1, type === 'weapons' ? 81 : 91); + const rareTotalPull = data.pityCount.rare; + + loading = false; + await tick(); + + Chart.defaults.global.defaultFontColor = '#ffffff'; + Chart.defaults.global.defaultFontFamily = 'Poppins'; + new Chart(chart, { + type: 'bar', + data: { + labels: [...new Array(type === 'weapons' ? 80 : 90)].map((_, i) => i + 1), + datasets: [ + { + label: 'Total Pull 5★', + data: legendaryTotalPull, + borderColor: '#ffb13f', + backgroundColor: '#ffb13f', + fill: false, + yAxisID: 'left-y-axis', + order: 1, + }, + { + label: 'Chance%', + data: legendaryPityChance, + borderColor: '#4E7CFF', + backgroundColor: '#4E7CFF', + borderWidth: 3, + pointRadius: 2, + fill: false, + type: 'line', + yAxisID: 'right-y-axis', + order: 0, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { + mode: 'index', + intersect: false, + }, + tooltips: { + mode: 'index', + intersect: false, + callbacks: { + title: (tooltipItem) => { + return 'Pity ' + tooltipItem[0].label; + }, + }, + }, + stacked: false, + scales: { + yAxes: [ + { + id: 'left-y-axis', + type: 'linear', + display: true, + position: 'left', + }, + { + id: 'right-y-axis', + type: 'linear', + display: true, + position: 'right', + grid: { + drawOnChartArea: false, + }, + }, + ], + }, + }, + }); + + new Chart(chart2, { + type: 'bar', + data: { + labels: [...new Array(10)].map((_, i) => i + 1), + datasets: [ + { + label: 'Total Pull 4★', + data: rareTotalPull, + borderColor: '#D28FD6', + backgroundColor: '#D28FD6', + fill: false, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { + mode: 'index', + intersect: false, + }, + tooltips: { + mode: 'index', + intersect: false, + callbacks: { + title: (tooltipItem) => { + return 'Pity ' + tooltipItem[0].label; + }, + }, + }, + }, + }); + } catch (err) { + console.error(err); + } + } + + onMount(() => { + getData(); + }); + + $: dual = bannersDual[`${banner.name} ${banner.image}`]; + $: banner2 = dual !== undefined ? dual[1] : null; + $: bannerOptions = banners[type] + .map((e, i) => { + const name = `${e.name} ${e.image}`; + const image = type !== 'standard' ? `/images/${type}/${e.featured[0]}.png` : undefined; + + if (bannersDual[name]) + return { label: `${name} & ${bannersDual[name][1].name} ${bannersDual[name][1].image}`, value: i, image }; + else return { label: name, value: i, image }; + }) + .reverse(); @@ -19,59 +316,273 @@
-
+

{$t('wish.tally.title')}

※ {$t('wish.tally.subtitle')}

-
- - -
- - - - +
+
- - - - - {#each showOld as show, i} - {#if show} - +
+ {#if loading} + + {:else} +
+ {banner.name} - - {:else} - -
- {/if} - {/each} + {#if dual !== undefined} + {banner2.name} + {/if} +
+
+ + {#if featured.type === 'standard'} + + + + + + + + + + {:else} + {#each featured.items as feat, i} + {#if i === 1} + + {/if} + + + + + + {/each} + {/if} +
+ {numberFormatSecondary.format(featuredValues[0].total)} + +

+ {$t('wish.tally.character')} + {$t('wish.tally.summoned')} +

+

+ {numberFormat.format(featuredValues[0].percentage)}% {$t('wish.tally.fromFiveStar')} +

+
+ {numberFormatSecondary.format(featuredValues[1].total)} + +

+ {$t('wish.tally.weapon')} + {$t('wish.tally.summoned')} +

+

+ {numberFormat.format(featuredValues[1].percentage)}% {$t('wish.tally.fromFiveStar')} +

+
+ + + {numberFormatSecondary.format(featuredValues[i].total)} + +

+ {featured.type === 'characters' ? characters[feat].name : weaponList[feat].name} + {$t('wish.tally.summoned')} +

+

+ {numberFormat.format(featuredValues[i].guaranteed)}% + {$t('wish.tally.wonFiftyFifty')} +

+
+ + + + + + + + + + +
+ {numberFormat.format(legendary.percentage)}% + +

+ + + + + +

+

Total {numberFormat.format(legendary.total)}

+
+ {numberFormat.format(rare.percentage)}% + +

+ + + + +

+

Total {numberFormat.format(rare.total)}

+
+
+ + + + + + + + + + + + + + + + + +
+ {$t('wish.tally.median')} + + {numberFormat.format(median)} +
+ {$t('wish.tally.user')} + + {numberFormat.format(totalUser)} +
+ {$t('wish.tally.wishTotal')} + + {numberFormat.format(totalWish)} +
+ {$t('wish.tally.worth')} primogem + + {numberFormat.format(worth)} +
+
+
+
+ +
+
+
+

5★ List

+ + + + + + + {#each legendaryList as item} + + + + + + + {/each} +
+ NameTotal%
+ {item.name} + + {item.fullname} + + {numberFormatSecondary.format(item.count)} + + {numberFormat.format((item.count / legendary.total) * 100)} +
+
+
+

4★ List

+ + + + + + + {#each rareList as item} + + + + + + + {/each} +
+ NameTotal%
+ {item.name} + + {item.fullname} + + {numberFormatSecondary.format(item.count)} + + {numberFormat.format((item.count / rare.total) * 100)} +
+
+
+ +
+
+ {/if}
+ +