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}
+
+

-
- {:else}
-
-
- {/if}
- {/each}
+ {#if dual !== undefined}
+

+ {/if}
+
+
+
+ {#if featured.type === 'standard'}
+
+
+ {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')}
+
+ |
+
+ {:else}
+ {#each featured.items as feat, i}
+ {#if i === 1}
+
+ {/if}
+
+
+
+ |
+
+ {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')}
+
+ |
+
+ {/each}
+ {/if}
+
+
+
+
+ {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')}
+ |
+
+ {numberFormat.format(worth)}
+ |
+
+
+
+
+
+
+
+
+
+
5★ List
+
+
+ |
+ Name |
+ Total |
+ % |
+
+ {#each legendaryList as item}
+
+
+
+ |
+
+ {item.fullname}
+ |
+
+ {numberFormatSecondary.format(item.count)}
+ |
+
+ {numberFormat.format((item.count / legendary.total) * 100)}
+ |
+
+ {/each}
+
+
+
+
4★ List
+
+
+ |
+ Name |
+ Total |
+ % |
+
+ {#each rareList as item}
+
+
+
+ |
+
+ {item.fullname}
+ |
+
+ {numberFormatSecondary.format(item.count)}
+ |
+
+ {numberFormat.format((item.count / rare.total) * 100)}
+ |
+
+ {/each}
+
+
+
+
+
+
+ {/if}
+
+