Merge branch 'main' of https://github.com/MadeBaruna/paimon-moe
|
@ -24,6 +24,7 @@
|
|||
{ id: 'en', label: 'English' },
|
||||
{ id: 'id', label: 'Indonesia' },
|
||||
{ id: 'ru', label: 'Русский' },
|
||||
{ id: 'ko', label: '한국어' },
|
||||
];
|
||||
$: currentLocale = languages.find((e) => e.id === $locale.substring(0, 2)) || { id: 'en', label: 'English' };
|
||||
$: locales = languages.filter((e) => e.id !== currentLocale.id);
|
||||
|
@ -140,7 +141,7 @@
|
|||
<div class="locale-dropdown" style="top: {locales.length * -45}px;">
|
||||
{#each locales as locale}
|
||||
<div
|
||||
class="flex items-center justify-center py-2 cursor-pointer rounded-xl bg-opacity-50 bg-black hover:bg-opacity-75"
|
||||
class="flex items-center justify-center py-2 cursor-pointer rounded-xl bg-black hover:bg-gray-900"
|
||||
on:click={() => changeLocale(locale.id)}
|
||||
>
|
||||
<img class="w-4 h-4 rounded-full mr-2" alt={locale.label} src="/images/locales/{locale.id}.svg" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { fly } from 'svelte/transition';
|
||||
|
||||
export let image;
|
||||
export let label;
|
||||
|
@ -14,7 +14,6 @@
|
|||
const dispatch = createEventDispatcher();
|
||||
|
||||
function expand() {
|
||||
if (!mobile) return;
|
||||
expandMenu = !expandMenu;
|
||||
}
|
||||
|
||||
|
@ -58,8 +57,8 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{#if mobile && expandMenu}
|
||||
<div class="py-2 flex flex-col w-full" transition:slide={{ duration: 100 }}>
|
||||
{#if expandMenu}
|
||||
<div class="py-2 flex flex-col w-full" transition:fly={{ y: -100, duration: 100 }}>
|
||||
{#each items as item}
|
||||
<a
|
||||
on:click={clicked}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
onMount(() => {
|
||||
if (!isEdit) return;
|
||||
if (editType === 'unknown_3_star') return;
|
||||
|
||||
type = selectOptions.find((e) => e.value === editType);
|
||||
|
||||
|
|
|
@ -73,11 +73,6 @@
|
|||
let currentBanner = '';
|
||||
let currentPage = 1;
|
||||
|
||||
let lastPull = {
|
||||
id: '0',
|
||||
time: '',
|
||||
};
|
||||
|
||||
function cancel() {
|
||||
fetchController.abort();
|
||||
cancelled = true;
|
||||
|
@ -229,11 +224,6 @@
|
|||
const name = row.name;
|
||||
const type = row.item_type.replace(/ /g, '');
|
||||
|
||||
if (row.id > lastPull.id) {
|
||||
lastPull.id = row.id;
|
||||
lastPull.time = row.time;
|
||||
}
|
||||
|
||||
if (time.unix() <= newestPullTime) {
|
||||
return;
|
||||
}
|
||||
|
@ -409,7 +399,7 @@
|
|||
pushToast($t('wish.import.success'));
|
||||
|
||||
if (wishTallyChecked) {
|
||||
submitWishTally(lastPull);
|
||||
submitWishTally();
|
||||
}
|
||||
|
||||
const prefix = getAccountPrefix();
|
||||
|
|
1
src/data/achievement/ko.json
Normal file
1381
src/data/characterData/yanfei.json
Normal file
|
@ -2264,6 +2264,77 @@ export const characters = {
|
|||
boss: itemList.tusk_of_monoceros_caeli,
|
||||
},
|
||||
},
|
||||
yanfei: {
|
||||
name: 'Yanfei',
|
||||
id: 'yanfei',
|
||||
rarity: 4,
|
||||
element: elements.pyro,
|
||||
weapon: weapons.catalyst,
|
||||
sex: 'female',
|
||||
nation: 'liyue',
|
||||
ascension: [
|
||||
{
|
||||
items: [
|
||||
{ item: itemList.agnidus_agate_sliver, amount: 1 },
|
||||
{ item: itemList.none, amount: null },
|
||||
{ item: itemList.noctilucous_jade, amount: 3 },
|
||||
{ item: itemList.treasure_hoarder_insignia, amount: 3 },
|
||||
],
|
||||
mora: 20000,
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ item: itemList.agnidus_agate_fragment, amount: 3 },
|
||||
{ item: itemList.juvenile_jade, amount: 2 },
|
||||
{ item: itemList.noctilucous_jade, amount: 10 },
|
||||
{ item: itemList.treasure_hoarder_insignia, amount: 15 },
|
||||
],
|
||||
mora: 40000,
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ item: itemList.agnidus_agate_fragment, amount: 6 },
|
||||
{ item: itemList.juvenile_jade, amount: 4 },
|
||||
{ item: itemList.noctilucous_jade, amount: 20 },
|
||||
{ item: itemList.silver_raven_insignia, amount: 12 },
|
||||
],
|
||||
mora: 60000,
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ item: itemList.agnidus_agate_chunk, amount: 3 },
|
||||
{ item: itemList.juvenile_jade, amount: 8 },
|
||||
{ item: itemList.noctilucous_jade, amount: 30 },
|
||||
{ item: itemList.silver_raven_insignia, amount: 18 },
|
||||
],
|
||||
mora: 80000,
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ item: itemList.agnidus_agate_chunk, amount: 6 },
|
||||
{ item: itemList.juvenile_jade, amount: 12 },
|
||||
{ item: itemList.noctilucous_jade, amount: 45 },
|
||||
{ item: itemList.golden_raven_insignia, amount: 12 },
|
||||
],
|
||||
mora: 100000,
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ item: itemList.agnidus_agate_gemstone, amount: 6 },
|
||||
{ item: itemList.juvenile_jade, amount: 20 },
|
||||
{ item: itemList.noctilucous_jade, amount: 60 },
|
||||
{ item: itemList.golden_raven_insignia, amount: 24 },
|
||||
],
|
||||
mora: 120000,
|
||||
},
|
||||
],
|
||||
stats: { hp: 10425, atk: 231, def: 743 },
|
||||
material: {
|
||||
book: [itemList.teachings_of_gold, itemList.guide_to_gold, itemList.philosophies_of_gold],
|
||||
material: [itemList.treasure_hoarder_insignia, itemList.silver_raven_insignia, itemList.golden_raven_insignia],
|
||||
boss: itemList.bloodjade_branch,
|
||||
},
|
||||
},
|
||||
zhongli: {
|
||||
name: 'Zhongli',
|
||||
id: 'zhongli',
|
||||
|
|
|
@ -635,4 +635,5 @@ export const itemList = {
|
|||
qingxin: { id: 'qingxin', name: 'Qingxin' },
|
||||
shadow_of_the_warrior: { id: 'shadow_of_the_warrior', name: 'Shadow of the Warrior' },
|
||||
juvenile_jade: { id: 'juvenile_jade', name: 'Juvenile Jade', rarity: 5 },
|
||||
bloodjade_branch: { id: 'bloodjade_branch', name: 'Bloodjade Branch', rarity: 5 }
|
||||
};
|
||||
|
|
|
@ -33,6 +33,17 @@ export const eventsData = [
|
|||
url: 'https://webstatic-sea.mihoyo.com/ys/event/e20210421-homeland/index.html',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'To the Stars Once More',
|
||||
pos: '20% 15%',
|
||||
image: 'to_the_stars_once_more.jpg',
|
||||
start: '2021-04-28 13:00:00',
|
||||
end: '2021-05-17 23:59:59',
|
||||
color: '#DDC59A',
|
||||
zoom: '180%',
|
||||
url: 'https://genshin.mihoyo.com/en/news/detail/12368',
|
||||
showOnHome: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
|
@ -68,18 +79,6 @@ export const eventsData = [
|
|||
url: 'https://www.hoyolab.com/genshin/article/295568',
|
||||
showOnHome: true,
|
||||
},
|
||||
{
|
||||
name: 'Update 1.5!',
|
||||
pos: '0% 25%',
|
||||
image: 'update15.jpg',
|
||||
start: '2021-04-28 06:00:00',
|
||||
end: '2021-05-05 06:00:00',
|
||||
color: '#FFD768',
|
||||
zoom: '130%',
|
||||
url: 'https://www.youtube.com/watch?v=wgNiGj1nGYE',
|
||||
showOnHome: true,
|
||||
startOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'Windtrace',
|
||||
pos: '0% 80%',
|
||||
|
@ -117,13 +116,13 @@ export const eventsData = [
|
|||
},
|
||||
{
|
||||
name: 'Energy Amplifier Initiation',
|
||||
pos: '0% 20%',
|
||||
image: 'energy_amplifier.jpg',
|
||||
pos: '0% 25%',
|
||||
image: 'energy_amplifier_initiation.jpg',
|
||||
start: '2021-04-30 10:00:00',
|
||||
end: '2021-05-17 04:00:00',
|
||||
color: '#5CB8FE',
|
||||
zoom: '180%',
|
||||
url: 'https://genshin.mihoyo.com/en/news/detail/12281',
|
||||
zoom: '220%',
|
||||
url: 'https://www.hoyolab.com/genshin/article/329821',
|
||||
showOnHome: true,
|
||||
},
|
||||
],
|
||||
|
@ -171,6 +170,33 @@ export const eventsData = [
|
|||
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
|
||||
startOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'Act I',
|
||||
start: '2021-04-30 10:00:00',
|
||||
end: '2021-05-03 04:00:00',
|
||||
color: '#5CB8FE',
|
||||
url: 'https://www.hoyolab.com/genshin/article/329821',
|
||||
startOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'Act II',
|
||||
start: '2021-05-03 04:00:00',
|
||||
end: '2021-05-06 04:00:00',
|
||||
color: '#5CB8FE',
|
||||
url: 'https://www.hoyolab.com/genshin/article/329821',
|
||||
startOnly: true,
|
||||
},
|
||||
{
|
||||
name: 'Act III',
|
||||
start: '2021-05-06 04:00:00',
|
||||
end: '2021-05-17 04:00:00',
|
||||
color: '#5CB8FE',
|
||||
url: 'https://www.hoyolab.com/genshin/article/329821',
|
||||
zoom: '220%',
|
||||
pos: '0% 25%',
|
||||
image: 'energy_amplifier_initiation.jpg',
|
||||
startOnly: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
|
@ -300,6 +326,16 @@ export const eventsData = [
|
|||
image: 'paimon_bargain.png',
|
||||
description: 'Now selling: Razor, Amber, and Royal Weapons.',
|
||||
},
|
||||
{
|
||||
name: "Paimon's Bargain - Bennett, Lisa, and Blackcliff",
|
||||
pos: '0% 50%',
|
||||
zoom: '150%',
|
||||
start: '2021-05-01 04:00:00',
|
||||
end: '2021-06-01 04:00:00',
|
||||
color: '#B6A1EA',
|
||||
image: 'paimon_bargain.png',
|
||||
description: 'Now selling: Bennett, Lisa, and Blackcliff Weapons.',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
|
|
|
@ -72,6 +72,7 @@ export function process(id) {
|
|||
image,
|
||||
total: 0,
|
||||
legendary: [],
|
||||
pityCount: [],
|
||||
rarePity: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
rare: {
|
||||
character: [],
|
||||
|
@ -97,6 +98,7 @@ export function process(id) {
|
|||
let currentBannerIndex = -1;
|
||||
let hasManualInput = false;
|
||||
|
||||
let pity = 0;
|
||||
for (let i = 0; i < pullData.length; i++) {
|
||||
const pull = pullData[i];
|
||||
const next = pullData[i + 1] || { time: dayjs().year(2000).unix() };
|
||||
|
@ -128,6 +130,9 @@ export function process(id) {
|
|||
|
||||
selectedBanners[currentBannerIndex].total++;
|
||||
|
||||
const currentPity = selectedBanners[currentBannerIndex].pityCount[pity];
|
||||
selectedBanners[currentBannerIndex].pityCount[pity] = (currentPity || 0) + 1;
|
||||
|
||||
const newPull = {
|
||||
...pull,
|
||||
formattedTime: formatTime(pull.time),
|
||||
|
@ -136,6 +141,7 @@ export function process(id) {
|
|||
banner: currentBanner,
|
||||
start: startBanner,
|
||||
at: selectedBanners[currentBannerIndex].total,
|
||||
currentPity: ++pity,
|
||||
};
|
||||
|
||||
if (item.rarity === 5) {
|
||||
|
@ -146,6 +152,7 @@ export function process(id) {
|
|||
|
||||
selectedBanners[currentBannerIndex].legendary.push(newPull);
|
||||
allLegendary.push(newPull);
|
||||
pity = 0;
|
||||
} else if (item.rarity === 4) {
|
||||
allRare.push(newPull);
|
||||
selectedBanners[currentBannerIndex].rarePity[newPull.pity - 1]++;
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { process } from './wish';
|
||||
|
||||
const bannerCategories = ['beginners', 'standard', 'character-event', 'weapon-event'];
|
||||
const rareInclude = {
|
||||
300011: ['rosaria'],
|
||||
300012: ['yanfei', 'noelle', 'diona'],
|
||||
};
|
||||
|
||||
async function sendWish(data) {
|
||||
try {
|
||||
|
@ -14,7 +18,7 @@ async function sendWish(data) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function submitWishTally(lastPull) {
|
||||
export async function submitWishTally() {
|
||||
let prefixId = 0;
|
||||
for (const id of bannerCategories) {
|
||||
prefixId += 100000;
|
||||
|
@ -33,6 +37,9 @@ export async function submitWishTally(lastPull) {
|
|||
const total = banner[i].total;
|
||||
if (total === 0) continue;
|
||||
|
||||
const pityCount = [...banner[i].pityCount].map((e) => e || 0);
|
||||
if (pityCount.length > 90) continue;
|
||||
|
||||
const rarePity = banner[i].rarePity;
|
||||
const legendaryCount = banner[i].legendary.length;
|
||||
const rareCount = banner[i].rare.character.length + banner[i].rare.weapon.length;
|
||||
|
@ -46,15 +53,18 @@ export async function submitWishTally(lastPull) {
|
|||
5,
|
||||
]);
|
||||
|
||||
// rosaria only
|
||||
const rosariaPulls = banner[i].rare.character
|
||||
.filter((e) => e.id === 'rosaria')
|
||||
.map((e) => [e.time.toString(), e.id, e.type, e.pity, e.group === 'group', true, 4]);
|
||||
legendaryPulls.push(...rosariaPulls);
|
||||
// specific 4star include
|
||||
if (rareInclude[prefixId + i + 1]) {
|
||||
const includedRarePulls = banner[i].rare.character
|
||||
.filter((e) => rareInclude[prefixId + i + 1].includes(e.id))
|
||||
.map((e) => [e.time.toString(), e.id, e.type, e.pity, e.group === 'group', true, 4]);
|
||||
legendaryPulls.push(...includedRarePulls);
|
||||
}
|
||||
|
||||
console.log(legendaryPulls);
|
||||
console.log(rarePity);
|
||||
console.log(legendaryCount, rareCount, total);
|
||||
// console.log(pityCount);
|
||||
// console.log(legendaryPulls);
|
||||
// console.log(rarePity);
|
||||
// console.log(legendaryCount, rareCount, total);
|
||||
|
||||
await sendWish({
|
||||
firstPulls: firstFivePulls,
|
||||
|
@ -64,7 +74,7 @@ export async function submitWishTally(lastPull) {
|
|||
total,
|
||||
legendary: legendaryCount,
|
||||
rare: rareCount,
|
||||
lastPull,
|
||||
pityCount,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
"message": "Your best Genshin Impact companion! Help you plan what to farm with ascension calculator, also track your progress with todo and wish counter.",
|
||||
"banner": {
|
||||
"featured": [
|
||||
"Rosaria",
|
||||
"Tartaglia"
|
||||
"Zhongli",
|
||||
"Yanfei"
|
||||
],
|
||||
"summoned": "Summoned",
|
||||
"percentage": "from all {rarity}",
|
||||
|
@ -117,6 +117,10 @@
|
|||
"manualButton": "Enable Manual Input",
|
||||
"errorBanner": "Banner time missmatch! Please adjust your server on settings page. Still not working? please leave a message on Discord 😅",
|
||||
"globalWishTally": "Global Wish Tally",
|
||||
"pityTooltip": [
|
||||
"Shows your current {rarity} pity",
|
||||
"{count} pulls to guaranteed {rarity}"
|
||||
],
|
||||
"import": {
|
||||
"title": "Import Wish History",
|
||||
"faqsButton": "FAQ - READ FIRST",
|
||||
|
@ -300,7 +304,8 @@
|
|||
"totalThisBanner": "Total pull on this banner",
|
||||
"worth": "Worth",
|
||||
"loading": "Loading... (If this stuck, change your server on settings page)",
|
||||
"guaranteed": "The next 5★ is guaranteed to be the promotional character or weapon"
|
||||
"guaranteed": "The next 5★ is guaranteed to be the promotional character or weapon",
|
||||
"unknown_3_star": "Unknown"
|
||||
},
|
||||
"tally": {
|
||||
"title": "Wish Tally",
|
||||
|
@ -310,6 +315,7 @@
|
|||
"wonFiftyFifty": "won 50:50",
|
||||
"fromFiveStar": "from all 5★",
|
||||
"fromFourStar": "from all 4★",
|
||||
"fromFourStarFeatured": "from featured 4★",
|
||||
"show": "Show",
|
||||
"name": "Name",
|
||||
"total": "Total",
|
||||
|
@ -575,4 +581,4 @@
|
|||
"title": "Achievement",
|
||||
"of": "of"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,10 +17,6 @@
|
|||
"welcome": "Selamat Datang di Paimon.moe! 👋",
|
||||
"message": "Your best Genshin Impact companion! Membantu kamu merencanakan apa yang harus di farm dengan kalkulator ascension, juga catat progress mu dengan todo dan wish counter.",
|
||||
"banner": {
|
||||
"featured": [
|
||||
"Rosaria",
|
||||
"Tartaglia"
|
||||
],
|
||||
"summoned": "Pulang",
|
||||
"percentage": "dari semua {rarity}",
|
||||
"avg": "Pity rata-rata",
|
||||
|
@ -92,6 +88,10 @@
|
|||
"manualButton": "Nyalakan Input Manual",
|
||||
"errorBanner": "Waktu banner salah! Coba sesuaikan server di halaman settings. Masih gak bisa? tolong chat di Discord 😅",
|
||||
"globalWishTally": "Perhitungan Pity Wish Global",
|
||||
"pityTooltip": [
|
||||
"Ini adalah pity {rarity} sekarang",
|
||||
"{count}x wish lagi untuk dijamin {rarity}"
|
||||
],
|
||||
"import": {
|
||||
"title": "Import Riwayat Wish",
|
||||
"faqsButton": "FAQS - BACA DULU",
|
||||
|
@ -285,6 +285,7 @@
|
|||
"wonFiftyFifty": "menang 50:50",
|
||||
"fromFiveStar": "dari semua 5★",
|
||||
"fromFourStar": "dari semua 4★",
|
||||
"fromFourStarFeatured": "dari featured 4★",
|
||||
"show": "Tampilkan",
|
||||
"name": "Nama",
|
||||
"total": "Total",
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
"welcome": "Paimon.moe에 어서오세요! 👋",
|
||||
"message": "최고의 원신 동반자! 돌파 계산기로 파밍 계획을 도와주고, 할 일 목록과 기원 통계를 통해 진척도를 확인해줍니다.",
|
||||
"banner": {
|
||||
"featured": [
|
||||
"로자리아",
|
||||
"타르탈리아"
|
||||
],
|
||||
"summoned": "획득",
|
||||
"percentage": "(모든 {rarity} 중)",
|
||||
"avg": "평균 천장",
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
"welcome": "Добро пожаловать на Paimon.moe! 👋",
|
||||
"message": "Твой лучший компаньон в Genshin Impact! Поможет тебе распланировать что фармить в калькуляторе возвышения, а также отслеживать свой прогресс с помощью счетчика молитв и cписка дел",
|
||||
"banner": {
|
||||
"featured": [
|
||||
"Rosaria",
|
||||
"Tartaglia"
|
||||
],
|
||||
"summoned": "Призывов",
|
||||
"percentage": "из всех {rarity}",
|
||||
"avg": "Среднее число молитв",
|
||||
|
|
|
@ -13,21 +13,21 @@
|
|||
const dispatch = createEventDispatcher();
|
||||
|
||||
const featured = {
|
||||
rosaria: {
|
||||
rarity: 'rare',
|
||||
count: 0,
|
||||
average: '...',
|
||||
percentage: '...',
|
||||
},
|
||||
tartaglia: {
|
||||
zhongli: {
|
||||
rarity: 'legendary',
|
||||
count: 0,
|
||||
average: '...',
|
||||
percentage: '...',
|
||||
},
|
||||
yanfei: {
|
||||
rarity: 'rare',
|
||||
count: 0,
|
||||
average: '...',
|
||||
percentage: '...',
|
||||
},
|
||||
};
|
||||
const bannerId = 300011;
|
||||
const image = 'childerosaria.png';
|
||||
const bannerId = 300012;
|
||||
const image = 'zhongliyanfei.png';
|
||||
|
||||
let loading = true;
|
||||
let user = '';
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
|
||||
import Check from '../../components/Check.svelte';
|
||||
import Checkbox from '../../components/Checkbox.svelte';
|
||||
import { getAccountPrefix } from '../../stores/account';
|
||||
import { readSave, updateSave } from '../../stores/saveManager';
|
||||
|
||||
|
@ -29,6 +30,9 @@
|
|||
let obtainedPrimogem = 0;
|
||||
let categories = [];
|
||||
|
||||
let originalList = [];
|
||||
let sort = false;
|
||||
|
||||
function parseCategories() {
|
||||
categories = Object.entries(achievement).map(([id, data]) => ({
|
||||
id,
|
||||
|
@ -66,6 +70,28 @@
|
|||
}));
|
||||
}
|
||||
|
||||
function orderAchievement() {
|
||||
if (!sort) {
|
||||
if (originalList.length === 0) return;
|
||||
list = originalList;
|
||||
return;
|
||||
}
|
||||
|
||||
originalList = list.slice();
|
||||
list = list.sort((a, b) => {
|
||||
let first = a;
|
||||
let second = b;
|
||||
if (Array.isArray(a)) first = a[a.length - 1];
|
||||
if (Array.isArray(b)) second = b[b.length - 1];
|
||||
return first.checked === second.checked ? 0 : first.checked ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
function changeSort(val) {
|
||||
sort = val;
|
||||
orderAchievement();
|
||||
}
|
||||
|
||||
const saveData = debounce(() => {
|
||||
const data = JSON.stringify(checkList);
|
||||
|
||||
|
@ -93,6 +119,17 @@
|
|||
}
|
||||
});
|
||||
|
||||
if (sort) {
|
||||
originalList = list.slice();
|
||||
list = list.sort((a, b) => {
|
||||
let first = a;
|
||||
let second = b;
|
||||
if (Array.isArray(a)) first = a[a.length - 1];
|
||||
if (Array.isArray(b)) second = b[b.length - 1];
|
||||
return first.checked === second.checked ? 0 : first.checked ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
if (firstLoad) return;
|
||||
await tick();
|
||||
achievementContainer.scrollIntoView({
|
||||
|
@ -134,6 +171,31 @@
|
|||
const achievementData = readSave(`${prefix}achievement`);
|
||||
if (achievementData !== null) {
|
||||
checkList = JSON.parse(achievementData);
|
||||
migrateNewVersion();
|
||||
}
|
||||
}
|
||||
|
||||
function migrateNewVersion() {
|
||||
// version 1.5
|
||||
// prettier-ignore
|
||||
const movedIds = [
|
||||
84026, 84100, 84101, 84104, 84028,
|
||||
84107, 84102, 84108, 84105, 84103,
|
||||
84106, 84109, 84110, 84111, 84112,
|
||||
84113, 84114, 84115
|
||||
];
|
||||
|
||||
if (checkList['17'] === undefined) {
|
||||
checkList['17'] = {};
|
||||
}
|
||||
|
||||
if (checkList['0']) {
|
||||
for (const item of movedIds) {
|
||||
if (checkList['0'][item] === true) {
|
||||
checkList['17'][item] = true;
|
||||
delete checkList['0'][item];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,6 +230,9 @@
|
|||
<img src="/images/primogem.png" class="w-4 h-4 ml-1" alt="primogem" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:pl-4 text-white">
|
||||
<Checkbox checked={sort} on:change={() => changeSort(!sort)}>Show not achieved first</Checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col lg:flex-row space-y-3 lg:space-y-0 lg:space-x-3">
|
||||
<div class="flex flex-col space-y-2 lg:h-screen lg:overflow-auto lg:sticky lg:pr-1 pb-4 category">
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
const name = data.name;
|
||||
</script>
|
||||
|
||||
<div class="py-4 rounded-xl bg-item flex flex-col mb-4" style="{fade ? 'filter: grayscale(30%);' : ''}">
|
||||
<div class="flex mb-2 items-start px-4">
|
||||
<div class="py-4 rounded-xl bg-item flex flex-col mb-2" style="{fade ? 'filter: grayscale(30%);' : ''}">
|
||||
<div class="flex items-start px-4">
|
||||
<img src="/images/skills/{id}/{image}.png" alt={name} class="w-16 h-16 mr-4" />
|
||||
<div>
|
||||
<p class="font-black font-display text-xl">{name}</p>
|
||||
|
|
|
@ -247,7 +247,7 @@
|
|||
|
||||
<div class="lg:ml-64 pt-20 px-4 md:px-8 lg:pt-8">
|
||||
<div class="bg-item rounded-xl mb-4 p-4">
|
||||
<p class="text-white">{$t('settings.version')} <b>1.4</b></p>
|
||||
<p class="text-white">{$t('settings.version')} <b>1.5</b></p>
|
||||
</div>
|
||||
<div class="bg-item rounded-xl mb-4 p-4 flex flex-col">
|
||||
<p class="text-white">{$t('settings.multiple')}</p>
|
||||
|
|
|
@ -75,6 +75,11 @@
|
|||
index: i,
|
||||
...e,
|
||||
});
|
||||
} else if (e.type === 'unknown_3_star' && showRarity[2]) {
|
||||
sorted.push({
|
||||
index: i,
|
||||
...e,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,6 +233,7 @@
|
|||
id: 'unknown_3_star',
|
||||
time: dayjs().unix(),
|
||||
pity: 1,
|
||||
manualInput: true,
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
@ -278,7 +284,7 @@
|
|||
<div
|
||||
class={`${
|
||||
isEdit ? 'bg-item flex-col py-2' : 'bg-background flex-row items-center justify-center mb-2 p-4'
|
||||
} rounded-xl flex`}
|
||||
} rounded-xl flex relative`}
|
||||
>
|
||||
<span class="text-gray-200 whitespace-no-wrap flex-1">
|
||||
{$t('wish.lifetimePulls')}<br />
|
||||
|
@ -294,9 +300,16 @@
|
|||
<div
|
||||
class={`${
|
||||
isEdit ? 'bg-item flex-col py-2' : 'bg-background flex-row items-center justify-center mb-2 p-4'
|
||||
} rounded-xl flex`}
|
||||
title={`${legendaryPity - legendary} pulls to guaranteed 5 star`}
|
||||
} rounded-xl flex group relative`}
|
||||
>
|
||||
{#if !isEdit}
|
||||
<div class="group-hover:flex hidden absolute left-0 pl-2 items-center z-10" style="max-width: 80%;">
|
||||
<div class="bg-gray-200 text-gray-900 px-4 py-2 rounded-xl text-sm md:text-base">
|
||||
<p>{$t('wish.pityTooltip.0', { values: { rarity: '5★' } })}</p>
|
||||
<p>{$t('wish.pityTooltip.1', { values: { count: legendaryPity - legendary, rarity: '5★' } })}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<span class="text-gray-200 whitespace-no-wrap flex-1">
|
||||
5
|
||||
<Icon path={mdiStar} size={0.75} className="mb-1" />
|
||||
|
@ -310,9 +323,16 @@
|
|||
<div
|
||||
class={`${
|
||||
isEdit ? 'bg-item flex-col py-2' : 'bg-background flex-row items-center justify-center mb-2 p-4'
|
||||
} rounded-xl flex`}
|
||||
title={`${10 - rare} pulls to guaranteed 4 star`}
|
||||
} rounded-xl flex group relative`}
|
||||
>
|
||||
{#if !isEdit}
|
||||
<div class="group-hover:flex hidden absolute left-0 pl-2 items-center z-10" style="max-width: 80%;">
|
||||
<div class="bg-gray-200 text-gray-900 px-4 py-2 rounded-xl text-sm md:text-base">
|
||||
<p>{$t('wish.pityTooltip.0', { values: { rarity: '4★' } })}</p>
|
||||
<p>{$t('wish.pityTooltip.1', { values: { count: 10 - rare, rarity: '4★' } })}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<span class="text-gray-200 whitespace-no-wrap flex-1">
|
||||
4
|
||||
<Icon path={mdiStar} size={0.75} className="mb-1" />
|
||||
|
@ -370,6 +390,9 @@
|
|||
<button on:click={() => toggleShowRarity(2)} class={`pill normal ${showRarity[2] ? 'active' : ''}`}>
|
||||
3 <Icon path={mdiStar} size={0.75} className="mb-1" />
|
||||
</button>
|
||||
<a href="/wish/{id}" class="pill normal">
|
||||
<Icon path={mdiTableOfContents} color="white" />
|
||||
</a>
|
||||
</div>
|
||||
<table class="w-full">
|
||||
<tr>
|
||||
|
@ -378,7 +401,11 @@
|
|||
<th class="border-b border-gray-700 text-gray-400 font-display text-right">Pity</th>
|
||||
</tr>
|
||||
{#each sortedPull as pull}
|
||||
<tr on:click={manualInput ? () => openEditModal(pull.index, pull.type, pull.id, pull.time, pull.pity) : () => {}}>
|
||||
<tr
|
||||
on:click={manualInput
|
||||
? () => openEditModal(pull.index, pull.type, pull.id, pull.time, pull.pity)
|
||||
: () => {}}
|
||||
>
|
||||
{#if pull.type === 'character'}
|
||||
<td
|
||||
class={`border-b border-gray-700 py-1 pl-2 font-semibold ${
|
||||
|
@ -399,6 +426,8 @@
|
|||
: 'text-primary'
|
||||
}`}>{weaponList[pull.id].name}</td
|
||||
>
|
||||
{:else if pull.type === 'unknown_3_star'}
|
||||
<td class="border-b border-gray-700 py-1 pl-2 font-semibold text-primary">Unknown</td>
|
||||
{/if}
|
||||
<td class="border-b border-gray-700 text-sm py-1 px-2 whitespace-no-wrap" style="font-family: monospace;"
|
||||
>{dayjs.unix(pull.time).format('YYYY-MM-DD HH:mm:ss')}</td
|
||||
|
|
|
@ -104,6 +104,12 @@
|
|||
min: 0,
|
||||
max: 0,
|
||||
};
|
||||
|
||||
const rareInclude = {
|
||||
300011: ['rosaria'],
|
||||
300012: ['yanfei', 'noelle', 'diona'],
|
||||
};
|
||||
let promotedRarePercentage = 0;
|
||||
|
||||
let legendaryList = [];
|
||||
let legendaryPity = [];
|
||||
|
@ -137,14 +143,14 @@
|
|||
guaranteed: item.guaranteed,
|
||||
percentage:
|
||||
(item.count /
|
||||
(type === 'character' && id !== 200001 && i === 1 ? data.total.rare : data.total.legendary)) *
|
||||
(type === 'character' && id !== 200002 && i === 1 ? data.total.rare : data.total.legendary)) *
|
||||
100,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// only for standard banner
|
||||
if (id === 200001) {
|
||||
if (id === 200002) {
|
||||
const values = [0, 0];
|
||||
|
||||
for (let i = 0; i < data.list.length; i++) {
|
||||
|
@ -165,6 +171,23 @@
|
|||
];
|
||||
}
|
||||
|
||||
// only for zhongli banner upward
|
||||
if (id > 300011 && id < 400000) {
|
||||
const totalRare = data.list.reduce((prev, current) => {
|
||||
if (rareInclude[id].includes(current.name)) {
|
||||
prev.total += current.count;
|
||||
}
|
||||
if (featured[1] === current.name) {
|
||||
prev.featured = current.count;
|
||||
}
|
||||
return prev;
|
||||
}, {
|
||||
total: 0,
|
||||
featured: 0,
|
||||
});
|
||||
promotedRarePercentage = totalRare.featured / totalRare.total * 100
|
||||
}
|
||||
|
||||
legendary = {
|
||||
total: data.total.legendary,
|
||||
percentage: (data.total.legendary / data.total.all) * 100,
|
||||
|
@ -257,7 +280,7 @@
|
|||
class="flex flex-row items-center bg-background rounded-xl py-2 relative px-4 mb-2 flex-1 cursor-pointer"
|
||||
on:click={toggleLegendaryList}
|
||||
>
|
||||
{#if id === 200001}
|
||||
{#if id === 200002}
|
||||
<table class="flex-1">
|
||||
<tr>
|
||||
<td
|
||||
|
@ -310,13 +333,16 @@
|
|||
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
|
||||
</p>
|
||||
<p class="text-gray-400">
|
||||
{#if id === 300011 && i === 0}
|
||||
{#if id >= 300011 && id < 400000 && i === 0}
|
||||
{numberFormat.format(
|
||||
((featuredValues[i].total - featuredValues[i].guaranteed) /
|
||||
(data.total.legendary - featuredValues[i].guaranteed)) *
|
||||
100,
|
||||
)}%
|
||||
{$t('wish.tally.wonFiftyFifty')}
|
||||
{:else if id > 300011 && id < 400000 && i === 1}
|
||||
{numberFormat.format(promotedRarePercentage)}%
|
||||
{$t('wish.tally.fromFourStarFeatured')}
|
||||
{:else}
|
||||
{numberFormat.format(featuredValues[i].percentage)}% {$t(
|
||||
type === 'character' && i === 1 ? 'wish.tally.fromFourStar' : 'wish.tally.fromFiveStar',
|
||||
|
|
638
src/routes/wish/tally/_itemNew.svelte
Normal file
|
@ -0,0 +1,638 @@
|
|||
<script>
|
||||
import { t, locale } from 'svelte-i18n';
|
||||
import { mdiChevronDown, mdiLoading, mdiStar } from '@mdi/js';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import 'dayjs/locale/id';
|
||||
import 'dayjs/locale/en';
|
||||
import 'dayjs/locale/ru';
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
import Icon from '../../../components/Icon.svelte';
|
||||
import { characters } from '../../../data/characters';
|
||||
import { weaponList } from '../../../data/weaponList';
|
||||
|
||||
const numberFormat = Intl.NumberFormat('en', {
|
||||
maximumFractionDigits: 2,
|
||||
minimumFractionDigits: 0,
|
||||
});
|
||||
|
||||
const numberFormatSecondary = Intl.NumberFormat('en', {
|
||||
maximumFractionDigits: 1,
|
||||
minimumFractionDigits: 0,
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const legendaryMapping = {
|
||||
character: [
|
||||
0, 1, 1, 1, 1, 2, 2, 2, 2, 2, // 1
|
||||
3, 3, 3, 3, 3, 4, 4, 4, 4, 4, // 2
|
||||
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, // 3
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 5
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 6
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // 7
|
||||
11, 11, 11, 12, 13, 14, 15, 16, 17, 18, // 8
|
||||
19, 20, 20, 20, 20, 21, 21, 21, 21, 22, // 9
|
||||
],
|
||||
weapon: [
|
||||
0, 1, 1, 1, 1, 2, 2, 2, 2, 2, // 1
|
||||
3, 3, 3, 3, 3, 4, 4, 4, 4, 4, // 2
|
||||
5, 5, 5, 5, 5, 6, 6, 6, 6, 6, // 3
|
||||
7, 7, 7, 7, 7, 8, 8, 8, 8, 8, // 4
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 5
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // 6
|
||||
11, 11, 11, 12, 13, 14, 15, 16, 17, 18, // 7
|
||||
19, 20, 20, 20, 20, 21, 21, 21, 21, 22, // 8
|
||||
],
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
const legendaryLabels = {
|
||||
character: [
|
||||
1, [2, 5], [6, 10], [11, 15], [16, 20], [21, 25],
|
||||
[26, 30], [31, 40], [41, 50], [51, 60], [61, 70],
|
||||
[71, 73], 74, 75, 76, 77, 78, 79, 80, 81,
|
||||
[82, 85], [86, 89], 90,
|
||||
],
|
||||
weapon: [
|
||||
1, [2, 5], [6, 10], [11, 15], [16, 20], [21, 25],
|
||||
[26, 30], [31, 35], [36, 40], [41, 50], [51, 60],
|
||||
[61, 63], 64, 65, 66, 67, 68, 69, 70, 71,
|
||||
[72, 75], [76, 79], 80,
|
||||
],
|
||||
};
|
||||
|
||||
export let type;
|
||||
export let id;
|
||||
|
||||
const legendaryMap = legendaryMapping[type];
|
||||
const legendaryStep = legendaryLabels[type];
|
||||
|
||||
let openLegendaryList = false;
|
||||
let loading = true;
|
||||
let error = false;
|
||||
let data = {};
|
||||
|
||||
export let banner;
|
||||
export let featured = [];
|
||||
let featuredValues = featured.map(() => ({
|
||||
total: 0,
|
||||
percentage: 0,
|
||||
}));
|
||||
|
||||
let legendary = {
|
||||
total: 0,
|
||||
percentage: 0,
|
||||
};
|
||||
|
||||
let rare = {
|
||||
total: 0,
|
||||
percentage: 0,
|
||||
};
|
||||
|
||||
let rarePercentage = {
|
||||
min: 0,
|
||||
max: 0,
|
||||
};
|
||||
|
||||
let legendaryPercentage = {
|
||||
min: 0,
|
||||
max: 0,
|
||||
};
|
||||
|
||||
const rareInclude = {
|
||||
300011: ['rosaria'],
|
||||
300012: ['yanfei', 'noelle', 'diona'],
|
||||
};
|
||||
let promotedRarePercentage = 0;
|
||||
|
||||
let legendaryList = [];
|
||||
let legendaryPity = [];
|
||||
let rarePity = [];
|
||||
|
||||
let updateTime = '';
|
||||
locale.subscribe((val) => {
|
||||
dayjs.locale(val.substring(0, 2));
|
||||
updateTime = dayjs(data.time).fromNow();
|
||||
});
|
||||
|
||||
async function getData() {
|
||||
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' },
|
||||
});
|
||||
|
||||
data = await res.json();
|
||||
|
||||
for (let i = 0; i < featured.length; i++) {
|
||||
const feat = featured[i];
|
||||
const item = data.list.find((e) => e.name === feat);
|
||||
if (item) {
|
||||
featuredValues[i] = {
|
||||
total: item.count,
|
||||
guaranteed: item.guaranteed,
|
||||
percentage:
|
||||
(item.count /
|
||||
(type === 'character' && id !== 200002 && i === 1 ? data.total.rare : data.total.legendary)) *
|
||||
100,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// only for standard banner
|
||||
if (id === 200002) {
|
||||
const values = [0, 0];
|
||||
|
||||
for (let i = 0; i < data.list.length; i++) {
|
||||
const feat = data.list[i];
|
||||
const index = feat.type === 'character' ? 0 : 1;
|
||||
values[index] += feat.count;
|
||||
}
|
||||
|
||||
featuredValues = [
|
||||
{
|
||||
total: values[0],
|
||||
percentage: (values[0] / data.total.legendary) * 100,
|
||||
},
|
||||
{
|
||||
total: values[1],
|
||||
percentage: (values[1] / data.total.legendary) * 100,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// only for zhongli banner upward
|
||||
if (id > 300011 && id < 400000) {
|
||||
const totalRare = data.list.reduce(
|
||||
(prev, current) => {
|
||||
if (rareInclude[id].includes(current.name)) {
|
||||
prev.total += current.count;
|
||||
}
|
||||
if (featured[1] === current.name) {
|
||||
prev.featured = current.count;
|
||||
}
|
||||
return prev;
|
||||
},
|
||||
{
|
||||
total: 0,
|
||||
featured: 0,
|
||||
},
|
||||
);
|
||||
promotedRarePercentage = (totalRare.featured / totalRare.total) * 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,
|
||||
};
|
||||
|
||||
rarePity = data.pityCount.rare.map((e) => ({
|
||||
total: e,
|
||||
percentage: e / data.total.rare,
|
||||
}));
|
||||
|
||||
rarePity.forEach((e) => {
|
||||
if (rarePercentage.min > e.percentage) {
|
||||
rarePercentage.min = e.percentage;
|
||||
}
|
||||
if (rarePercentage.max < e.percentage) {
|
||||
rarePercentage.max = e.percentage;
|
||||
}
|
||||
});
|
||||
|
||||
const mapped = new Array(legendaryMap[legendaryMap.length - 1] + 1).fill(0);
|
||||
const legendaryPityData = data.pityCount.legendary.slice(1, 91);
|
||||
for (let i = 0; i < legendaryPityData.length; i++) {
|
||||
const label = legendaryMap[i];
|
||||
const pity = legendaryPityData[i];
|
||||
mapped[label] += pity;
|
||||
}
|
||||
|
||||
const percentageEachPity = new Array(90).fill(0);
|
||||
for (let i = 0; i < 90; i++) {
|
||||
percentageEachPity[i] = legendaryPityData[i] / data.countEachPity[i];
|
||||
}
|
||||
console.log(percentageEachPity);
|
||||
|
||||
const mappedPercentage = new Array(legendaryMap[legendaryMap.length - 1] + 1).fill(0);
|
||||
for (let i = 0; i < 90; i++) {
|
||||
const label = legendaryMap[i];
|
||||
const percentage = percentageEachPity[i];
|
||||
if (mappedPercentage[label] < percentage) {
|
||||
mappedPercentage[label] = percentage;
|
||||
}
|
||||
}
|
||||
|
||||
legendaryPity = mapped.map((e, i) => ({
|
||||
total: e,
|
||||
percentage: mappedPercentage[i],
|
||||
}));
|
||||
|
||||
legendaryList = data.list.sort((a, b) => {
|
||||
if (featured.includes(a.name) && featured.includes(b.name)) {
|
||||
return 0;
|
||||
} else if (featured.includes(a.name)) {
|
||||
return -1;
|
||||
} else if (featured.includes(b.name)) {
|
||||
return 1;
|
||||
} else {
|
||||
return b.count - a.count;
|
||||
}
|
||||
});
|
||||
|
||||
legendaryPity.forEach((e) => {
|
||||
if (legendaryPercentage.min > e.percentage) {
|
||||
legendaryPercentage.min = e.percentage;
|
||||
}
|
||||
if (legendaryPercentage.max < e.percentage) {
|
||||
legendaryPercentage.max = e.percentage;
|
||||
}
|
||||
});
|
||||
|
||||
updateTime = dayjs(data.time).fromNow();
|
||||
loading = false;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
loading = false;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
function mapVal(value, x1, y1, x2, y2) {
|
||||
return ((value - x1) * (y2 - x2)) / (y1 - x1) + x2;
|
||||
}
|
||||
|
||||
function toggleLegendaryList() {
|
||||
openLegendaryList = !openLegendaryList;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col bg-item rounded-xl px-4 pt-4 pb-2 mb-4 space-y-4">
|
||||
<div class="flex flex-col xl:flex-row">
|
||||
<img src="/images/banners/{banner.name} {banner.image}.png" alt={banner.name} class="rounded-xl xl:h-64 xl:mr-4" />
|
||||
<div class="h-4 xl:h-0" />
|
||||
{#if loading}
|
||||
<Icon className="m-4" path={mdiLoading} color="white" size={2} spin />
|
||||
{:else if !error}
|
||||
<div class="flex flex-col pity-summary" style="min-height: 16rem;">
|
||||
<div
|
||||
class="flex flex-row items-center bg-background rounded-xl py-2 relative px-4 mb-2 flex-1 cursor-pointer"
|
||||
on:click={toggleLegendaryList}
|
||||
>
|
||||
{#if id === 200002}
|
||||
<table class="flex-1">
|
||||
<tr>
|
||||
<td
|
||||
class="font-black text-white text-5xl leading-10 pr-2 text-center"
|
||||
style="padding-top: 4px; width: 1%;"
|
||||
>
|
||||
{featuredValues[0].total}
|
||||
</td>
|
||||
<td>
|
||||
<p class="text-white font-semibold">
|
||||
{$t('wish.tally.character')}
|
||||
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
|
||||
</p>
|
||||
<p class="text-gray-400">
|
||||
{numberFormat.format(featuredValues[0].percentage)}% {$t('wish.tally.fromFiveStar')}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="font-black text-white text-5xl leading-10 pr-2 text-center"
|
||||
style="padding-top: 4px; width: 1%;"
|
||||
>
|
||||
{featuredValues[1].total}
|
||||
</td>
|
||||
<td>
|
||||
<p class="text-white font-semibold">
|
||||
{$t('wish.tally.weapon')}
|
||||
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
|
||||
</p>
|
||||
<p class="text-gray-400">
|
||||
{numberFormat.format(featuredValues[1].percentage)}% {$t('wish.tally.fromFiveStar')}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
{:else}
|
||||
<table class="flex-1">
|
||||
{#each featured as feat, i}
|
||||
<tr>
|
||||
<td
|
||||
class="font-black text-white text-5xl leading-10 pr-2 text-center"
|
||||
style="padding-top: 4px; width: 1%;"
|
||||
>
|
||||
{featuredValues[i].total}
|
||||
</td>
|
||||
<td>
|
||||
<p class="text-white font-semibold">
|
||||
{type === 'character' ? characters[feat].name : weaponList[feat].name}
|
||||
<span class="text-gray-400">{$t('wish.tally.summoned')}</span>
|
||||
</p>
|
||||
<p class="text-gray-400">
|
||||
{#if id >= 300011 && id < 400000 && i === 0}
|
||||
{numberFormat.format(
|
||||
((featuredValues[i].total - featuredValues[i].guaranteed) /
|
||||
(data.total.legendary - featuredValues[i].guaranteed)) *
|
||||
100,
|
||||
)}%
|
||||
{$t('wish.tally.wonFiftyFifty')}
|
||||
{:else if id > 300011 && id < 400000 && i === 1}
|
||||
{numberFormat.format(promotedRarePercentage)}%
|
||||
{$t('wish.tally.fromFourStarFeatured')}
|
||||
{:else}
|
||||
{numberFormat.format(featuredValues[i].percentage)}% {$t(
|
||||
type === 'character' && i === 1 ? 'wish.tally.fromFourStar' : 'wish.tally.fromFiveStar',
|
||||
)}
|
||||
{/if}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
{/if}
|
||||
<div class="text-gray-400 chevron-detail">
|
||||
<Icon path={mdiChevronDown} />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-center bg-background rounded-xl px-4 py-2 relative mb-2 flex-1 text-legendary-from"
|
||||
>
|
||||
<p class="font-black mr-2 text-5xl leading-10" style="margin-top: 4px;">
|
||||
{numberFormat.format(legendary.percentage)}%
|
||||
</p>
|
||||
<div class="flex flex-col flex-1">
|
||||
<p class="font-semibold whitespace-no-wrap">
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
</p>
|
||||
<p>Total {numberFormat.format(legendary.total)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center bg-background rounded-xl px-4 py-2 relative flex-1 text-rare-from">
|
||||
<p class="font-black mr-2 text-5xl leading-10" style="margin-top: 4px;">
|
||||
{numberFormatSecondary.format(rare.percentage)}%
|
||||
</p>
|
||||
<div class="flex flex-col flex-1">
|
||||
<p class="font-semibold whitespace-no-wrap">
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
<Icon path={mdiStar} size={0.8} />
|
||||
</p>
|
||||
<p>Total {numberFormat.format(rare.total)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if openLegendaryList}
|
||||
<div transition:fade class="bg-background rounded-xl mt-2 xl:mt-0 py-2 px-4 xl:ml-2">
|
||||
<table class="text-white w-full">
|
||||
<tr>
|
||||
<td class="border-b border-gray-700 px-2">{$t('wish.tally.name')}</td>
|
||||
<td class="border-b border-gray-700 px-2 text-center">{$t('wish.tally.total')}</td>
|
||||
</tr>
|
||||
{#each legendaryList as item}
|
||||
<tr>
|
||||
<td class="border-b border-gray-700 px-2">
|
||||
{item.type === 'character' ? characters[item.name].name : weaponList[item.name].name}
|
||||
</td>
|
||||
<td class="border-b border-gray-700 px-2 text-center">{item.count}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
<p class="text-gray-400 mt-2 text-sm text-center">
|
||||
{$t('wish.tally.update')}
|
||||
{updateTime}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{#if !loading && !error}
|
||||
<div class="border border-background rounded-xl xl:hidden overflow-hidden">
|
||||
<table class="text-white w-full table-fixed">
|
||||
<tr>
|
||||
<th class="font-display text-gray-200 font-semibold px-2 pt-2">5★ {$t('wish.tally.pity')}</th>
|
||||
<th class="font-display text-gray-200 font-semibold border-l border-r border-background px-2 pt-2"
|
||||
>{$t('wish.tally.total')}</th
|
||||
>
|
||||
<th class="font-display text-gray-200 font-semibold px-2 pt-2">Chance%</th>
|
||||
</tr>
|
||||
{#each legendaryStep as label, i}
|
||||
<tr>
|
||||
<td class="text-center px-2 border-t border-background">
|
||||
{#if Array.isArray(label)}
|
||||
{label[0]} - {label[1]}
|
||||
{:else}
|
||||
{label}
|
||||
{/if}
|
||||
</td>
|
||||
<td class="text-center px-2 border-t border-l border-r border-background">
|
||||
{numberFormat.format(legendaryPity[i].total)}
|
||||
</td>
|
||||
<td
|
||||
class="text-center py-1 border-t border-background"
|
||||
style="background: rgba(25, 142, 81, {mapVal(
|
||||
legendaryPity[i].percentage,
|
||||
legendaryPercentage.min,
|
||||
legendaryPercentage.max,
|
||||
0,
|
||||
1,
|
||||
)}); width: 60px;"
|
||||
>
|
||||
{numberFormat.format(legendaryPity[i].percentage * 100)}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</div>
|
||||
<div class="border border-background rounded-xl xl:hidden overflow-hidden">
|
||||
<table class="text-white w-full table-fixed">
|
||||
<tr>
|
||||
<th class="font-display text-gray-200 font-semibold px-2 pt-2">4★ {$t('wish.tally.pity')}</th>
|
||||
<th class="font-display text-gray-200 font-semibold border-l border-r border-background px-2 pt-2"
|
||||
>{$t('wish.tally.total')}</th
|
||||
>
|
||||
<th class="font-display text-gray-200 font-semibold px-2 pt-2">%</th>
|
||||
</tr>
|
||||
{#each rarePity as pity, i}
|
||||
<tr>
|
||||
<td class="text-center px-2 border-t border-background">
|
||||
{i + 1}
|
||||
</td>
|
||||
<td class="text-center px-2 border-t border-l border-r border-background">
|
||||
{numberFormat.format(pity.total)}
|
||||
</td>
|
||||
<td
|
||||
class="text-center py-1 border-t border-background"
|
||||
style="background: rgba(25, 142, 81, {mapVal(
|
||||
pity.percentage,
|
||||
rarePercentage.min,
|
||||
rarePercentage.max,
|
||||
0,
|
||||
1,
|
||||
)}); width: 60px;"
|
||||
>
|
||||
{numberFormat.format(pity.percentage * 100)}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</div>
|
||||
<div class="border border-background rounded-xl hidden xl:block">
|
||||
<table class="text-white w-full table-fixed text-sm">
|
||||
<tr>
|
||||
<td
|
||||
class="font-display text-gray-200 font-semibold px-2 py-1 whitespace-no-wrap text-right border-b border-background"
|
||||
>
|
||||
5★<br />{$t('wish.tally.pity')}
|
||||
</td>
|
||||
{#each legendaryStep as label}
|
||||
<td class="text-center py-1 border-l border-b border-background">
|
||||
{#if Array.isArray(label)}
|
||||
<div>
|
||||
<span class="block">{label[0]}</span>
|
||||
<span class="block" style="height: 4px; overflow: hidden; line-height: 4px;">|</span>
|
||||
<span class="block">{label[1]}</span>
|
||||
</div>
|
||||
{:else}
|
||||
{label}
|
||||
{/if}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-b border-background">
|
||||
Total
|
||||
</td>
|
||||
{#each legendaryPity as pity}
|
||||
<td class="text-center py-1 border-l border-b border-background">{numberFormat.format(pity.total)}</td>
|
||||
{/each}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-background" title="Chance %">Chc%</td>
|
||||
{#each legendaryPity as pity}
|
||||
<td
|
||||
class="text-center py-1 border-l border-background"
|
||||
style="background: rgba(25, 142, 81, {mapVal(
|
||||
pity.percentage,
|
||||
legendaryPercentage.min,
|
||||
legendaryPercentage.max,
|
||||
0,
|
||||
1,
|
||||
)}); width: 60px;"
|
||||
>
|
||||
{numberFormat.format(pity.percentage * 100)}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flex flex-wrap">
|
||||
<div
|
||||
class="border border-background rounded-xl hidden xl:block overflow-hidden mr-4 mb-2"
|
||||
style="width: fit-content;"
|
||||
>
|
||||
<table class="text-white text-sm">
|
||||
<tr>
|
||||
<td
|
||||
class="font-display text-gray-200 font-semibold px-2 py-1 whitespace-no-wrap text-right border-b border-background"
|
||||
>
|
||||
4★ {$t('wish.tally.pity')}
|
||||
</td>
|
||||
{#each rarePity as _, i}
|
||||
<td class="text-center px-2 py-1 border-l border-b border-background">{i + 1}</td>
|
||||
{/each}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-b border-background"
|
||||
>Total</td
|
||||
>
|
||||
{#each rarePity as pity}
|
||||
<td class="text-center px-2 py-1 border-l border-b border-background"
|
||||
>{numberFormat.format(pity.total)}</td
|
||||
>
|
||||
{/each}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="font-display text-gray-200 font-semibold px-2 py-1 text-right border-background">%</td>
|
||||
{#each rarePity as pity}
|
||||
<td
|
||||
class="text-center px-2 py-1 border-l border-background"
|
||||
style="background: rgba(25, 142, 81, {mapVal(
|
||||
pity.percentage,
|
||||
rarePercentage.min,
|
||||
rarePercentage.max,
|
||||
0,
|
||||
1,
|
||||
)}); width: 60px;"
|
||||
>
|
||||
{numberFormat.format(pity.percentage * 100)}
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="flex flex-wrap text-white -mt-2 mb-2">
|
||||
<div class="space-y-2 flex flex-col flex-wrap mr-2 mt-2">
|
||||
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
|
||||
{$t('wish.tally.wishTotal')} <span class="font-semibold ml-2">{numberFormat.format(data.total.all)}</span>
|
||||
</div>
|
||||
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
|
||||
{$t('wish.tally.worth')} <img class="w-4 h-4 inline mx-1" src="/images/primogem.png" alt="primogem" />
|
||||
<span class="font-semibold">{numberFormat.format(data.total.all * 160)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2 flex flex-col flex-wrap mt-2">
|
||||
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
|
||||
{$t('wish.tally.median')}
|
||||
<span class="font-semibold ml-2">{numberFormat.format(data.median.legendary)}</span>
|
||||
</div>
|
||||
<div class="bg-background rounded-xl px-4 py-2 flex-1 flex items-center whitespace-no-wrap">
|
||||
{$t('wish.tally.user')} <span class="font-semibold ml-2">{numberFormat.format(data.total.users)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if error}
|
||||
<div class="text-white mt-4">{$t('wish.tally.error')}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@screen xl {
|
||||
.pity-summary {
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
.chevron-detail {
|
||||
@apply transform;
|
||||
@apply -rotate-90;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,14 @@
|
|||
<script>
|
||||
import { t } from 'svelte-i18n';
|
||||
import Button from '../../../components/Button.svelte';
|
||||
import Button from '../../../components/Button.svelte';
|
||||
import { banners } from '../../../data/banners';
|
||||
import Item from './_item.svelte';
|
||||
import ItemNew from './_itemNew.svelte';
|
||||
|
||||
let showOld = false;
|
||||
let showOld = [false, false];
|
||||
|
||||
function showOldTally() {
|
||||
showOld = true;
|
||||
function showOldTally(index) {
|
||||
showOld[index] = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -30,6 +31,8 @@ import Button from '../../../components/Button.svelte';
|
|||
</p>
|
||||
|
||||
<div class="px-4 md:px-8">
|
||||
<ItemNew type="character" banner={banners.characters[11]} id={300012} featured={['zhongli', 'yanfei']} />
|
||||
<ItemNew type="weapon" banner={banners.weapons[10]} id={400011} featured={['summit_shaper', 'memory_of_dust']} />
|
||||
<Item type="character" banner={banners.characters[10]} id={300011} featured={['tartaglia', 'rosaria']} />
|
||||
<Item
|
||||
type="weapon"
|
||||
|
@ -37,14 +40,36 @@ import Button from '../../../components/Button.svelte';
|
|||
id={400010}
|
||||
featured={['skyward_harp', 'lost_prayer_to_the_sacred_winds']}
|
||||
/>
|
||||
<Item type="character" banner={banners.characters[9]} id={300010} featured={['venti']} />
|
||||
<Item type="weapon" banner={banners.weapons[8]} id={400009} featured={['elegy_for_the_end', 'skyward_blade']} />
|
||||
<Item type="character" banner={banners.standard[0]} id={200001} />
|
||||
{#if showOld}
|
||||
<Item type="character" banner={banners.characters[8]} id={300009} featured={['hu_tao']} />
|
||||
<Item type="weapon" banner={banners.weapons[7]} id={400008} featured={['wolfs_gravestone', 'staff_of_homa']} />
|
||||
<Item type="character" banner={banners.standard[0]} id={200002} />
|
||||
{#if showOld[0]}
|
||||
<Item type="character" banner={banners.characters[9]} id={300010} featured={['venti']} />
|
||||
<Item
|
||||
type="weapon"
|
||||
banner={banners.weapons[8]}
|
||||
id={400009}
|
||||
featured={['skyward_harp', 'lost_prayer_to_the_sacred_winds']}
|
||||
/>
|
||||
{:else}
|
||||
<Button on:click={showOldTally}>{$t('wish.tally.show')} {banners.characters[8].name} & {banners.weapons[7].name}</Button>
|
||||
<Button on:click={() => showOldTally(0)}>
|
||||
{$t('wish.tally.show')}
|
||||
{banners.characters[9].name} & {banners.weapons[8].name}
|
||||
</Button>
|
||||
<div class="mb-2" />
|
||||
{/if}
|
||||
{#if showOld[1]}
|
||||
<Item type="character" banner={banners.characters[8]} id={300009} featured={['hu_tao']} />
|
||||
<Item
|
||||
type="weapon"
|
||||
banner={banners.weapons[7]}
|
||||
id={400008}
|
||||
featured={['wolfs_gravestone', 'staff_of_homa']}
|
||||
/>
|
||||
{:else}
|
||||
<Button on:click={() => showOldTally(1)}>
|
||||
{$t('wish.tally.show')}
|
||||
{banners.characters[8].name} & {banners.weapons[7].name}
|
||||
</Button>
|
||||
<div class="mb-2" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
BIN
static/images/characters/full/yanfei.png
Normal file
After Width: | Height: | Size: 394 KiB |
BIN
static/images/characters/yanfei.png
Normal file
After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 110 KiB |
BIN
static/images/events/energy_amplifier_initiation.jpg
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
static/images/events/to_the_stars_once_more.jpg
Normal file
After Width: | Height: | Size: 259 KiB |
BIN
static/images/home/zhongliyanfei.png
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
static/images/items/bloodjade_branch.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
static/images/skills/yanfei/constellation_1.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
static/images/skills/yanfei/constellation_2.png
Normal file
After Width: | Height: | Size: 9 KiB |
BIN
static/images/skills/yanfei/constellation_3.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
static/images/skills/yanfei/constellation_4.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
static/images/skills/yanfei/constellation_5.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
static/images/skills/yanfei/constellation_6.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
static/images/skills/yanfei/talent_1.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
static/images/skills/yanfei/talent_2.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
static/images/skills/yanfei/talent_3.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
static/images/skills/yanfei/talent_4.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
static/images/skills/yanfei/talent_5.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
static/images/skills/yanfei/talent_6.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
|
@ -54,6 +54,7 @@ module.exports = {
|
|||
borderColor: ['responsive', 'hover', 'focus', 'focus-within', 'disabled'],
|
||||
opacity: ['group-hover', 'responsive', 'hover', 'focus', 'disabled'],
|
||||
borderWidth: ['responsive', 'last', 'hover', 'focus'],
|
||||
display: ['responsive', 'group-hover'],
|
||||
},
|
||||
plugins: [],
|
||||
future: {
|
||||
|
|