Merge branch 'MadeBaruna:main' into main

pull/1/head
Klamist 2021-06-25 08:56:02 +08:00 committed by GitHub
commit 00e25cfcca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
185 changed files with 2073 additions and 844 deletions

View File

@ -108,7 +108,7 @@
{mobile}
{segment}
on:clicked={close}
active={['items', 'achievement', 'reminder', 'furnishing'].includes(segment)}
active={['items', 'achievement', 'reminder', 'furnishing', 'weapons'].includes(segment)}
image="/images/items.png"
label={$t('sidebar.database')}
items={[
@ -116,6 +116,7 @@
{ label: $t('sidebar.achievement'), href: '/achievement' },
{ label: $t('sidebar.reminder'), href: '/reminder' },
{ label: $t('sidebar.furnishing'), href: '/furnishing' },
{ label: $t('sidebar.weapons'), href: '/weapons' },
]}
/>
<SidebarItem

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -112,8 +112,9 @@ export const banners = {
start: '2021-03-17 06:00:00',
end: '2021-04-06 16:00:00',
color: '#35C297',
timezoneDependent: true,
featured: ['venti'],
featuredRare: ['sucrose', 'razor', 'noelle'],
timezoneDependent: true,
},
{
name: 'Farewell of Snezhnaya',
@ -123,6 +124,7 @@ export const banners = {
end: '2021-04-27 15:00:00',
color: '#50A3C0',
featured: ['tartaglia'],
featuredRare: ['rosaria', 'fischl', 'barbara'],
},
{
name: 'Gentry of Hermitage',
@ -142,8 +144,19 @@ export const banners = {
start: '2021-05-18 18:00:00',
end: '2021-06-08 15:00:00',
color: '#A6D6E0',
featuredRare: ['xingqiu', 'beidou', 'xinyan'],
featured: ['eula'],
featuredRare: ['xingqiu', 'beidou', 'xinyan'],
},
{
name: 'Sparkling Steps',
image: 2,
shortName: 'Klee',
start: '2021-06-09 06:00:00',
end: '2021-06-29 17:59:59',
color: '#CA360E',
featured: ['klee'],
featuredRare: ['fischl', 'sucrose', 'barbara'],
timezoneDependent: true,
},
],
weapons: [
@ -232,6 +245,7 @@ export const banners = {
color: '#f54e42',
timezoneDependent: true,
featured: ['elegy_for_the_end', 'skyward_blade'],
featuredRare: ['the_alley_flash', 'wine_and_song', 'favonius_greatsword', 'favonius_warbow', 'dragons_bane'],
},
{
name: 'Epitome Invocation',
@ -241,6 +255,7 @@ export const banners = {
shortName: 'Skyward',
color: '#f5c242',
featured: ['skyward_harp', 'lost_prayer_to_the_sacred_winds'],
featuredRare: ['alley_hunter', 'favonius_codex', 'favonius_lance', 'sacrificial_greatsword', 'favonius_sword'],
},
{
name: 'Epitome Invocation',
@ -248,9 +263,10 @@ export const banners = {
start: '2021-04-28 06:00:00',
end: '2021-05-18 17:59:59',
shortName: 'Summit',
color: '#f54e42',
color: '#f5ef42',
timezoneDependent: true,
featured: ['summit_shaper', 'memory_of_dust'],
featuredRare: ['lithic_blade', 'lithic_spear', 'sacrificial_bow', 'eye_of_perception', 'the_flute'],
},
{
name: 'Epitome Invocation',
@ -258,8 +274,20 @@ export const banners = {
start: '2021-05-18 18:00:00',
end: '2021-06-08 15:00:00',
shortName: 'Pines',
color: '#f5c242',
color: '#7ef542',
featured: ['song_of_broken_pines', 'aquila_favonia'],
featuredRare: ['rust', 'sacrificial_fragments', 'dragons_bane', 'rainslasher', 'sacrificial_sword'],
},
{
name: 'Epitome Invocation',
image: 13,
start: '2021-06-09 06:00:00',
end: '2021-06-29 17:59:59',
shortName: 'Lost Prayer',
color: '#42ecf5',
featured: ['lost_prayer_to_the_sacred_winds', 'skyward_pride'],
featuredRare: ['mitternachts_waltz', 'lions_roar', 'the_bell', 'favonius_lance', 'the_widsith'],
timezoneDependent: true,
},
],
};

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Wall","type":"interior"},{"category":"Flooring","name":"Flooring","type":"interior"},{"category":"Ceiling","name":"Ceiling","type":"interior"},{"category":"Ceiling Lamp","name":"Ceiling Lamp","type":"interior"},{"category":"Room Door","name":"Room Door","type":"interior"},{"category":"Stairs","name":"Stairs","type":"interior"},{"category":"Cabinet","name":"Cabinet","type":"interior"},{"category":"Bookcase","name":"Bookcase","type":"interior"},{"category":"Table","name":"Table","type":"interior"},{"category":"Counter","name":"Counter","type":"interior"},{"category":"Bed","name":"Bed","type":"interior"},{"category":"Seating","name":"Seating","type":"interior"},{"category":"Ornament","name":"Ornament","type":"interior"},{"category":"Carpet","name":"Carpet","type":"interior"},{"category":"Lighting","name":"Lighting","type":"interior"},{"category":"Potted Plant","name":"Potted Plant","type":"interior"},{"category":"Utensil","name":"Utensil","type":"interior"},{"category":"Artwork","name":"Artwork","type":"interior"},{"category":"Hanging Ornament","name":"Hanging Ornament","type":"interior"},{"category":"Courtyard Wall","name":"Courtyard Wall","type":"exterior"},{"category":"Large Ornament","name":"Large Ornament","type":"exterior"},{"category":"Liyue","name":"Liyue","type":"exterior"},{"category":"Mondstadt","name":"Mondstadt","type":"exterior"},{"category":"Hilichurl Style","name":"Hilichurl Style","type":"exterior"},{"category":"Mercantile","name":"Mercantile","type":"exterior"},{"category":"Free Booth","name":"Free Booth","type":"exterior"},{"category":"Mountain","name":"Mountain","type":"exterior"},{"category":"Rock","name":"Rock","type":"exterior"},{"category":"Tree","name":"Tree","type":"exterior"},{"category":"Shrub","name":"Shrub","type":"exterior"},{"category":"Item","name":"Item","type":"exterior"},{"category":"Terrace","name":"Terrace","type":"exterior"},{"category":"Ornament","name":"Ornament","type":"exterior"},{"category":"Lighting","name":"Lighting","type":"exterior"},{"category":"Indoor Creature","name":"Indoor Creature","type":"interior"},{"category":"Outdoor Creature","name":"Outdoor Creature","type":"exterior"},{"category":"Mansion","name":"Mansion","type":"exterior"},{"category":"Fence","name":"Fence","type":"exterior"},{"category":"Seating","name":"Seating","type":"exterior"},{"category":"Table","name":"Table","type":"exterior"},{"category":"Cabinet","name":"Cabinet","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Murs","type":"interior"},{"category":"Flooring","name":"Sols","type":"interior"},{"category":"Ceiling","name":"Plafonds","type":"interior"},{"category":"Ceiling Lamp","name":"Lustres","type":"interior"},{"category":"Room Door","name":"Portes","type":"interior"},{"category":"Stairs","name":"Escaliers","type":"interior"},{"category":"Cabinet","name":"Rangements","type":"interior"},{"category":"Bookcase","name":"Bibliothèques","type":"interior"},{"category":"Table","name":"Tables","type":"interior"},{"category":"Counter","name":"Comptoirs","type":"interior"},{"category":"Bed","name":"Lits","type":"interior"},{"category":"Seating","name":"Chaises","type":"interior"},{"category":"Ornament","name":"Objets d'agrément","type":"interior"},{"category":"Carpet","name":"Tapis","type":"interior"},{"category":"Lighting","name":"Éclairages","type":"interior"},{"category":"Potted Plant","name":"Plantes en pot","type":"interior"},{"category":"Utensil","name":"Objets divers","type":"interior"},{"category":"Artwork","name":"Œuvres d'art","type":"interior"},{"category":"Hanging Ornament","name":"Éléments suspendus","type":"interior"},{"category":"Courtyard Wall","name":"Murs de cour","type":"exterior"},{"category":"Large Ornament","name":"Grands ornements","type":"exterior"},{"category":"Liyue","name":"Style liyuéen","type":"exterior"},{"category":"Mondstadt","name":"Style mondstadtois","type":"exterior"},{"category":"Hilichurl Style","name":"Style Brutocollinus","type":"exterior"},{"category":"Mercantile","name":"Étals de marchands","type":"exterior"},{"category":"Free Booth","name":"Étals libres","type":"exterior"},{"category":"Mountain","name":"Reliefs","type":"exterior"},{"category":"Rock","name":"Roches","type":"exterior"},{"category":"Tree","name":"Arbres","type":"exterior"},{"category":"Shrub","name":"Buissons","type":"exterior"},{"category":"Item","name":"Objets divers","type":"exterior"},{"category":"Terrace","name":"Végétation en pot","type":"exterior"},{"category":"Ornament","name":"Aménagements divers","type":"exterior"},{"category":"Lighting","name":"Éclairages","type":"exterior"},{"category":"Indoor Creature","name":"Animaux d'intérieur","type":"interior"},{"category":"Outdoor Creature","name":"Animaux d'extérieur","type":"exterior"},{"category":"Mansion","name":"Résidences","type":"exterior"},{"category":"Fence","name":"Clôtures","type":"exterior"},{"category":"Seating","name":"Chaises","type":"exterior"},{"category":"Table","name":"Tables","type":"exterior"},{"category":"Cabinet","name":"Rangements","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Dinding","type":"interior"},{"category":"Flooring","name":"Lantai","type":"interior"},{"category":"Ceiling","name":"Langit-Langit","type":"interior"},{"category":"Ceiling Lamp","name":"Lampu Gantung","type":"interior"},{"category":"Room Door","name":"Pintu","type":"interior"},{"category":"Stairs","name":"Tangga","type":"interior"},{"category":"Cabinet","name":"Kabinet","type":"interior"},{"category":"Bookcase","name":"Rak Buku","type":"interior"},{"category":"Table","name":"Meja","type":"interior"},{"category":"Counter","name":"Konter","type":"interior"},{"category":"Bed","name":"Ranjang","type":"interior"},{"category":"Seating","name":"Kursi","type":"interior"},{"category":"Ornament","name":"Ornamen","type":"interior"},{"category":"Carpet","name":"Karpet","type":"interior"},{"category":"Lighting","name":"Lampu","type":"interior"},{"category":"Potted Plant","name":"Tanaman Pot","type":"interior"},{"category":"Utensil","name":"Perkakas","type":"interior"},{"category":"Artwork","name":"Karya Seni","type":"interior"},{"category":"Hanging Ornament","name":"Hiasan Gantung","type":"interior"},{"category":"Courtyard Wall","name":"Dinding Halaman","type":"exterior"},{"category":"Large Ornament","name":"Ornamen Besar","type":"exterior"},{"category":"Liyue","name":"Model Liyue","type":"exterior"},{"category":"Mondstadt","name":"Model Mondstadt","type":"exterior"},{"category":"Hilichurl Style","name":"Gaya Hilichurl","type":"exterior"},{"category":"Mercantile","name":"Model Perdagangan","type":"exterior"},{"category":"Free Booth","name":"Stan Cuma-Cuma","type":"exterior"},{"category":"Mountain","name":"Gunung","type":"exterior"},{"category":"Rock","name":"Batu","type":"exterior"},{"category":"Tree","name":"Pohon","type":"exterior"},{"category":"Shrub","name":"Semak Belukar","type":"exterior"},{"category":"Item","name":"Benda","type":"exterior"},{"category":"Terrace","name":"Teras Berbunga","type":"exterior"},{"category":"Ornament","name":"Ornamen","type":"exterior"},{"category":"Lighting","name":"Lampu","type":"exterior"},{"category":"Indoor Creature","name":"Makhluk Dalam Ruangan","type":"interior"},{"category":"Outdoor Creature","name":"Makhluk Luar Ruangan","type":"exterior"},{"category":"Mansion","name":"Rumah Mewah","type":"exterior"},{"category":"Fence","name":"Pagar","type":"exterior"},{"category":"Seating","name":"Kursi","type":"exterior"},{"category":"Table","name":"Meja","type":"exterior"},{"category":"Cabinet","name":"Kabinet","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"벽지","type":"interior"},{"category":"Flooring","name":"바닥","type":"interior"},{"category":"Ceiling","name":"천장","type":"interior"},{"category":"Ceiling Lamp","name":"조명","type":"interior"},{"category":"Room Door","name":"문","type":"interior"},{"category":"Stairs","name":"계단","type":"interior"},{"category":"Cabinet","name":"수납장","type":"interior"},{"category":"Bookcase","name":"책장","type":"interior"},{"category":"Table","name":"탁자","type":"interior"},{"category":"Counter","name":"카운터","type":"interior"},{"category":"Bed","name":"침대","type":"interior"},{"category":"Seating","name":"의자","type":"interior"},{"category":"Ornament","name":"장식","type":"interior"},{"category":"Carpet","name":"카펫","type":"interior"},{"category":"Lighting","name":"조명","type":"interior"},{"category":"Potted Plant","name":"분재","type":"interior"},{"category":"Utensil","name":"기물","type":"interior"},{"category":"Artwork","name":"그림","type":"interior"},{"category":"Hanging Ornament","name":"인테리어 모빌","type":"interior"},{"category":"Courtyard Wall","name":"담장","type":"exterior"},{"category":"Large Ornament","name":"대형 장식","type":"exterior"},{"category":"Liyue","name":"리월 테마","type":"exterior"},{"category":"Mondstadt","name":"몬드 테마","type":"exterior"},{"category":"Hilichurl Style","name":"츄츄 테마","type":"exterior"},{"category":"Mercantile","name":"상가 테마","type":"exterior"},{"category":"Free Booth","name":"자유 가판대","type":"exterior"},{"category":"Mountain","name":"산","type":"exterior"},{"category":"Rock","name":"암석","type":"exterior"},{"category":"Tree","name":"교목","type":"exterior"},{"category":"Shrub","name":"관목","type":"exterior"},{"category":"Item","name":"물품","type":"exterior"},{"category":"Terrace","name":"화단","type":"exterior"},{"category":"Ornament","name":"장식","type":"exterior"},{"category":"Lighting","name":"조명","type":"exterior"},{"category":"Indoor Creature","name":"실내 동물","type":"interior"},{"category":"Outdoor Creature","name":"실외 동물","type":"exterior"},{"category":"Mansion","name":"저택","type":"exterior"},{"category":"Fence","name":"울타리","type":"exterior"},{"category":"Seating","name":"의자","type":"exterior"},{"category":"Table","name":"탁자","type":"exterior"},{"category":"Cabinet","name":"수납장","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Parede","type":"interior"},{"category":"Flooring","name":"Assoalho","type":"interior"},{"category":"Ceiling","name":"Teto","type":"interior"},{"category":"Ceiling Lamp","name":"Luminária","type":"interior"},{"category":"Room Door","name":"Portas","type":"interior"},{"category":"Stairs","name":"Escadas","type":"interior"},{"category":"Cabinet","name":"Armário","type":"interior"},{"category":"Bookcase","name":"Estante","type":"interior"},{"category":"Table","name":"Mesa","type":"interior"},{"category":"Counter","name":"Balcão","type":"interior"},{"category":"Bed","name":"Cama","type":"interior"},{"category":"Seating","name":"Cadeira","type":"interior"},{"category":"Ornament","name":"Ornamentos","type":"interior"},{"category":"Carpet","name":"Carpete","type":"interior"},{"category":"Lighting","name":"Iluminação","type":"interior"},{"category":"Potted Plant","name":"Planta em Vaso","type":"interior"},{"category":"Utensil","name":"Utensílio","type":"interior"},{"category":"Artwork","name":"Obra de Arte","type":"interior"},{"category":"Hanging Ornament","name":"Ornamento Suspenso","type":"interior"},{"category":"Courtyard Wall","name":"Muro do Pátio","type":"exterior"},{"category":"Large Ornament","name":"Ornamento Largo","type":"exterior"},{"category":"Liyue","name":"Estilo de Liyue","type":"exterior"},{"category":"Mondstadt","name":"Estilo de Mondstadt","type":"exterior"},{"category":"Hilichurl Style","name":"Estilo de Hilichurls","type":"exterior"},{"category":"Mercantile","name":"Estilo de Comercial","type":"exterior"},{"category":"Free Booth","name":"Estande Livre","type":"exterior"},{"category":"Mountain","name":"Montanha","type":"exterior"},{"category":"Rock","name":"Selo de Pedra","type":"exterior"},{"category":"Tree","name":"Arborea","type":"exterior"},{"category":"Shrub","name":"Arbusto","type":"exterior"},{"category":"Item","name":"Item","type":"exterior"},{"category":"Terrace","name":"Terraço Florido","type":"exterior"},{"category":"Ornament","name":"Ornamentos","type":"exterior"},{"category":"Lighting","name":"Iluminação","type":"exterior"},{"category":"Indoor Creature","name":"Criaturas Domésticas","type":"interior"},{"category":"Outdoor Creature","name":"Criaturas Silvestres","type":"exterior"},{"category":"Mansion","name":"Residência","type":"exterior"},{"category":"Fence","name":"Cercas","type":"exterior"},{"category":"Seating","name":"Cadeira","type":"exterior"},{"category":"Table","name":"Mesa","type":"exterior"},{"category":"Cabinet","name":"Armário","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"Стены","type":"interior"},{"category":"Flooring","name":"Полы","type":"interior"},{"category":"Ceiling","name":"Потолки","type":"interior"},{"category":"Ceiling Lamp","name":"Люстры","type":"interior"},{"category":"Room Door","name":"Двери","type":"interior"},{"category":"Stairs","name":"Лестницы","type":"interior"},{"category":"Cabinet","name":"Шкафы","type":"interior"},{"category":"Bookcase","name":"Книжные полки","type":"interior"},{"category":"Table","name":"Столы","type":"interior"},{"category":"Counter","name":"Стойки","type":"interior"},{"category":"Bed","name":"Кровати","type":"interior"},{"category":"Seating","name":"Стулья","type":"interior"},{"category":"Ornament","name":"Украшения","type":"interior"},{"category":"Carpet","name":"Ковры","type":"interior"},{"category":"Lighting","name":"Освещение","type":"interior"},{"category":"Potted Plant","name":"Цветки","type":"interior"},{"category":"Utensil","name":"Утварь","type":"interior"},{"category":"Artwork","name":"Картины","type":"interior"},{"category":"Hanging Ornament","name":"Подвесные украшения","type":"interior"},{"category":"Courtyard Wall","name":"Ограды","type":"exterior"},{"category":"Large Ornament","name":"Крупные украшения","type":"exterior"},{"category":"Liyue","name":"Стиль Ли Юэ","type":"exterior"},{"category":"Mondstadt","name":"Стиль Мондштадта","type":"exterior"},{"category":"Hilichurl Style","name":"Стиль хиличурлов","type":"exterior"},{"category":"Mercantile","name":"Купеческий стиль","type":"exterior"},{"category":"Free Booth","name":"Свободный ларёк","type":"exterior"},{"category":"Mountain","name":"Скалы","type":"exterior"},{"category":"Rock","name":"Камни","type":"exterior"},{"category":"Tree","name":"Деревья","type":"exterior"},{"category":"Shrub","name":"Кусты","type":"exterior"},{"category":"Item","name":"Предметы","type":"exterior"},{"category":"Terrace","name":"Клумбы","type":"exterior"},{"category":"Ornament","name":"Украшения","type":"exterior"},{"category":"Lighting","name":"Лампы","type":"exterior"},{"category":"Indoor Creature","name":"Домашние животные","type":"interior"},{"category":"Outdoor Creature","name":"Домашние животные","type":"exterior"},{"category":"Mansion","name":"Дом","type":"exterior"},{"category":"Fence","name":"Изгороди","type":"exterior"},{"category":"Seating","name":"Стулья","type":"exterior"},{"category":"Table","name":"Столы","type":"exterior"},{"category":"Cabinet","name":"Шкафы","type":"exterior"}]

View File

@ -0,0 +1 @@
[{"category":"Wall","name":"墙面","type":"interior"},{"category":"Flooring","name":"地板","type":"interior"},{"category":"Ceiling","name":"天花板","type":"interior"},{"category":"Ceiling Lamp","name":"吊灯","type":"interior"},{"category":"Room Door","name":"房门","type":"interior"},{"category":"Stairs","name":"楼梯","type":"interior"},{"category":"Cabinet","name":"柜子","type":"interior"},{"category":"Bookcase","name":"书柜","type":"interior"},{"category":"Table","name":"桌子","type":"interior"},{"category":"Counter","name":"柜台","type":"interior"},{"category":"Bed","name":"床","type":"interior"},{"category":"Seating","name":"椅子","type":"interior"},{"category":"Ornament","name":"饰品","type":"interior"},{"category":"Carpet","name":"地毯","type":"interior"},{"category":"Lighting","name":"灯具","type":"interior"},{"category":"Potted Plant","name":"盆景","type":"interior"},{"category":"Utensil","name":"器物","type":"interior"},{"category":"Artwork","name":"字画","type":"interior"},{"category":"Hanging Ornament","name":"挂饰","type":"interior"},{"category":"Courtyard Wall","name":"院墙","type":"exterior"},{"category":"Large Ornament","name":"大型饰品","type":"exterior"},{"category":"Liyue","name":"璃月风格","type":"exterior"},{"category":"Mondstadt","name":"蒙德风格","type":"exterior"},{"category":"Hilichurl Style","name":"丘丘风格","type":"exterior"},{"category":"Mercantile","name":"商户风格","type":"exterior"},{"category":"Free Booth","name":"自由摊位","type":"exterior"},{"category":"Mountain","name":"山体","type":"exterior"},{"category":"Rock","name":"岩石","type":"exterior"},{"category":"Tree","name":"乔木","type":"exterior"},{"category":"Shrub","name":"灌木","type":"exterior"},{"category":"Item","name":"物件","type":"exterior"},{"category":"Terrace","name":"花坛","type":"exterior"},{"category":"Ornament","name":"饰品","type":"exterior"},{"category":"Lighting","name":"灯具","type":"exterior"},{"category":"Indoor Creature","name":"室内动物","type":"interior"},{"category":"Outdoor Creature","name":"室外动物","type":"exterior"},{"category":"Mansion","name":"宅邸","type":"exterior"},{"category":"Fence","name":"围栏","type":"exterior"},{"category":"Seating","name":"椅子","type":"exterior"},{"category":"Table","name":"桌子","type":"exterior"},{"category":"Cabinet","name":"柜子","type":"exterior"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,38 +1,5 @@
export const eventsData = [
[
{
name: 'A Wanmin Welcome - Web Event',
pos: '0% 45%',
image: 'a_wanmin_welcome.png',
start: '2021-03-16 13:00:00',
end: '2021-03-25 23:59:00',
color: '#FAE2B4',
url:
'https://webstatic-sea.mihoyo.com/ys/event/e20210316cooking-sea/index.html?lang=en-us?utm_source=hoyolab&utm_medium=banner',
showOnHome: true,
},
{
name: 'A Thousand Questions With Paimon',
pos: '60% 30%',
image: 'a_thousand_questions_with_paimon.jpg',
start: '2021-04-06 13:00:00',
end: '2021-04-08 23:59:59',
color: '#EFDEB4',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/9694',
showOnHome: true,
},
{
name: 'Hilidream Camp',
pos: '0% 58%',
image: 'hilidream_camp.png',
start: '2021-04-21 13:00:00',
end: '2021-04-27 23:59:59',
color: '#FAF8EB',
zoom: '180%',
url: 'https://webstatic-sea.mihoyo.com/ys/event/e20210421-homeland/index.html',
showOnHome: true,
},
{
name: 'To the Stars Once More',
pos: '20% 15%',
@ -45,51 +12,17 @@ export const eventsData = [
showOnHome: true,
},
{
name: 'Update 1.6!',
pos: '0% 40%',
image: 'update16.jpg',
start: '2021-06-09 10:00:00',
end: '2021-06-16 10:00:00',
color: '#3B8AFF',
zoom: '140%',
url: 'https://www.youtube.com/watch?v=OrZ8RbXwoK4',
name: 'Legend of the Vagabond Sword',
pos: '0% 45%',
image: 'legend_of_the_vagabond.jpg',
start: '2021-06-25 10:00:00',
end: '2021-07-08 03:59:59',
color: '#7A92FF',
zoom: '180%',
showOnHome: true,
},
],
[
{
name: 'Outland Gastronomy - Daily Login Event',
pos: '0% 50%',
image: 'outland_gastronomy.jpg',
start: '2021-03-17 06:00:00',
end: '2021-04-01 04:00:00',
color: '#DDD7E8',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/9262',
showOnHome: true,
},
{
name: 'Contending Tides Event',
pos: '0% 10%',
image: 'contending_tides.jpg',
start: '2021-04-02 10:00:00',
end: '2021-04-12 04:00:00',
color: '#6C99F7',
zoom: '180%',
url: 'https://www.hoyolab.com/genshin/article/275307',
showOnHome: true,
},
{
name: 'Marvelous Merchandise',
pos: '0% 60%',
image: 'marvelous_merchandise_event.jpg',
start: '2021-04-16 10:00:00',
end: '2021-04-23 04:00:00',
color: '#93E05A',
zoom: '170%',
url: 'https://www.hoyolab.com/genshin/article/295568',
showOnHome: true,
},
{
name: 'Windtrace',
pos: '0% 80%',
@ -106,36 +39,26 @@ export const eventsData = [
pos: '0% 35%',
image: 'mimi_tomo_update.jpg',
start: '2021-05-27 10:00:00',
end: '2021-06-08 04:00:00',
end: '2021-06-06 04:00:00',
color: '#E5C18B',
zoom: '200%',
url: 'https://www.hoyolab.com/genshin/article/387817',
showOnHome: true,
},
{
name: 'Echoing Tales',
pos: '0% 15%',
image: 'echoing_tales.png',
start: '2021-06-09 11:00:00',
end: '2021-07-21 15:00:00',
color: '#90CEF5',
zoom: '230%',
url: 'https://www.hoyolab.com/genshin/article/415684',
showOnHome: true,
timezoneDependent: true,
},
],
[
{
name: 'Invitation of Windblume - 1.4 Event',
pos: '0% 20%',
image: 'update14.png',
start: '2021-03-19 10:00:00',
end: '2021-04-05 04:00:00',
color: '#79D2EB',
zoom: '120%',
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
showOnHome: true,
},
{
name: 'Wishful Drops - Oceanid Event',
pos: '0% 20%',
image: 'wishful_drops.jpg',
start: '2021-04-09 10:00:00',
end: '2021-04-16 04:00:00',
color: '#579DE5',
zoom: '170%',
url: 'https://www.hoyolab.com/genshin/article/286280',
showOnHome: true,
},
{
name: 'Energy Amplifier Initiation',
pos: '0% 25%',
@ -169,51 +92,20 @@ export const eventsData = [
url: 'https://www.hoyolab.com/genshin/article/394841',
showOnHome: true,
},
{
name: 'Midsummer Island Adventure',
pos: '50% 35%',
image: 'midsummer_island_adventure.png',
start: '2021-06-09 11:00:00',
end: '2021-06-28 04:00:00',
color: '#63A3F6',
zoom: '200%',
url: 'https://www.hoyolab.com/genshin/article/415687',
showOnHome: true,
timezoneDependent: true,
},
],
[
{
name: 'Act I',
pos: '0% 20%',
start: '2021-03-19 10:00:00',
end: '2021-03-22 04:00:00',
color: '#79D2EB',
zoom: '120%',
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
startOnly: true,
},
{
name: 'Act II',
pos: '0% 20%',
image: '',
start: '2021-03-22 04:00:00',
end: '2021-03-25 04:00:00',
color: '#79D2EB',
zoom: '120%',
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
startOnly: true,
},
{
name: 'Act III',
pos: '0% 20%',
image: '',
start: '2021-03-25 04:00:00',
end: '2021-03-28 04:00:00',
color: '#79D2EB',
zoom: '120%',
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
startOnly: true,
},
{
name: 'Act IV',
pos: '0% 20%',
image: 'update14.png',
start: '2021-03-28 04:00:00',
end: '2021-04-05 04:00:00',
color: '#79D2EB',
zoom: '120%',
url: 'https://genshin.mihoyo.com/en/news/detail/9407',
startOnly: true,
},
{
name: 'Act I',
start: '2021-04-30 10:00:00',
@ -241,28 +133,45 @@ export const eventsData = [
image: 'energy_amplifier_initiation.jpg',
startOnly: true,
},
{
name: 'Act I',
start: '2021-06-09 11:00:00',
end: '2021-06-11 04:00:00',
color: '#63A3F6',
url: 'https://www.hoyolab.com/genshin/article/415687',
startOnly: true,
timezoneDependent: true,
},
{
name: 'Act II',
start: '2021-06-11 04:00:00',
end: '2021-06-14 04:00:00',
color: '#63A3F6',
url: 'https://www.hoyolab.com/genshin/article/415687',
startOnly: true,
},
{
name: 'Act III',
start: '2021-06-14 04:00:00',
end: '2021-06-17 04:00:00',
color: '#63A3F6',
url: 'https://www.hoyolab.com/genshin/article/415687',
startOnly: true,
},
{
name: 'Act IV',
start: '2021-06-17 04:00:00',
end: '2021-06-28 04:00:00',
color: '#63A3F6',
url: 'https://www.hoyolab.com/genshin/article/415687',
color: '#63A3F6',
zoom: '200%',
image: 'midsummer_island_adventure.png',
pos: '50% 35%',
startOnly: true,
},
],
[
{
name: 'Ballad in Goblets - Venti Banner',
pos: '20% 20%',
image: 'ballad_in_goblets.jpg',
start: '2021-03-17 06:00:00',
end: '2021-04-06 16:00:00',
color: '#6EDDCA',
url: 'https://genshin.mihoyo.com/en/news/detail/9269',
showOnHome: true,
},
{
name: 'Farewell of Snezhnaya - Tartaglia Banner',
pos: '30% 15%',
image: 'farewell_of_snezhnaya_2.jpg',
start: '2021-04-06 18:00:00',
end: '2021-04-27 15:00:00',
color: '#07E4FD',
url: 'https://genshin.mihoyo.com/en/news/detail/9714',
showOnHome: true,
},
{
name: 'Gentry of Hermitage - Zhongli Banner',
pos: '30% 15%',
@ -272,6 +181,7 @@ export const eventsData = [
color: '#FDFB80',
url: 'https://genshin.mihoyo.com/en/news/detail/12265',
showOnHome: true,
timezoneDependent: true,
},
{
name: 'Born of Ocean Swell - Eula Banner',
@ -282,30 +192,21 @@ export const eventsData = [
color: '#A6D6E0',
url: 'https://genshin.mihoyo.com/en/news/detail/12736',
showOnHome: true,
timezoneDependent: true,
},
{
name: 'Sparkling Steps - Klee Banner',
pos: '30% 22%',
image: 'sparkling_steps_2.png',
start: '2021-06-09 11:00:00',
end: '2021-06-29 17:59:59',
color: '#FF7357',
url: 'https://genshin.mihoyo.com/en/news/detail/13294',
showOnHome: true,
timezoneDependent: true,
},
],
[
{
name: 'Epitome Invocation - Weapon Banner',
image: 'epitome_invocation_2.jpg',
pos: '50% 20%',
start: '2021-03-17 06:00:00',
end: '2021-04-06 16:00:00',
color: '#FFAA4B',
url: 'https://genshin.mihoyo.com/en/news/detail/9278',
showOnHome: true,
},
{
name: 'Epitome Invocation - Weapon Banner',
image: 'epitome_Invocation_10.jpg',
pos: '30% 30%',
start: '2021-04-06 18:00:00',
end: '2021-04-27 15:00:00',
color: '#FFAA4B',
zoom: '170%',
url: 'https://genshin.mihoyo.com/en/news/detail/9739',
showOnHome: true,
},
{
name: 'Epitome Invocation - Weapon Banner',
image: 'epitome_invocation_11.jpg',
@ -316,6 +217,7 @@ export const eventsData = [
zoom: '170%',
url: 'https://genshin.mihoyo.com/en/news/detail/12270',
showOnHome: true,
timezoneDependent: true,
},
{
name: 'Epitome Invocation - Weapon Banner',
@ -327,29 +229,22 @@ export const eventsData = [
zoom: '170%',
url: 'https://genshin.mihoyo.com/en/news/detail/12749',
showOnHome: true,
timezoneDependent: true,
},
{
name: 'Epitome Invocation - Weapon Banner',
image: 'epitome_invocation_13.png',
pos: '30% 30%',
start: '2021-06-09 11:00:00',
end: '2021-06-29 17:59:59',
color: '#FFAA4B',
zoom: '170%',
url: 'https://genshin.mihoyo.com/en/news/detail/13295',
showOnHome: true,
timezoneDependent: true,
},
],
[
{
name: 'Spiral Abyss',
image: 'spiral_abyss.jpg',
pos: '50% 20%',
start: '2021-03-16 04:00:00',
end: '2021-04-01 04:00:00',
color: '#4299E1',
description:
"When the active character's HP is less than 50%, they gain a 2% DMG Bonus for 10s after hitting an opponent. This effect can stack up to 10 times, and each character in the party will have their own independent stacks and durations. Each time the stack count or duration of this effect resets, there is a 50% chance to release an Energy Blast. An Energy Blast can be triggered once every 10s.",
},
{
name: 'Spiral Abyss',
image: 'spiral_abyss.jpg',
pos: '50% 20%',
start: '2021-04-01 04:00:00',
end: '2021-04-16 04:00:00',
color: '#4299E1',
description:
"Characters possess an ATK-increasing Windward Aura that can increase their ATK by a maximum of 60%. For every opponent who steps within the Aura, the Aura's effect is decreased by 25%. When 4 or more opponents are within the Aura, this effect will not provide an ATK increase.",
},
{
name: 'Spiral Abyss',
image: 'spiral_abyss.jpg',
@ -389,18 +284,38 @@ export const eventsData = [
description:
"When a character scores Normal, Charged, and Plunging Attack hits that deal Physical DMG to opponents, this character's CRIT Rate is increased by 3% for 10s. This effect stacks up to 5 times, and only 1 stack is added at a time regardless of how many enemies are hit in one strike. When 5 stacks have been obtained, CRIT Hits on opponents will unleash shockwaves that deal AoE DMG. A shockwave can be created in this manner once every 1s.",
},
{
name: 'Spiral Abyss',
image: 'spiral_abyss.jpg',
pos: '50% 20%',
start: '2021-06-16 04:00:00',
end: '2021-07-01 04:00:00',
color: '#4299E1',
description:
"When an active character triggers a Swirl reaction on a nearby opponent, they will trigger a shockwave at that opponent's position that deals AoE DMG. A shockwave can be triggered in this manner once every 3.5s.",
},
{
name: 'Spiral Abyss',
image: 'spiral_abyss.jpg',
pos: '50% 20%',
start: '2021-07-01 04:00:00',
end: '2021-07-16 04:00:00',
color: '#4299E1',
description:
'When an active character triggers a Swirl reaction on a nearby opponent, they will regenerate 4 Energy for all characters in the party. This effect can be triggered once every 3.5s.',
},
{
name: 'Spiral Abyss',
image: 'spiral_abyss.jpg',
pos: '50% 20%',
start: '2021-07-16 04:00:00',
end: '2021-08-01 04:00:00',
color: '#4299E1',
description:
'When an active character triggers a Swirl reaction on a nearby opponent, a Brumous Aura that lasts 8s will be left at the location where the reaction took place. The Aura will inflict one count of DMG to opponents within it once every 2s. A Brumous Aura can be generated once every 9s.',
},
],
[
{
name: "Paimon's Bargain - Ningguang, Xingqiu, Blackcliff",
pos: '0% 50%',
zoom: '150%',
start: '2021-03-01 04:00:00',
end: '2021-04-01 04:00:00',
color: '#B6A1EA',
image: 'paimon_bargain.png',
description: 'Now selling: Ningguang, Xingqiu, and Blackcliff Weapons.',
},
{
name: "Paimon's Bargain - Razor, Amber, and Royal",
pos: '0% 50%',
@ -433,15 +348,6 @@ export const eventsData = [
},
],
[
{
name: 'Battle Pass',
image: 'windborne_blossoms.jpg',
pos: '0% 12%',
start: '2021-03-17 06:00:00',
end: '2021-04-26 04:00:00',
color: '#68D391',
url: 'https://genshin.mihoyo.com/en/news/detail/9289',
},
{
name: 'Battle Pass',
image: 'realm_of_diversion.jpg',
@ -449,6 +355,16 @@ export const eventsData = [
start: '2021-04-28 06:00:00',
end: '2021-06-07 04:00:00',
color: '#68D391',
timezoneDependent: true,
},
{
name: 'Battle Pass',
image: 'marine_hues.png',
pos: '0% 12%',
start: '2021-06-09 11:00:00',
end: '2021-07-19 04:00:00',
color: '#4EB3D6',
timezoneDependent: true,
},
],
];

View File

@ -6198,13 +6198,13 @@ export const weaponList = {
],
},
staff_of_homa: {
name: "Staff of Homa",
id: "staff_of_homa",
name: 'Staff of Homa',
id: 'staff_of_homa',
rarity: 5,
type: weapons.polearm,
source: "wish",
source: 'wish',
atk: 46,
secondary: "CRIT DMG",
secondary: 'CRIT DMG',
ascension: [
{
items: [
@ -6257,13 +6257,13 @@ export const weaponList = {
],
},
elegy_for_the_end: {
name: "Elegy for the End",
id: "elegy_for_the_end",
name: 'Elegy for the End',
id: 'elegy_for_the_end',
rarity: 5,
type: weapons.bow,
source: "wish",
source: 'wish',
atk: 46,
secondary: "Energy Recharge",
secondary: 'Energy Recharge',
ascension: [
{
items: [
@ -6375,7 +6375,7 @@ export const weaponList = {
],
},
song_of_broken_pines: {
name: "Song of Broken Pines",
name: 'Song of Broken Pines',
id: 'song_of_broken_pines',
rarity: 5,
atk: 46,
@ -6433,4 +6433,122 @@ export const weaponList = {
},
],
},
mitternachts_waltz: {
name: 'Mitternachts Waltz',
id: 'mitternachts_waltz',
rarity: 4,
atk: 42,
secondary: 'Phys DMG%',
type: weapons.bow,
source: 'Wish',
ascension: [
{
items: [
{ item: itemList.tile_of_decarabians_tower, amount: 3 },
{ item: itemList.heavy_horn, amount: 3 },
{ item: itemList.treasure_hoarder_insignia, amount: 2 },
],
mora: 5000,
},
{
items: [
{ item: itemList.debris_of_decarabians_city, amount: 3 },
{ item: itemList.heavy_horn, amount: 12 },
{ item: itemList.treasure_hoarder_insignia, amount: 8 },
],
mora: 15000,
},
{
items: [
{ item: itemList.debris_of_decarabians_city, amount: 6 },
{ item: itemList.black_bronze_horn, amount: 6 },
{ item: itemList.silver_raven_insignia, amount: 6 },
],
mora: 20000,
},
{
items: [
{ item: itemList.fragment_of_decarabians_epic, amount: 3 },
{ item: itemList.black_bronze_horn, amount: 12 },
{ item: itemList.silver_raven_insignia, amount: 9 },
],
mora: 30000,
},
{
items: [
{ item: itemList.fragment_of_decarabians_epic, amount: 6 },
{ item: itemList.black_crystal_horn, amount: 9 },
{ item: itemList.golden_raven_insignia, amount: 6 },
],
mora: 35000,
},
{
items: [
{ item: itemList.scattered_piece_of_decarabians_dream, amount: 4 },
{ item: itemList.black_crystal_horn, amount: 18 },
{ item: itemList.golden_raven_insignia, amount: 12 },
],
mora: 45000,
},
],
},
dodoco_tales: {
name: 'Dodoco Tales',
id: 'dodoco_tales',
rarity: 4,
atk: 42,
secondary: 'Phys DMG%',
type: weapons.catalyst,
source: 'Midsummer Island Adventure Event',
ascension: [
{
items: [
{ item: itemList.boreal_wolfs_milk_tooth, amount: 3 },
{ item: itemList.dead_ley_line_branch, amount: 3 },
{ item: itemList.damaged_mask, amount: 2 },
],
mora: 5000,
},
{
items: [
{ item: itemList.boreal_wolfs_cracked_tooth, amount: 3 },
{ item: itemList.dead_ley_line_branch, amount: 12 },
{ item: itemList.damaged_mask, amount: 8 },
],
mora: 15000,
},
{
items: [
{ item: itemList.boreal_wolfs_cracked_tooth, amount: 6 },
{ item: itemList.dead_ley_line_leaves, amount: 6 },
{ item: itemList.stained_mask, amount: 6 },
],
mora: 20000,
},
{
items: [
{ item: itemList.boreal_wolfs_broken_fang, amount: 3 },
{ item: itemList.dead_ley_line_leaves, amount: 12 },
{ item: itemList.stained_mask, amount: 9 },
],
mora: 30000,
},
{
items: [
{ item: itemList.boreal_wolfs_broken_fang, amount: 6 },
{ item: itemList.ley_line_sprouts, amount: 9 },
{ item: itemList.ominous_mask, amount: 6 },
],
mora: 35000,
},
{
items: [
{ item: itemList.boreal_wolfs_nostalgia, amount: 4 },
{ item: itemList.ley_line_sprouts, amount: 18 },
{ item: itemList.ominous_mask, amount: 12 },
],
mora: 45000,
},
],
},
};

1
src/data/weapons/en.json Normal file

File diff suppressed because one or more lines are too long

1
src/data/weapons/fr.json Normal file

File diff suppressed because one or more lines are too long

1
src/data/weapons/id.json Normal file

File diff suppressed because one or more lines are too long

1
src/data/weapons/ko.json Normal file

File diff suppressed because one or more lines are too long

1
src/data/weapons/pt.json Normal file

File diff suppressed because one or more lines are too long

1
src/data/weapons/ru.json Normal file

File diff suppressed because one or more lines are too long

1
src/data/weapons/zh.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
import dayjs from 'dayjs';
import { t as $t } from 'svelte-i18n'
import { t as $t } from 'svelte-i18n';
import { getAccountPrefix } from '../stores/account';
import { getTimeOffset } from '../stores/server';
@ -10,7 +10,7 @@ import { characters } from '../data/characters';
import { pushToast } from '../stores/toast';
let t;
$t.subscribe(f => t = f)
$t.subscribe((f) => (t = f));
const bannerTypes = {
'character-event': 'characters',
@ -78,6 +78,8 @@ export async function process(id) {
character: [],
weapon: [],
},
featured: e.featured,
featuredRare: e.featuredRare,
};
});
@ -94,6 +96,7 @@ export async function process(id) {
let grouped = false;
let striped = false;
let rateUp = false;
let rateUpRare = false;
let startBanner = false;
let currentBannerIndex = -1;
let hasManualInput = false;
@ -145,7 +148,7 @@ export async function process(id) {
currentPity: ++pity,
};
if (item.rarity === 5) {
if (item.rarity === 5) {
if (currentBanner.featured) {
newPull.guaranteed = rateUp;
rateUp = !currentBanner.featured.includes(newPull.id);
@ -155,6 +158,11 @@ export async function process(id) {
allLegendary.push(newPull);
pity = 0;
} else if (item.rarity === 4) {
if (currentBanner.featuredRare) {
newPull.guaranteed = rateUpRare;
rateUpRare = !currentBanner.featuredRare.includes(newPull.id);
}
allRare.push(newPull);
selectedBanners[currentBannerIndex].rarePity[newPull.pity - 1]++;
if (pull.type === 'character') {

View File

@ -2,11 +2,6 @@ import dayjs from 'dayjs';
import { process } from './wish';
const bannerCategories = ['beginners', 'standard', 'character-event', 'weapon-event'];
const rareInclude = {
300011: ['rosaria'],
300012: ['yanfei', 'noelle', 'diona'],
300013: ['xingqiu', 'beidou', 'xinyan'],
};
async function sendWish(data) {
try {
@ -58,11 +53,15 @@ export async function submitWishTally() {
]);
// specific 4star include
if (rareInclude[prefixId + i + 1]) {
if (banner[i].rare && banner[i].featuredRare) {
const includedRarePulls = banner[i].rare.character
.filter((e) => rareInclude[prefixId + i + 1].includes(e.id))
.map((e) => [dayjs(e.time).unix().toString(), e.id, e.type, e.pity, e.group === 'group', true, 4]);
.filter((e) => banner[i].featuredRare.includes(e.id))
.map((e) => [dayjs(e.time).unix().toString(), e.id, e.type, e.pity, e.group === 'group', e.guaranteed, 4]);
const includedRareWeaponPulls = banner[i].rare.weapon
.filter((e) => banner[i].featuredRare.includes(e.id))
.map((e) => [dayjs(e.time).unix().toString(), e.id, e.type, e.pity, e.group === 'group', e.guaranteed, 4]);
legendaryPulls.push(...includedRarePulls);
legendaryPulls.push(...includedRareWeaponPulls);
}
// console.log(pityCount);

View File

@ -25,6 +25,7 @@ $locale.subscribe((value) => {
}
});
const supportedLanguage = ['en', 'id', 'ru', 'ko', 'fr', 'zh', 'pt'];
addMessages('en', en);
addMessages('id', id);
addMessages('ru', ru);
@ -34,11 +35,22 @@ addMessages('zh', zh);
addMessages('pt', pt);
export function startClient() {
let used = 'en';
const savedLocale = localStorage.getItem('locale');
const detectedLocale = getLocaleFromNavigator().substring(0, 2);
if (savedLocale !== null) {
if (!supportedLanguage.includes(savedLocale)) {
localStorage.setItem('locale', 'en');
} else {
used = savedLocale;
}
} else if (supportedLanguage.includes(detectedLocale)) {
used = detectedLocale;
}
init({
...INIT_OPTIONS,
initialLocale: savedLocale !== null ? savedLocale : getLocaleFromNavigator().substring(0, 2),
initialLocale: used,
});
}

View File

@ -11,6 +11,7 @@
"todoList": "Todo List",
"timeline": "Timeline",
"furnishing": "Furnishing",
"weapons": "Weapons",
"settings": "Settings",
"donate": "Donate"
},
@ -18,9 +19,7 @@
"welcome": "Welcome to Paimon.moe! 👋",
"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": [
"Eula"
],
"featured": ["Eula"],
"summoned": "Summoned",
"percentage": "from all {rarity}",
"avg": "Pity average",
@ -66,6 +65,10 @@
"achievement": {
"title": "🏆 View and track your achievement list here",
"detail": "Achievement"
},
"furnishing": {
"title": "Check what furnishing you need to make to complete a sets. And also you can view the load of each furnishing.",
"detail": "Furnishing"
}
},
"characters": {
@ -78,6 +81,7 @@
"talents": "Talents",
"passiveTalents": "Passive Talents",
"constellations": "Constellations",
"const": "Const",
"asc": "ASC",
"lvl": "LVL",
"hp": "HP",
@ -98,7 +102,8 @@
"cryoDamageBonus": "Cryo DMG Bonus",
"anemoDamageBonus": "Anemo DMG Bonus",
"physicalDamageBonus": "Physical DMG Bonus",
"geoDamageBonus": "Geo DMG Bonus"
"geoDamageBonus": "Geo DMG Bonus",
"sortBy": "Sort by..."
},
"wish": {
"title": "Wish Counter",
@ -119,10 +124,7 @@
"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}"
],
"pityTooltip": ["Shows your current {rarity} pity", "{count} pulls to guaranteed {rarity}"],
"import": {
"title": "Import Wish History",
"faqsButton": "FAQ - READ FIRST",
@ -151,11 +153,7 @@
"server": "Select your server:",
"wishTallyCheck": "Submit pity for global wish tally",
"wishTally": "We are doing a global wish tally! You can submit your wish tally to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCollected": [
"What will be collected:",
"and",
"pity from your wish history"
],
"wishTallyCollected": ["What will be collected:", "and", "pity from your wish history"],
"faqs": {
"title": "Import Wish History FAQ",
"q1": "How does it work?",
@ -277,11 +275,7 @@
"exportFinish": "Export success, please wait until the browser download the file!",
"wishTallyTitle": "Submit Wish Tally",
"wishTally": "We are doing a global wish tally! You can submit your wish tally to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCollected": [
"What will be collected:",
"and",
"pity from your wish history"
],
"wishTallyCollected": ["What will be collected:", "and", "pity from your wish history"],
"wishTallySubmit": "Submit Wish Tally",
"wishTallyThankyou": "Thankyou for participating!",
"manualTitle": "Manual Input Setting",
@ -293,22 +287,13 @@
"subtitle": "After a 1x Wish:",
"pressWhenYouGet": "Press {button} when you get {rarity}★",
"p1": "It will automatically add the lifetime pulls, 5★, and 4★ pity",
"p2": [
"When the",
"pity reaches 10, it will automatically be reset to 0"
],
"p3": [
"When the",
"pity reaches 90, it will automatically be reset to 0"
],
"p2": ["When the", "pity reaches 10, it will automatically be reset to 0"],
"p3": ["When the", "pity reaches 90, it will automatically be reset to 0"],
"p4": [
"After a 10x Wish, press",
"but keep in mind that the pity counter might not be accurate, because there is no way to tell when the drop occured (maybe you got it on the 1st or even the 10th pull). To ensure that the counter is still accurate, you need to check the history table and add it one-by-one like you do 1x Wishes."
],
"p5": [
"You can also press the",
"button to edit the values manually!"
],
"p5": ["You can also press the", "button to edit the values manually!"],
"p6": [
"Press the arrow on the bottom to see your pulls' details. A popup will show up when you get a",
"or",
@ -401,11 +386,7 @@
"calculateTalent": "Calculate Talent Material?",
"inputTalentLevel": "Input the 1st, 2nd & 3rd current talent level",
"inputTalentNotice": "If it has different color, substract it by 3",
"inputTalent": [
"1st talent lvl",
"2nd talent lvl",
"3rd talent lvl"
],
"inputTalent": ["1st talent lvl", "2nd talent lvl", "3rd talent lvl"],
"talentToLevel": "to level",
"calculate": "Calculate",
"unknownInformation": "There are some unknown information",
@ -413,7 +394,8 @@
"mora": "Mora (approximate ±40)",
"expWasted": "EXP Wasted",
"addToTodo": "Add to Todo List",
"addedToTodo": "Added to Todo List"
"addedToTodo": "Added to Todo List",
"talent": ["Attack", "Skill", "Burst"]
},
"expTable": {
"level": "Level",
@ -468,10 +450,7 @@
"todo": {
"title": "Todo List",
"summary": "Summary",
"empty": [
"Nothing to do yet 😀",
"Add some from the Items page or the Calculator!"
],
"empty": ["Nothing to do yet 😀", "Add some from the Items page or the Calculator!"],
"farmableToday": "Farmable Today",
"resin": "Resin needed",
"based": "Based on AR:{ar} and WL:{wl}",
@ -629,6 +608,49 @@
"hall": "Hall",
"room": "Room {number}",
"exteriorNum": "Area {number}",
"corridor": "Corridor"
"corridor": "Corridor",
"inventoryButton": "Inventory",
"listButton": "List",
"inventory": {
"title": "Furnishing Inventory",
"subtitle": "Some special categories like wall are not shown",
"all": "All",
"openSets": "Open Sets"
},
"sets": {
"title": "Furnishing Sets",
"subtitle": "Click the furnishing icon for detail, and you can click the character icon to mark them! Open the Inventory menu above to fill your furnishing items count.",
"setPlaced": "Set as placed",
"setUnplaced": "Set as unplaced",
"inInventory": "In inventory",
"available": "Available",
"used": "Used in other set"
}
},
"weapon": {
"title": "Weapon List",
"subtitle": "Stats are at max level",
"name": "Name",
"type": "Type",
"rarity": "Rarity",
"atk": "ATK",
"secondary": "Secondary",
"critRate": "CRIT Rate",
"critDamage": "CRIT DMG",
"em": "Elemental Mastery",
"er": "Energy Recharge",
"atkPercent": "ATK",
"hpPercent": "HP",
"defPercent": "DEF",
"physicalDamage": "Physical DMG Bonus",
"bow": "Bow",
"polearm": "Polearm",
"sword": "Sword",
"catalyst": "Catalyst",
"claymore": "Claymore",
"ascensionMaterial": "Ascension Materials",
"asc": "ASC",
"lvl": "LVL",
"baseAtk": "Base ATK"
}
}
}

View File

@ -11,6 +11,7 @@
"todoList": "Todo List",
"timeline": "Timeline",
"furnishing": "Furnitur",
"weapons": "Senjata",
"settings": "Pengaturan",
"donate": "Donasi"
},
@ -59,6 +60,10 @@
"twitter": {
"title": "Follow my Twitter, akan post tentang apa yang lagi di develop dan update terbaru tentang paimon.moe!",
"detail": "Follow Twitter"
},
"furnishing": {
"title": "Cek furnitur apa saja yang kamu perlukan untuk menyelesaikan suatu set. Dan kamu juga bisa melihat beban masing-masing furnitur.",
"detail": "Furnitur"
}
},
"characters": {
@ -91,10 +96,7 @@
"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}"
],
"pityTooltip": ["Ini adalah pity {rarity} sekarang", "{count}x wish lagi untuk dijamin {rarity}"],
"import": {
"title": "Import Riwayat Wish",
"faqsButton": "FAQS - BACA DULU",
@ -123,11 +125,7 @@
"server": "Pilih server mu:",
"wishTallyCheck": "Submit pity untuk perhitungan pity global",
"wishTally": "Kita sedang melakukan perhitungan pity global! Kamu bisa mensubmit pity mu untuk berpartisipasi. Semua data pity akan dikumpulkan untuk mengetahui berapa pity rata-rata pengguna paimon.moe.",
"wishTallyCollected": [
"Yang dikumpulkan:",
"dan",
"pity dari riwayat wish mu"
],
"wishTallyCollected": ["Yang dikumpulkan:", "dan", "pity dari riwayat wish mu"],
"faqs": {
"title": "Import Riwayat Wish FAQS",
"q1": "Cara kerjanya gimana?",
@ -249,11 +247,7 @@
"exportFinish": "Export berhasil, harap tunggu sampai file nya sudah ter-download!",
"wishTallyTitle": "Submit Perhitungan Pity Wish",
"wishTally": "Kita sedang melakukan perhitungan pity global! Kamu bisa mensubmit pity mu untuk berpartisipasi. Semua data pity akan dikumpulkan untuk mengetahui berapa pity rata-rata pengguna paimon.moe.",
"wishTallyCollected": [
"Yang dikumpulkan:",
"dan",
"pity dari riwayat wish mu"
],
"wishTallyCollected": ["Yang dikumpulkan:", "dan", "pity dari riwayat wish mu"],
"wishTallySubmit": "Submit Perhitungan Pity Wish",
"wishTallyThankyou": "Terimakasih sudah berpartisipasi!",
"manualTitle": "Pengaturan Manual Input",
@ -265,22 +259,13 @@
"subtitle": "Setelah kamu melakukan x1 pull wish:",
"pressWhenYouGet": "Tekan {button} ketika kamu mendapatkan {rarity}★",
"p1": "Itu akan otomatis menambahkan total pull, 5★ dan, 4★ pity",
"p2": [
"Ketika",
"pity mencapai 10, angkanya akan otomatis reset menjadi 0"
],
"p3": [
"Ketika",
"pity mencapai 90, angkanya akan otomatis reset menjadi 0"
],
"p2": ["Ketika", "pity mencapai 10, angkanya akan otomatis reset menjadi 0"],
"p3": ["Ketika", "pity mencapai 90, angkanya akan otomatis reset menjadi 0"],
"p4": [
"Ketika kamu melakukan wish x10 pull, tekan",
"tapi counter pity nya tidak akan akurat, karena tidak tahu kapan drop nya terjadi (bisa saja kamu dapat nya saat pull ke 1 atau bisa saja ke 10). Untuk membuat counter nya akurat, kamu perlu mengecek nya di tabel riwayat wish mu dan tabahkan 1-per-1 seperti kamu melakukan 1x pull wish."
],
"p5": [
"Kamu juga bisa menekan",
"untuk mengedit nilai nya secara manual!"
],
"p5": ["Kamu juga bisa menekan", "untuk mengedit nilai nya secara manual!"],
"p6": [
"Tekan tombol panah dibawah untuk melihat detail riwayat mu. Sebuah form akan muncul ketika kamu mendapat",
"atau",
@ -371,11 +356,7 @@
"calculateTalent": "Hitung Material Talent?",
"inputTalentLevel": "Masukkan level talent ke 1, 2 dan 3 saat ini",
"inputTalentNotice": "Jika warna level nya berbeda, kurangi 3",
"inputTalent": [
"lvl talent ke 1",
"lvl talent ke 2",
"lvl talent ke 3"
],
"inputTalent": ["lvl talent ke 1", "lvl talent ke 2", "lvl talent ke 3"],
"talentToLevel": "ke level",
"calculate": "Hitung",
"unknownInformation": "Ada beberapa informasi yang tidak diketahui",
@ -438,10 +419,7 @@
"todo": {
"title": "Todo List",
"summary": "Summary",
"empty": [
"Belum ada yang ditambahkan 😀",
"Tambahkan todo dari halaman Items atau dari Kalkulator!"
],
"empty": ["Belum ada yang ditambahkan 😀", "Tambahkan todo dari halaman Items atau dari Kalkulator!"],
"farmableToday": "Bisa di farm hari ini",
"resin": "resin diperlukan",
"based": "Berdasarkan AR:{ar} and WL:{wl}",
@ -594,6 +572,27 @@
"info": [
"Ini menunjukkan berapa beban yang bisa ditampung dalam pulau. Masing-masing furnitur mempunyai nilai beban yang tersembunyi yang bisa dilihat di bawah.",
"(Beban maximum belum dikonfirmasi!)"
]
],
"hall": "Aula Utama",
"room": "Kamar {number}",
"exteriorNum": "Area {number}",
"corridor": "Koridor",
"inventoryButton": "Inventory",
"listButton": "List",
"inventory": {
"title": "Furnishing Inventory",
"subtitle": "Beberapa kategori special seperti dinding tidak ditampilkan",
"all": "Semua",
"openSets": "Buka Set"
},
"sets": {
"title": "Furnishing Set",
"subtitle": "Klik icon furnitur untuk detail, dan kamu bisa klik icon karakter untuk menandai mereka! Buka menu inventory diatas untuk mengisi jumlah furnitur yang kamu punya.",
"setPlaced": "Set ditempatkan",
"setUnplaced": "Set belum ditempatkan",
"inInventory": "Di inventory",
"available": "Tersedia",
"used": "Digunakan di set lain"
}
}
}
}

View File

@ -16,7 +16,7 @@
},
"home": {
"welcome": "Boas-vindas ao Paimon.moe! 👋",
"message": "Seu melhor companheiro de Genshin Impact! Planeje seu farm com a calculadora de ascenção e acompanhe seu progresso com a lista de afazeres e o histórico de orações.",
"message": "Seu melhor companheiro de Genshin Impact! Planeje seu farm com a calculadora de ascensão e acompanhe seu progresso com a lista de afazeres e o histórico de orações.",
"banner": {
"featured": [
"Eula"
@ -51,12 +51,12 @@
"join": "Junte-se ao Discord"
},
"items": {
"title": "Farmáveis Hoje",
"title": "Farmaveis Hoje",
"detail": "Itens",
"sunday": "Todos os itens podem ser farmados aos domingos 😁"
},
"calculator": {
"title": "🧮 Calcule materiais de ascenção de Personagens, Armas e elevação de talento! Todos os cálculos podem ser adicionados na Lista de Afazeres. Também mostra quantas resinas você irá precisar!",
"title": "🧮 Calcule materiais de ascensão de Personagens, Armas e elevação de talento! Todos os cálculos podem ser adicionados na Lista de Afazeres. Também mostra quantas resinas você irá precisar!",
"detail": "Calculadora"
},
"twitter": {
@ -70,7 +70,7 @@
},
"characters": {
"title": "Personagens",
"subtitle": "Os números dos status são do Nível 80, Ascenção 6. Você pode clicar nos índices da tabela para ordenar!",
"subtitle": "Os números dos status são do Nível 80, Ascensão 6. Você pode clicar nos índices da tabela para ordenar!",
"name": "Nome",
"element": "Elemento",
"rarity": "Raridade",
@ -115,7 +115,7 @@
"welcomeStart2": "acima",
"manual": "Se você quiser informar os dados manualmente, você pode fazer isso aqui:",
"manualButton": "Habilitar Inserção Manual",
"errorBanner": "Incompatibilidade no tempo do banner! Ajuste seu servidor na página de configurações. Ainda não está funcionando? Por favor, deixa uma mensagem no Discord 😅",
"errorBanner": "Incompatibilidade no tempo do banner! Ajuste seu servidor na página de configurações. Ainda não está funcionando? Por favor, deixe uma mensagem no Discord 😅",
"globalWishTally": "Contagem Global de Orações",
"pityTooltip": [
"Informa seu pity atual para {rarity}",
@ -179,7 +179,7 @@
"Se você não deseja compartilhar sua URL, você pode utilizar um pequeno script de importação para salvar seu histórico de orações em seu PC (opção para PC Local)"
],
"q6": "Eu fiz todos os procedimentos, mas tive um erro na API?",
"a6": "Certifique-se de copiar todo o texto (segura e pressione Selecionar Tudo para dispositivos móveis); talvez você tenha esquecido alguns caracteres que não necessários para o funcionamento da importação.",
"a6": "Certifique-se de copiar todo o texto (segure e pressione Selecionar Tudo para dispositivos móveis); talvez você tenha esquecido alguns caracteres que não necessários para o funcionamento da importação.",
"q7": "Não leu todo meu histórico!?",
"a7": "Histórico de orações mais antigos que 6 meses são deletados dos servidores da MiHoYo, então se você não possui um backup em algum lugar, infelizmente se foi"
},
@ -225,7 +225,7 @@
"pclocal": [
"Se você não se sente confortável em compartilhar sua URL de feedback, você pode usar esta opção para processar seu histórico de orações em seu PC localmente. Este script irá ler o log em seu PC para gerar a URL do histórico de orações.",
"Abra o menu Iniciar, e então procure por Powershell",
"Abra o Powershell do Widows, e então copie & cole o script abaixo no Powershell",
"Abra o Powershell do Windows, e então copie & cole o script abaixo no Powershell",
"iex ((New-Object System.Net.WebClient).DownloadString('https://gist.githubusercontent.com/MadeBaruna/9ff8b7a2af11f3002395af7963b5ed18/raw/cdfead30f830b897e8822a40f98fea5340dbd62e/importer.ps1'))",
"Você pode revisar o script",
"aqui",
@ -244,7 +244,7 @@
"export": "Exportar para Excel",
"exporting": "Exportando...",
"import": "Importar",
"exportFinish": "Exportado com successo, aguarde até que o navegador realize o download do arquivo!",
"exportFinish": "Exportado com sucesso, aguarde até que o navegador realize o download do arquivo!",
"wishTallyTitle": "Enviar Contagem de Orações",
"wishTally": "Estamos fazendo uma contagem global de orações! Você pode enviar sua contagem de orações para participar. Todas as informações do pity serão agregadas para saber qual a média de pity dos usuários do paimon.moe.",
"wishTallyCollected": [
@ -280,7 +280,7 @@
"para editar os valores manualmente!"
],
"p6": [
"Pressione a seta embaixo para visualizar os detalhes das orações. Um pop-up irá aparecer quando você conseguir um item",
"Pressione a seta abaixo para visualizar os detalhes das orações. Um pop-up irá aparecer quando você conseguir um item",
"ou",
"Você também pode adicionar ou editar a tabela manualmente."
]
@ -342,7 +342,7 @@
"howToCharacter": "Como usar a Calculadora de Personagens"
},
"weapon": {
"calculateAscension": "Calcular Materiais de Ascenção?",
"calculateAscension": "Calcular Materiais de Ascensão?",
"selectRarity": "Selecione a raridade da arma",
"selectWeapon": "Selecione a arma",
"current": "Nível, EXP, & Ascenção Atual da Arma",
@ -353,14 +353,14 @@
"resource": "Recursos para Usar",
"calculate": "Calcular",
"unknownInformation": "Há alguma informação desconhecida",
"ascensionLevel": "Nível de ascenção",
"ascensionLevel": "Nível de ascensão",
"mora": "Mora (aproximadamente ±40)",
"expWasted": "EXP Desperdiçada",
"addToTodo": "Adicionar na Lista de Afazeres",
"addedToTodo": "Adicionado na Lista de Afazeres"
},
"character": {
"calculateAscension": "Calcular Materiais de Ascenção?",
"calculateAscension": "Calcular Materiais de Ascensão?",
"selectCharacter": "Selecione o personagem",
"current": "Nível, EXP, & Ascenção Atual do Personagem",
"inputCurrentLevel": "Informe o nível atual do personagem...",
@ -379,7 +379,7 @@
"talentToLevel": "para o nível",
"calculate": "Calcular",
"unknownInformation": "Há alguma informação desconhecida",
"ascensionLevel": "Nível de ascenção",
"ascensionLevel": "Nível de ascensão",
"mora": "Mora (aproximadamente ±40)",
"expWasted": "EXP Desperdiçada",
"addToTodo": "Adicionar na Lista de Afazeres",
@ -440,12 +440,12 @@
"summary": "Sumário",
"empty": [
"Nada para fazer ainda 😀",
"Adicione algumas coisas a partir da Cálculadora ou da página de Itens!"
"Adicione algumas coisas a partir da Calculadora ou da página de Itens!"
],
"farmableToday": "Farmáveis hoje",
"resin": "Resinas necessárias",
"based": "Baseado no AR:{ar} e WL:{wl}",
"change": "(altera nas configurações)",
"change": "(alterar nas configurações)",
"approximation": "Aproximação calculada a partir da taxa de drops por",
"delete": {
"title": "Deletar esse afazer?",
@ -549,7 +549,7 @@
"donate": {
"message": [
"Muito obrigado! Espero que ache isso útil.",
"Fiz este site para me divertir quando tenha um tempo livre. Obviamente irei adicionar mais recursos e atualizarei os dados a cada nova atualização, pois gosto de explorar o jogo e tentar maximizar minha experiência",
"Fiz este site para me divertir quando tenho um tempo livre. Obviamente irei adicionar mais recursos e atualizarei os dados a cada nova atualização, pois gosto de explorar o jogo e tentar maximizar minha experiência",
"Se você quiser doar, sua doação definitivamente irá ajudar a melhorar o site e me motivar a adicionar mais conteúdo para Paimon.moe! Muito obrigado e divirta-se, espero que goste!",
"Se você gosta de codificar, você também pode contribuir para o projeto em"
],

View File

@ -11,6 +11,7 @@
"todoList": "Список дел",
"timeline": "Лента событий",
"furnishing": "Мебель",
"weapons": "Оружие",
"settings": "Настройки",
"donate": "Поддержать"
},
@ -18,9 +19,7 @@
"welcome": "Добро пожаловать на Paimon.moe! 👋",
"message": "Твой лучший компаньон в Genshin Impact! Поможет тебе распланировать что фармить в калькуляторе возвышения, а также отслеживать свой прогресс с помощью счетчика молитв и cписка дел",
"banner": {
"featured": [
"Эола"
],
"featured": ["Кли"],
"summoned": "Призывов",
"percentage": "из всех {rarity}",
"avg": "Среднее число молитв",
@ -78,6 +77,7 @@
"talents": "Таланты",
"passiveTalents": "Пассивные таланты",
"constellations": "Созвездие",
"const": "Созвездие",
"asc": "Возвышение",
"lvl": "Уровень",
"hp": "HP",
@ -98,7 +98,8 @@
"cryoDamageBonus": "Бонус Крио урона",
"anemoDamageBonus": "Бонус Анемо урона",
"physicalDamageBonus": "Бонус физ. урона",
"geoDamageBonus": "Бонус Гео урона"
"geoDamageBonus": "Бонус Гео урона",
"sortBy": "Сортировать по..."
},
"wish": {
"title": "Счетчик молитв",
@ -119,10 +120,7 @@
"manualButton": "Включить ручной ввод",
"errorBanner": "Несоответствие времени баннера! Пожалуйста, настройте свой сервер на странице настроек. Все еще не работает? Напишите нам в дискорд!",
"globalWishTally": "Глобальная статистика молитв",
"pityTooltip": [
"Отображает ваши текущие {rarity} молитвы",
"{count} молитв до гарантированного {rarity}"
],
"pityTooltip": ["Отображает ваши текущие {rarity} молитвы", "{count} молитв до гарантированного {rarity}"],
"import": {
"title": "Импорт истории молитв",
"faqsButton": "FAQ - ПРОЧЕСТЬ В ПЕРВУЮ ОЧЕРЕДЬ",
@ -151,11 +149,7 @@
"server": "Выберите ваш сервер:",
"wishTallyCheck": "Отправить данные о молитвах для глобальной статистики",
"wishTally": "Мы собираем глобальную статистику молитв! Вы можете отправить свои молитвы чтобы помочь нам. Все данные будут собраны чтобы узнать средний гарант у пользователей paimon.moe.",
"wishTallyCollected": [
"Какие данные будут сохранены:",
"и",
"гаранты из вашей истории молитв"
],
"wishTallyCollected": ["Какие данные будут сохранены:", "и", "гаранты из вашей истории молитв"],
"faqs": {
"title": "FAQ Импорт истории молитв",
"q1": "Как это работает?",
@ -277,11 +271,7 @@
"exportFinish": "Успешный экспорт, пожалуйста подождите пока браузер загрузит файл!",
"wishTallyTitle": "Отправить данные о молитвах для глобальной статистики",
"wishTally": "Мы собираем глобальную статистику молитв! Вы можете отправить свои молитвы чтобы помочь нам. Все данные будут собраны чтобы узнать средний гарант у пользователей paimon.moe.",
"wishTallyCollected": [
"Какие данные будут сохранены:",
"и",
"гаранты из вашей истории молитв"
],
"wishTallyCollected": ["Какие данные будут сохранены:", "и", "гаранты из вашей истории молитв"],
"wishTallySubmit": "Отправить данные о молитвах для глобальной статистики",
"wishTallyThankyou": "Спасибо за участие!!",
"manualTitle": "Настройки ручного ввода",
@ -293,22 +283,13 @@
"subtitle": "После 1 молитвы:",
"pressWhenYouGet": "Нажмите {button} когда вы получили {rarity}★",
"p1": "Это автоматически добавит его в счетчик молитв за все время, 5★, и 4★ Гарант",
"p2": [
"Когда",
"гарант достигнет 10, он автоматически сбросится до 0"
],
"p3": [
"Когда",
"гарант достигнет 90, он автоматически сбросится до 0"
],
"p2": ["Когда", "гарант достигнет 10, он автоматически сбросится до 0"],
"p3": ["Когда", "гарант достигнет 90, он автоматически сбросится до 0"],
"p4": [
"После 10 молитв",
"но имейте в виду, что счетчик гаранта может быть неточным, потому что нет способа определить, когда выпал гарант (возможно, он выпал на 1 или даже 10 попытке). Чтобы убедиться, что счетчик по-прежнему точен, вам нужно проверить таблицу истории и добавлять по 1х молитве."
],
"p5": [
"Вы также можете нажать",
"кнопку чтобы редактировать значения вручную!"
],
"p5": ["Вы также можете нажать", "кнопку чтобы редактировать значения вручную!"],
"p6": [
"Нажмите на стрелку ниже, чтобы увидеть подробности о ваших молитвах. Всплывающее окно появится, когда вы получите",
"или",
@ -400,12 +381,8 @@
"resource": "Ресурсы для использования",
"calculateTalent": "Рассчитать материалы для талантов?",
"inputTalentLevel": "Введите текущий уровень 1, 2 и 3 таланта",
"inputTalentNotice": "If it has different color, substract it by 3",
"inputTalent": [
"Уровень первого таланта",
"Уровень второго таланта",
"Уровень третьего таланта"
],
"inputTalentNotice": "Если он имеет другой цвет, вычтите из него 3",
"inputTalent": ["Уровень первого таланта", "Уровень второго таланта", "Уровень третьего таланта"],
"talentToLevel": "до уровня",
"calculate": "Рассчитать",
"unknownInformation": "Здесь какая-то неизвестная информация...",
@ -413,7 +390,8 @@
"mora": "Мора (приблизительно ±40)",
"expWasted": "EXP потеряно",
"addToTodo": "Добавить в список дел",
"addedToTodo": "Добавлено в список дел"
"addedToTodo": "Добавлено в список дел",
"talent": ["Обычн.Атака", "Элем.Навык", "Взрыв Стихии"]
},
"expTable": {
"level": "Уровень",
@ -468,10 +446,7 @@
"todo": {
"title": "Список дел",
"summary": "Сводка",
"empty": [
"Ничего нет 😀",
"Добавьте что-нибудь со страницы предметов или калькулятора!"
],
"empty": ["Ничего нет 😀", "Добавьте что-нибудь со страницы предметов или калькулятора!"],
"farmableToday": "Можно фармить сегодня",
"resin": "Смолы необходимо",
"based": "Основываясь на ранге приключений :{ar} и уровне мира:{wl}",
@ -630,5 +605,31 @@
"room": "Комната {number}",
"exteriorNum": "Регион {number}",
"corridor": "Коридор"
},
"weapon": {
"title": "Список оружия",
"subtitle": "Статы на максимальном уровне оружия",
"name": "Имя",
"type": "Тип",
"rarity": "Редкость",
"atk": "Баз.Атака",
"secondary": "Втор.Стат",
"critRate": "Шанс крит.попадания",
"critDamage": "Крит.Урон",
"em": "Мастерство стихий",
"er": "Восст.энергии",
"atkPercent": "Сила атаки",
"hpPercent": "HP",
"defPercent": "Защита",
"physicalDamage": "Бонус физ.урона",
"bow": "Лук",
"polearm": "Копьё",
"sword": "Меч",
"catalyst": "Катализатор",
"claymore": "Двуручный меч",
"ascensionMaterial": "Материалы возвышения",
"asc": "Пробуждение",
"lvl": "Уровень",
"baseAtk": "Базовая атака"
}
}
}

View File

@ -13,15 +13,15 @@
const dispatch = createEventDispatcher();
const featured = {
eula: {
klee: {
rarity: 'legendary',
count: 0,
average: '...',
percentage: '...',
},
};
const bannerId = 300013;
const image = 'eula.png';
const bannerId = 300014;
const image = 'klee.png';
let loading = true;
let user = '';
@ -80,7 +80,7 @@
{/if}
</h3>
<p class="ml-6 font-sm leading-2">
<span class="font-semibold">{$t(`home.banner.featured.${i}`)}</span>
<span class="font-semibold">Klee</span>
{$t('home.banner.summoned')}
</p>
</div>

View File

@ -8,18 +8,31 @@
import Icon from '../../components/Icon.svelte';
import { eventsData } from '../../data/timeline';
import { getTimeDifference, getTimeDifferenceAsia, server } from '../../stores/server';
import { getAccountPrefix } from '../../stores/account';
import { readSave } from '../../stores/saveManager';
const dispatch = createEventDispatcher();
const now = dayjs();
let now;
let upcoming = [];
let current = [];
let timeDifferenceEvent;
let timeDifferenceAsia;
function checkEvent(event) {
if (!event.showOnHome) return;
const start = dayjs(event.start);
const end = dayjs(event.end);
let start;
if (event.timezoneDependent) {
start = dayjs(event.start, 'YYYY-MM-DD HH:mm:ss').subtract(timeDifferenceAsia, 'minute');
} else {
start = dayjs(event.start, 'YYYY-MM-DD HH:mm:ss').subtract(timeDifferenceEvent, 'minute');
}
const end = dayjs(event.end).subtract(timeDifferenceEvent, 'minute');
if (start.isBefore(now) && end.isAfter(now)) {
const diff = end.diff(now);
const timeLeft = dayjs.duration(diff);
@ -46,10 +59,21 @@
}
onMount(async () => {
const prefix = getAccountPrefix();
const serverSave = await readSave(`${prefix}server`);
if (serverSave !== null) {
server.set(serverSave);
}
timeDifferenceEvent = getTimeDifference();
timeDifferenceAsia = getTimeDifferenceAsia();
now = dayjs();
parseEvents();
await tick();
dispatch('done');
});
</script>
<div class="bg-item rounded-xl p-4 flex flex-col">

View File

@ -0,0 +1,18 @@
<script>
import { mdiChevronRight } from '@mdi/js';
import { t } from 'svelte-i18n';
import Icon from '../../components/Icon.svelte';
</script>
<div class="bg-item rounded-xl p-4 flex flex-col">
<p class="text-white">{$t('home.furnishing.title')}</p>
<a
href="/furnishing"
class="flex justify-end items-center self-end lg:self-start text-white mt-4
bg-background-secondary rounded-xl py-2 px-4 hover:bg-background transition-colors duration-100"
>
{$t('home.furnishing.detail')}
<Icon path={mdiChevronRight} />
</a>
</div>

View File

@ -35,40 +35,43 @@
let sort = false;
function parseCategories() {
categories = Object.entries(achievement).map(([id, data]) => ({
id,
name: data.name,
finished: checkList[id] !== undefined ? Object.values(checkList[id]).filter((e) => e === true).length : 0,
...data.achievements.reduce(
(prev, cur) => {
if (Array.isArray(cur)) {
prev.total += cur.length;
totalAchievement += cur.length;
categories = Object.entries(achievement)
.map(([id, data]) => ({
id,
name: data.name,
order: data.order,
finished: checkList[id] !== undefined ? Object.values(checkList[id]).filter((e) => e === true).length : 0,
...data.achievements.reduce(
(prev, cur) => {
if (Array.isArray(cur)) {
prev.total += cur.length;
totalAchievement += cur.length;
for (const f of cur) {
totalPrimogem += f.reward;
for (const f of cur) {
totalPrimogem += f.reward;
const finished = checkList[id] !== undefined && checkList[id][f.id];
prev.primogem += finished ? f.reward : 0;
obtainedPrimogem += finished ? f.reward : 0;
const finished = checkList[id] !== undefined && checkList[id][f.id];
prev.primogem += finished ? f.reward : 0;
obtainedPrimogem += finished ? f.reward : 0;
finishedAchievement += finished ? 1 : 0;
}
} else {
prev.total += 1;
totalAchievement += 1;
totalPrimogem += cur.reward;
const finished = checkList[id] !== undefined && checkList[id][cur.id];
prev.primogem += finished ? cur.reward : 0;
obtainedPrimogem += finished ? cur.reward : 0;
finishedAchievement += finished ? 1 : 0;
}
} else {
prev.total += 1;
totalAchievement += 1;
totalPrimogem += cur.reward;
const finished = checkList[id] !== undefined && checkList[id][cur.id];
prev.primogem += finished ? cur.reward : 0;
obtainedPrimogem += finished ? cur.reward : 0;
finishedAchievement += finished ? 1 : 0;
}
return prev;
},
{ total: 0, primogem: 0 },
),
}));
return prev;
},
{ total: 0, primogem: 0 },
),
}))
.sort((a, b) => a.order - b.order);
}
function orderAchievement() {
@ -161,9 +164,11 @@
async function changeLocale(locale) {
const data = await import(`../../data/achievement/${locale}.json`);
achievement = data.default;
Object.entries(achievement).forEach(([id, data], i) => {
categories[i].name = data.name;
});
Object.entries(achievement)
.sort((a, b) => a[1].order - b[1].order)
.forEach(([id, data], i) => {
categories[i].name = data.name;
});
changeCategory(active, activeIndex, true);
}
@ -238,7 +243,7 @@
</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">
{#each categories as category, index}
{#each categories as category, index (category.id)}
<div
class="rounded-xl p-2 cursor-pointer flex flex-col {category.id === active ? 'bg-primary' : 'bg-item'}"
on:click={() => changeCategory(category.id, index)}
@ -246,9 +251,8 @@
<p class="font-semibold {category.id === active ? 'text-black' : 'text-white'}">{category.name}</p>
<div class="flex">
<p class="flex-1 {category.id === active ? 'text-gray-900' : 'text-gray-400'}">
{category.finished}
{$t('achievement.of')}
{category.total}
{category.finished}/{category.total}
({((category.finished / category.total) * 100).toFixed(0)}%)
</p>
<p class={category.id === active ? 'text-gray-900' : 'text-gray-400'}>
{category.primogem}

View File

@ -284,6 +284,8 @@
}
}
});
moraNeeded = moraNeeded + talentMaterial.mora;
}
function calculateTalent() {
@ -407,6 +409,14 @@
calculateTalent();
}
}
for (const [id, item] of Object.entries(talentMaterial.items)) {
if (ascensionResouce[id]) {
ascensionResouce[id].amount += item.amount;
} else {
ascensionResouce[id] = item;
}
}
}
changed = false;
@ -429,14 +439,6 @@
return prev;
}, {});
const talentRes = Object.keys(talentMaterial.items).reduce((prev, item) => {
if (talentMaterial.items[item].amount > 0) {
prev[item] = talentMaterial.items[item].amount;
}
return prev;
}, {});
addTodo({
type: 'character',
character: withAscension ? selectedCharacter : null,
@ -445,13 +447,11 @@
mora: moraNeeded,
...levelRes,
...ascensionRes,
...talentRes,
},
original: {
mora: moraNeeded,
...levelRes,
...ascensionRes,
...talentRes,
},
});
@ -460,6 +460,7 @@
addedToTodo = false;
}, 2000);
}
</script>
<div class="bg-item rounded-xl p-4">
@ -665,26 +666,6 @@
</tr>
{/if}
{/each}
{#each Object.entries(talentMaterial.items) as [id, item]}
{#if item.amount > 0}
<tr>
<td class="text-right border-b border-gray-700 py-1">
<span class="text-white mr-2 whitespace-no-wrap"
>{item.amount}
<Icon size={0.5} path={mdiClose} /></span
>
</td>
<td class="border-b border-gray-700 py-1">
<span class="text-white">
<span class="w-6 inline-block">
<img class="h-6 inline-block mr-1" src={`/images/items/${id}.png`} alt={item.name} />
</span>
{item.name}
</span>
</td>
</tr>
{/if}
{/each}
<tr>
<td class="text-right border-b border-gray-700 py-1">
<span class="text-white mr-2 whitespace-no-wrap"

View File

@ -1,7 +1,7 @@
<script>
import { t } from 'svelte-i18n';
import { onMount } from 'svelte';
import { mdiStar, mdiViewGrid, mdiViewList } from '@mdi/js';
import { mdiSortAscending, mdiSortDescending, mdiStar, mdiViewGrid, mdiViewList } from '@mdi/js';
import Icon from '../../components/Icon.svelte';
import TableHeader from '../../components/Table/TableHeader.svelte';
@ -9,59 +9,100 @@
import { characters } from '../../data/characters';
import { getAccountPrefix } from '../../stores/account';
import { readSave, updateSave } from '../../stores/saveManager';
import Select from '../../components/Select.svelte';
import Button from '../../components/Button.svelte';
const sortOptions = [
{ label: $t('characters.name'), value: 'name' },
{ label: $t('characters.element'), value: 'element' },
{ label: $t('characters.rarity'), value: 'rarity' },
{ label: $t('characters.weapon'), value: 'weapon' },
{ label: $t('characters.constellations'), value: 'constellation' },
];
let sortBySelect = null;
let sortBy = '';
let sortOrder = false;
let type = 'grid';
let showConstellation = false;
let constellation = {};
let elementFilter = {
pyro: true,
hydro: true,
anemo: true,
electro: true,
cryo: true,
dendro: true,
geo: true,
};
let weaponFilter = {
sword: true,
claymore: true,
polearm: true,
catalyst: true,
bow: true,
};
$: chars = Object.entries(characters).sort((a, b) => {
switch (sortBy) {
case 'name':
if (sortOrder) {
return a[1].name.localeCompare(b[1].name);
} else {
return b[1].name.localeCompare(a[1].name);
}
case 'element':
if (sortOrder) {
return a[1].element.name.localeCompare(b[1].element.name);
} else {
return b[1].element.name.localeCompare(a[1].element.name);
}
case 'rarity':
if (sortOrder) {
return a[1].rarity - b[1].rarity;
} else {
return b[1].rarity - a[1].rarity;
}
case 'weapon':
if (sortOrder) {
return a[1].weapon.name.localeCompare(b[1].weapon.name);
} else {
return b[1].weapon.name.localeCompare(a[1].weapon.name);
}
case 'hp':
if (sortOrder) {
return a[1].stats.hp - b[1].stats.hp;
} else {
return b[1].stats.hp - a[1].stats.hp;
}
case 'atk':
if (sortOrder) {
return a[1].stats.atk - b[1].stats.atk;
} else {
return b[1].stats.atk - a[1].stats.atk;
}
case 'def':
if (sortOrder) {
return a[1].stats.def - b[1].stats.def;
} else {
return b[1].stats.def - a[1].stats.def;
}
}
});
$: chars = Object.entries(characters)
.filter((e) => elementFilter[e[1].element.id] && weaponFilter[e[1].weapon.id])
.sort((a, b) => {
switch (sortBy) {
case 'name':
if (sortOrder) {
return a[1].name.localeCompare(b[1].name);
} else {
return b[1].name.localeCompare(a[1].name);
}
case 'element':
if (sortOrder) {
return a[1].element.name.localeCompare(b[1].element.name);
} else {
return b[1].element.name.localeCompare(a[1].element.name);
}
case 'rarity':
if (sortOrder) {
return a[1].rarity - b[1].rarity;
} else {
return b[1].rarity - a[1].rarity;
}
case 'weapon':
if (sortOrder) {
return a[1].weapon.name.localeCompare(b[1].weapon.name);
} else {
return b[1].weapon.name.localeCompare(a[1].weapon.name);
}
case 'hp':
if (sortOrder) {
return a[1].stats.hp - b[1].stats.hp;
} else {
return b[1].stats.hp - a[1].stats.hp;
}
case 'atk':
if (sortOrder) {
return a[1].stats.atk - b[1].stats.atk;
} else {
return b[1].stats.atk - a[1].stats.atk;
}
case 'def':
if (sortOrder) {
return a[1].stats.def - b[1].stats.def;
} else {
return b[1].stats.def - a[1].stats.def;
}
case 'constellation':
const ca = constellation[a[0]]
? constellation[a[0]].default + constellation[a[0]].wish + constellation[a[0]].manual - 1
: -999999;
const cb = constellation[b[0]]
? constellation[b[0]].default + constellation[b[0]].wish + constellation[b[0]].manual - 1
: -999999;
if (sortOrder) {
return ca - cb;
} else {
return cb - ca;
}
}
});
function sort(by) {
if (sortBy === by) {
@ -71,6 +112,22 @@
}
}
function orderSelectChanged() {
sort(sortBySelect.value);
}
function orderGridChange() {
sortOrder = !sortOrder;
}
function toggleElement(element) {
elementFilter[element] = !elementFilter[element];
}
function toggleWeapon(weapon) {
weaponFilter[weapon] = !weaponFilter[weapon];
}
async function processWishes() {
const chars = {
amber: {
@ -153,6 +210,7 @@
onMount(async () => {
await getConstellation();
});
</script>
<svelte:head>
@ -192,6 +250,114 @@
</div>
{#if type === 'grid'}
<div class="flex flex-col lg:flex-row flex-wrap px-4 md:pl-8 md:pr-4 mb-4">
<div class="flex mr-2 mb-2 lg:mb-0">
<Select
bind:selected={sortBySelect}
on:change={orderSelectChanged}
options={sortOptions}
className="w-full md:w-48 mr-2"
placeholder={$t('characters.sortBy')}
/>
<Button on:click={orderGridChange}>
<Icon path={sortOrder ? mdiSortAscending : mdiSortDescending} />
</Button>
</div>
<div class="flex flex-col md:flex-row">
<div class="flex items-center justify-center md:justify-start md:mr-4">
<button
on:click={() => toggleElement('pyro')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {elementFilter.pyro
? ''
: 'opacity-25'}"
>
<img src="/images/elements/pyro.png" alt="pyro" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleElement('hydro')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {elementFilter.hydro
? ''
: 'opacity-25'}"
>
<img src="/images/elements/hydro.png" alt="hydro" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleElement('anemo')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {elementFilter.anemo
? ''
: 'opacity-25'}"
>
<img src="/images/elements/anemo.png" alt="anemo" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleElement('electro')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {elementFilter.electro
? ''
: 'opacity-25'}"
>
<img src="/images/elements/electro.png" alt="electro" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleElement('cryo')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {elementFilter.cryo
? ''
: 'opacity-25'}"
>
<img src="/images/elements/cryo.png" alt="cryo" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleElement('geo')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {elementFilter.geo
? ''
: 'opacity-25'}"
>
<img src="/images/elements/geo.png" alt="geo" class="w-8 h-8" style="min-width: 2rem;" />
</button>
</div>
<div class="flex items-center justify-center md:justify-start">
<button
on:click={() => toggleWeapon('sword')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {weaponFilter.sword
? ''
: 'opacity-25'}"
>
<img src="/images/weapons/sword.png" alt="sword" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleWeapon('claymore')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {weaponFilter.claymore
? ''
: 'opacity-25'}"
>
<img src="/images/weapons/claymore.png" alt="claymore" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleWeapon('polearm')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {weaponFilter.polearm
? ''
: 'opacity-25'}"
>
<img src="/images/weapons/polearm.png" alt="polearm" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleWeapon('catalyst')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {weaponFilter.catalyst
? ''
: 'opacity-25'}"
>
<img src="/images/weapons/catalyst.png" alt="catalyst" class="w-8 h-8" style="min-width: 2rem;" />
</button>
<button
on:click={() => toggleWeapon('bow')}
class="rounded-xl hover:bg-black hover:bg-opacity-25 cursor-pointer p-2 focus:outline-none {weaponFilter.bow
? ''
: 'opacity-25'}"
>
<img src="/images/weapons/bow.png" alt="bow" class="w-8 h-8" style="min-width: 2rem;" />
</button>
</div>
</div>
</div>
<div class="px-4 md:pl-6 md:pr-4 flex flex-wrap max-w-screen-xl mt-2">
{#each chars as [id, char] (id)}
<a
@ -214,7 +380,7 @@
>
{#if constellation[id]}
<span class="mx-1 text-white text-xs font-semibold">
C{Math.max(0, (constellation[id].default + constellation[id].wish + constellation[id].manual) - 1)}
C{Math.max(0, constellation[id].default + constellation[id].wish + constellation[id].manual - 1)}
</span>
{/if}
<img class="w-4 h-4" src={`/images/elements/${char.element.id}.png`} alt={char.element.name} />
@ -240,6 +406,14 @@
<TableHeader on:click={() => sort('element')} sort={sortBy === 'element'} order={sortOrder} align="center">
{$t('characters.element')}
</TableHeader>
<TableHeader
on:click={() => sort('constellation')}
sort={sortBy === 'constellation'}
order={sortOrder}
align="center"
>
{$t('characters.const')}
</TableHeader>
<TableHeader on:click={() => sort('rarity')} sort={sortBy === 'rarity'} order={sortOrder} align="center">
{$t('characters.rarity')}
</TableHeader>
@ -266,6 +440,11 @@
<td class="text-center">
<img class="w-8 h-8 inline" src={`/images/elements/${char.element.id}.png`} alt={char.element.name} />
</td>
<td class="text-center">
C{constellation[id]
? Math.max(0, constellation[id].default + constellation[id].wish + constellation[id].manual - 1)
: 0}
</td>
<td class="text-center">
<Icon color={char.rarity === 5 ? '#B9812E' : '#AD76B0'} path={mdiStar} />
</td>
@ -319,4 +498,5 @@
padding-top: 0.85rem;
padding-bottom: 0.85rem;
}
</style>

View File

@ -1,362 +1,256 @@
<script context="module">
import setsData from '../../data/furnishing/sets/en.json';
import data from '../../data/furnishing/en.json';
export async function preload() {
return { data };
return { data, setsData };
}
</script>
<script>
import { onMount } from 'svelte';
import { locale, t } from 'svelte-i18n';
import debounce from 'lodash/debounce';
import { mdiInformationOutline, mdiMinus, mdiPlus } from '@mdi/js';
import TableHeader from '../../components/Table/TableHeader.svelte';
import Button from '../../components/Button.svelte';
import CharacterSelect from '../../components/CharacterSelect.svelte';
import { onMount } from 'svelte';
import Icon from '../../components/Icon.svelte';
import { mdiCheckCircleOutline, mdiClose } from '@mdi/js';
import { getAccountPrefix } from '../../stores/account';
import { readSave, updateSave } from '../../stores/saveManager';
export let data;
export let setsData;
let type = 'hall';
let items = [];
let max = 0;
let loading = true;
let furnishing = {};
let sets = [];
let saved = {};
let _saved = {};
let used = {};
let placed = {};
let character = {};
let charFilter = null;
const minEnergy = {
exterior: 0,
exterior2: 0,
exterior3: 0,
exterior4: 0,
hall: 150,
room1: 150,
room2: 150,
room3: 150,
corridor: 150,
};
const maxLoad = {
exterior: 8700,
exterior2: 8700,
exterior3: 8700,
exterior4: 8700,
hall: 8700,
room1: 3700,
room2: 3700,
room3: 3700,
corridor: 3700,
};
let currentUsage = {
exterior: {},
exterior2: {},
exterior3: {},
exterior4: {},
hall: {},
room1: {},
room2: {},
room3: {},
corridor: {},
};
$: currentLoad = Object.entries(currentUsage[type]).reduce(
(prev, [id, val]) => {
prev.load += data[id].load * val;
prev.energy += data[id].energy * val;
return prev;
},
{
load: 0,
energy: minEnergy[type],
},
);
function parseSets() {
const _sets = [];
furnishing = data;
used = {};
saved = { ..._saved };
for (const set of setsData) {
for (const [item, amount] of Object.entries(set.items)) {
if (placed[set.id] === true) {
if (used[item] === undefined) {
used[item] = 0;
}
let sortBy = 'ratio';
let sortOrder = false;
async function parseFurnishing() {
const currentType = type.startsWith('exterior') ? 'exterior' : 'interior';
items = Object.values(data)
.filter((e) => e.type === currentType || e.type === '')
.sort((a, b) => {
switch (sortBy) {
case 'ratio':
if (sortOrder) return a.ratio - b.ratio;
else return b.ratio - a.ratio;
case 'energy':
if (sortOrder) return a.energy - b.energy;
else return b.energy - a.energy;
case 'load':
if (sortOrder) return a.load - b.load;
else return b.load - a.load;
case 'using':
if (sortOrder) return (currentUsage[type][a.id] || 0) - (currentUsage[type][b.id] || 0);
else return (currentUsage[type][b.id] || 0) - (currentUsage[type][a.id] || 0);
case 'name':
if (sortOrder) return a.name.localeCompare(b.name);
else return b.name.localeCompare(a.name);
used[item] += amount;
saved[item] -= amount;
}
});
}
function sort(by) {
if (sortBy === by) {
sortOrder = !sortOrder;
} else {
sortBy = by;
}
}
parseFurnishing();
for (const set of setsData.sort((a, b) => b.gift - a.gift)) {
set.enough = {};
set.canBePlaced = true;
for (const [item, amount] of Object.entries(set.items)) {
let enough = true;
if (placed[set.id] !== true) {
enough = saved[item] >= amount;
}
set.enough[item] = enough;
if (!enough) set.canBePlaced = false;
}
_sets.push(set);
}
sets = _sets;
console.log(sets);
loading = false;
}
function changeType(_type) {
type = _type;
sortBy = 'ratio';
sortOrder = false;
parseFurnishing();
max = items[0].ratio;
}
function changeUsage(id, val) {
if (currentUsage[type][id] === undefined) currentUsage[type][id] = 0;
currentUsage[type][id] = Math.max(0, currentUsage[type][id] + val);
if (currentUsage[type][id] === 0) delete currentUsage[type][id];
function place(id) {
placed[id] = !placed[id];
saveData();
parseSets();
}
function checkCharacter(setId, id) {
if (character[setId] === undefined) {
character[setId] = {};
}
character[setId][id] = !character[setId][id];
saveData();
}
async function readLocalData() {
const prefix = getAccountPrefix();
const savedIventory = await readSave(`${prefix}furnishing-inventory`);
const savedSet = await readSave(`${prefix}furnishing-set-placed`);
const savedSetCharacters = await readSave(`${prefix}furnishing-set-character`);
if (savedIventory !== null) {
_saved = { ...savedIventory };
saved = { ...savedIventory };
}
if (savedSet !== null) {
placed = savedSet;
}
if (savedSetCharacters !== null) {
character = savedSetCharacters;
}
}
async function changeLocale(locale) {
data = (await import(`../../data/furnishing/${locale}.json`)).default;
parseFurnishing();
}
function calculateColor(percentage) {
const hue = (percentage / max) * 120;
return `color: hsl(${hue}, 100%, 60%);`;
}
function calculateColorLoad(percentage) {
const hue = Math.max((1 - percentage) * 120, 0);
return `color: hsl(${hue}, 100%, 60%);`;
setsData = (await import(`../../data/furnishing/sets/${locale}.json`)).default;
parseSets();
}
const saveData = debounce(async () => {
const data = currentUsage;
const prefix = getAccountPrefix();
await updateSave(`${prefix}furnishing`, data);
}, 2000);
await updateSave(`${prefix}furnishing-set-placed`, placed);
await updateSave(`${prefix}furnishing-set-character`, character);
}, 1000);
async function readLocalData() {
const prefix = getAccountPrefix();
const furnishingData = await readSave(`${prefix}furnishing`);
if (furnishingData !== null) {
currentUsage = {
...currentUsage,
...furnishingData,
};
function onCharFilterChanged() {
if (!loading) {
parseSets();
}
}
onMount(async () => {
parseFurnishing();
max = items[0].ratio;
await readLocalData();
locale.subscribe((val) => {
changeLocale(val);
});
});
// $: charFilter, onCharFilterChanged();
</script>
<svelte:head>
<title>Furnishing - Paimon.moe</title>
<title>Furnishing Sets - Paimon.moe</title>
<meta name="description" content="Genshin Impact Furnishing list with the load and energy values" />
<meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" />
</svelte:head>
<div class="lg:ml-64 pt-20 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="px-4 flex md:space-x-2 items-start md:items-center flex-col md:flex-row">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.title')}</h1>
<div class="flex items-center space-x-2">
<div
class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2 group relative"
style={calculateColorLoad(currentLoad.load / maxLoad[type])}
>
<Icon size={0.7} path={mdiInformationOutline} />
{$t('furnishing.load')}
{currentLoad.load} / {maxLoad[type]}
<div
class="hidden group-hover:block absolute left-0 transform translate-y-full
bg-white rounded-xl z-50 text-gray-900 px-4 py-2 w-screen max-w-xs md:max-w-sm"
style="bottom: -10px;"
>
<p>{$t('furnishing.info.0')}</p>
<p>{$t('furnishing.info.1')}</p>
</div>
</div>
<div class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2">
{$t('furnishing.energy')}
{currentLoad.energy}
</div>
<div class="lg:ml-64 pt-20 px-4 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="flex flex-col md:flex-row items-center md:space-x-2 space-y-2 md:space-y-0">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.sets.title')}</h1>
<div>
<a href="/furnishing/inventory">
<Button>{$t('furnishing.inventoryButton')}</Button>
</a>
<a href="/furnishing/list">
<Button>{$t('furnishing.listButton')}</Button>
</a>
</div>
</div>
<div class="px-4 flex space-x-2 mt-2 mb-2">
<button on:click={() => changeType('hall')} class="pill {type.startsWith('exterior') ? '' : 'active'}">
{$t('furnishing.interior')}
</button>
<button on:click={() => changeType('exterior')} class="pill {type.startsWith('exterior') ? 'active' : ''}">
{$t('furnishing.exterior')}
</button>
</div>
{#if type.startsWith('exterior')}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('exterior')} class="pill {type === 'exterior' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('exterior2')} class="pill {type === 'exterior2' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('exterior3')} class="pill {type === 'exterior3' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('exterior4')} class="pill {type === 'exterior4' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 4 } })}
</button>
</div>
{#if loading}
<p class="text-white">Loading...</p>
{:else}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('hall')} class="pill {type === 'hall' ? 'active' : ''}">
{$t('furnishing.hall')}
</button>
<button on:click={() => changeType('room1')} class="pill {type === 'room1' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('room2')} class="pill {type === 'room2' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('room3')} class="pill {type === 'room3' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('corridor')} class="pill {type === 'corridor' ? 'active' : ''}">
{$t('furnishing.corridor')}
</button>
<p class="text-gray-400 font-medium pb-2">
{$t('furnishing.sets.subtitle')}
</p>
<div class="w-full md:w-64">
<CharacterSelect bind:selected={charFilter} />
</div>
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-4 gap-2 flex-1 mt-2"
style="height: fit-content;"
>
{#each sets as set (set.id)}
{#if charFilter === null || (charFilter !== null && set.characters && set.characters.includes(charFilter.id))}
<div class="text-white p-2 rounded-xl flex flex-col bg-item">
<div class="w-full flex items-center justify-center relative">
<img
src="/images/furnishing/sets/{set.id}.png"
alt=""
class="w-full image relative object-contain rounded-xl"
/>
{#if placed[set.id]}
<div class="text-green-300 absolute bottom-0 right-0 p-2">
<Icon path={mdiCheckCircleOutline} size={1.5} />
</div>
{/if}
</div>
<div class="mt-1 flex flex-col h-full">
<p class="text-white mb-1">{set.name}</p>
{#if set.gift}
<div class="flex -m-1 py-1">
{#each set.characters as char}
<div class="relative cursor-pointer" on:click={() => checkCharacter(set.id, char)}>
<img src="/images/characters/{char}.png" class="w-10 h-10 rounded-full m-1" alt={char} />
{#if character[set.id]?.[char]}
<div class="text-green-300 absolute bottom-0 right-0">
<Icon path={mdiCheckCircleOutline} size={1} />
</div>
{/if}
</div>
{/each}
</div>
{/if}
<div class="flex -m-1 flex-wrap pt-1">
{#each Object.entries(set.items) as [item, amount]}
<button
class="rounded-xl {set.enough[item] === true
? 'bg-background'
: 'bg-red-900'} text-white px-2 furnishing-item focus:outline-none filter"
style="margin: 2px;"
>
<div class="flex items-center">
<img src="/images/furnishing/{item}.png" class="w-8 h-8" alt={item} />
<Icon path={mdiClose} size={0.5} />
<span class="inline-block w-2">{amount}</span>
</div>
<div class="popup text-left">
<p class="text-left mb-1">{furnishing[item].name}</p>
<table>
<tr>
<td>{$t('furnishing.sets.inInventory')}</td>
<td class="pl-2 text-center">{_saved[item] || 0}</td>
</tr>
<tr>
<td>{$t('furnishing.sets.used')}</td>
<td class="pl-2 text-center">{used[item] || 0}</td>
</tr>
<tr>
<td>{$t('furnishing.sets.available')}</td>
<td class="pl-2 text-center">{saved[item] || 0}</td>
</tr>
</table>
</div>
</button>
{/each}
</div>
<div class="flex-1" />
<Button className="mt-2" disabled={!set.canBePlaced} on:click={() => place(set.id)}>
{placed[set.id] ? $t('furnishing.sets.setUnplaced') : $t('furnishing.sets.setPlaced')}
</Button>
</div>
</div>
{/if}
{/each}
</div>
{/if}
<div class="flex mt-4 wrapper">
<div class="block overflow-x-auto xl:overflow-x-visible whitespace-no-wrap">
<div class="px-4 table">
<table class="w-full block pl-4 pr-4 py-2 md:pl-8 md:py-4 bg-item rounded-xl">
<tr>
<th />
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('name')}
sort={sortBy === 'name'}
order={sortOrder}
align="left"
>
{$t('furnishing.name')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('energy')}
sort={sortBy === 'energy'}
order={sortOrder}
align="center"
>
{$t('furnishing.energy')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('load')}
sort={sortBy === 'load'}
order={sortOrder}
align="center"
>
{$t('furnishing.load')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('ratio')}
sort={sortBy === 'ratio'}
order={sortOrder}
align="center"
>
{$t('furnishing.ratio')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('using')}
sort={sortBy === 'using'}
order={sortOrder}
align="center"
>
{$t('furnishing.using')}
</TableHeader>
</tr>
{#each items as item (item.id)}
<tr>
<td class="pr-4 h-12">
<img
src="/images/furnishing/{item.id}.png"
alt=""
class="h-12 w-12 image relative"
style="min-width: 3rem;"
/>
</td>
<td class="px-4 text-gray-200">{item.name}</td>
<td class="px-4 text-gray-200 text-center">{item.energy}</td>
<td class="px-4 text-gray-200 text-center">{item.load}</td>
<td class="px-4 text-gray-200 text-center" style={calculateColor(item.ratio)}>{item.ratio.toFixed(2)}</td>
<td class="px-4">
<div
class="flex justify-between items-center border-2 border-white border-opacity-25 rounded-xl text-gray-400"
>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, 1)}>
<Icon path={mdiPlus} />
</button>
<p
class="h-full px-2 text-center {currentUsage[type][item.id] > 0
? 'text-gray-200'
: 'text-gray-700'}"
style="min-width: 40px;"
>
{currentUsage[type][item.id] || 0}
</p>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, -1)}>
<Icon path={mdiMinus} />
</button>
</div>
</td>
</tr>
{/each}
</table>
</div>
</div>
</div>
</div>
<style>
.pill {
@apply rounded-2xl;
@apply border-2;
@apply border-white;
@apply border-opacity-25;
@apply text-white;
@apply px-4;
@apply py-1;
@apply outline-none;
@apply transition;
@apply duration-100;
@apply whitespace-no-wrap;
.popup {
@apply text-sm pt-1 hidden p-2 rounded-xl;
}
&:hover {
@apply border-primary;
}
.furnishing-item:focus {
@apply w-full;
&.active {
@apply bg-primary;
@apply border-primary;
@apply text-background;
.popup {
@apply block;
}
}
.image[alt]:after {
@apply block absolute top-0 left-0 w-full h-full bg-item;
content: '';
}
</style>

View File

@ -0,0 +1,266 @@
<script context="module">
import data from '../../data/furnishing/en.json';
import categories from '../../data/furnishing/category/en.json';
export async function preload() {
return { data, categories };
}
</script>
<script>
import { locale, t } from 'svelte-i18n';
import { onMount, tick } from 'svelte';
import { mdiMinus, mdiPlus } from '@mdi/js';
import debounce from 'lodash/debounce';
import Icon from '../../components/Icon.svelte';
import { readSave, updateSave } from '../../stores/saveManager';
import { getAccountPrefix } from '../../stores/account';
import Button from '../../components/Button.svelte';
export let data;
export let categories;
let loading = true;
let active = {
name: 'all',
type: 'all',
};
let inventoryContainer;
let categoryList = [];
let categoryData = {
'all-all': {
items: [],
count: {},
},
};
let saved = {};
async function parseFurnishing() {
categoryList = categories;
categoryData = {
'all-all': {
items: [],
count: {},
},
};
for (const category of categories) {
const categoryId = `${category.category}-${category.type}`;
categoryData[categoryId] = {
items: [],
count: {},
};
}
for (const item of Object.values(data)) {
if (item.category.category === 'Companion') continue;
if (item.category.type !== '') {
const categoryId = `${item.category.category}-${item.category.type}`;
categoryData[categoryId].items.push(item);
categoryData['all-all'].items.push(item);
if (saved[item.id] && saved[item.id] > 0) {
categoryData[categoryId].count[item.id] = true;
categoryData['all-all'].count[item.id] = true;
}
} else {
const categoryIdEx = `${item.category.category}-exterior`;
const categoryIdIn = `${item.category.category}-interior`;
categoryData[categoryIdEx]?.items.push(item);
categoryData[categoryIdIn]?.items.push(item);
categoryData['all-all'].items.push(item);
if (saved[item.id] && saved[item.id] > 0) {
if (categoryData[categoryIdEx]) categoryData[categoryIdEx].count[item.id] = true;
if (categoryData[categoryIdIn]) categoryData[categoryIdIn].count[item.id] = true;
categoryData['all-all'].count[item.id] = true;
}
}
}
loading = false;
}
async function changeCategory(category) {
active = category;
await tick();
inventoryContainer.scrollIntoView({
behavior: 'smooth',
});
}
async function readLocalData() {
const prefix = getAccountPrefix();
const savedIventory = await readSave(`${prefix}furnishing-inventory`);
if (savedIventory !== null) {
saved = savedIventory;
}
}
const saveData = debounce(async () => {
const prefix = getAccountPrefix();
await updateSave(`${prefix}furnishing-inventory`, saved);
}, 1000);
function changeItemValue(id, val) {
saved[id] = Math.max(0, val);
if (saved[id] === 0) {
delete categoryData[`${active.name}-${active.type}`].count[id];
categoryData = categoryData;
} else {
categoryData[`${active.name}-${active.type}`].count[id] = true;
}
saveData();
}
async function changeLocale(locale) {
categories = (await import(`../../data/furnishing/category/${locale}.json`)).default;
data = (await import(`../../data/furnishing/${locale}.json`)).default;
parseFurnishing();
}
onMount(async () => {
await readLocalData();
locale.subscribe((val) => {
changeLocale(val);
});
});
</script>
<svelte:head>
<title>Furnishing Inventory - Paimon.moe</title>
<meta name="description" content="Genshin Impact Furnishing list with the load and energy values" />
<meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" />
</svelte:head>
<div class="lg:ml-64 pt-20 px-4 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="flex flex-col md:flex-row items-center md:space-x-2 space-y-2 md:space-y-0">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.inventory.title')}</h1>
<a href="/furnishing">
<Button>{$t('furnishing.inventory.openSets')}</Button>
</a>
</div>
{#if loading}
<p class="text-white">Loading...</p>
{:else}
<p class="text-gray-400 font-medium pb-2">
{$t('furnishing.inventory.subtitle')}
</p>
<div class="flex flex-col lg:flex-row space-y-3 lg:space-y-0 lg:space-x-3 mt-4 md:mt-0">
<div class="flex flex-col space-y-2 lg:h-screen lg:overflow-auto lg:sticky lg:pr-1 pb-4 category">
<div
on:click={() => changeCategory({ name: 'all', type: 'all' })}
class="rounded-xl p-2 cursor-pointer flex text-white {active.name === 'all' ? 'bg-primary' : 'bg-item'}"
>
<p class="flex-1">{$t('furnishing.inventory.all')}</p>
<!-- <p class={Object.keys(categoryData['all-all'].count).length === 0 ? 'text-gray-600' : 'text-white'}>
{Object.keys(categoryData['all-all'].count).length}
</p> -->
</div>
{#each categoryList as category (`${category.category}-${category.type}`)}
{#if categoryData[`${category.category}-${category.type}`].items.length > 0}
<div
on:click={() => changeCategory({ name: category.category, type: category.type })}
class="rounded-xl p-2 cursor-pointer flex text-white {category.category === active.name &&
category.type === active.type
? 'bg-primary'
: 'bg-item'}"
>
<p class="flex-1">{category.name}</p>
<p
class={Object.keys(categoryData[`${category.category}-${category.type}`].count).length === 0
? 'text-gray-600'
: 'text-white'}
>
{Object.keys(categoryData[`${category.category}-${category.type}`].count).length}
</p>
</div>
{/if}
{/each}
</div>
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-4 gap-2 flex-1 pt-20 lg:pt-2"
style="height: max-content;"
bind:this={inventoryContainer}
>
{#each categoryData[`${active.name}-${active.type}`].items as item (item.id)}
<div class="text-white p-2 rounded-xl flex bg-item">
<div class="h-16 w-16 flex items-center justify-center">
<img
src="/images/furnishing/{item.id}.png"
alt=""
class="h-16 w-16 image relative"
style="min-width: 4rem;"
/>
</div>
<div class="ml-2 flex flex-col">
<p class="text-sm flex-1">
{item.name}
</p>
<div class="flex items-center bg-black bg-opacity-25 rounded-md" style="width: max-content;">
<button class="hover:text-primary" on:click={() => changeItemValue(item.id, (saved[item.id] || 0) + 1)}>
<Icon path={mdiPlus} />
</button>
<input
type="number"
value={saved[item.id] || 0}
on:change={(e) => changeItemValue(item.id, e.target.value)}
class="bg-transparent w-12 text-center {(saved[item.id] || 0) > 0 ? 'text-white' : 'text-gray-600'}"
/>
<button class="hover:text-primary" on:click={() => changeItemValue(item.id, (saved[item.id] || 0) - 1)}>
<Icon path={mdiMinus} />
</button>
</div>
</div>
</div>
{/each}
</div>
</div>
{/if}
</div>
<style>
.category {
width: 100%;
}
@screen lg {
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
@apply bg-transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.35);
@apply rounded-xl;
}
.category {
min-width: 15rem;
width: 15rem;
top: 0px;
padding-top: 8px;
}
}
.image[alt]:after {
@apply block absolute top-0 left-0 w-full h-full bg-item;
content: '';
}
input[type='number'] {
-moz-appearance: textfield;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
</style>

View File

@ -0,0 +1,381 @@
<script context="module">
import data from '../../data/furnishing/en.json';
export async function preload() {
return { data };
}
</script>
<script>
import { onMount } from 'svelte';
import { locale, t } from 'svelte-i18n';
import debounce from 'lodash/debounce';
import { mdiInformationOutline, mdiMinus, mdiPlus } from '@mdi/js';
import TableHeader from '../../components/Table/TableHeader.svelte';
import Icon from '../../components/Icon.svelte';
import Button from '../../components/Button.svelte';
import { getAccountPrefix } from '../../stores/account';
import { readSave, updateSave } from '../../stores/saveManager';
export let data;
let type = 'hall';
let items = [];
let max = 0;
const minEnergy = {
exterior: 0,
exterior2: 0,
exterior3: 0,
exterior4: 0,
hall: 150,
room1: 150,
room2: 150,
room3: 150,
room4: 150,
room5: 150,
corridor: 150,
};
const maxLoad = {
exterior: 10000,
exterior2: 10000,
exterior3: 10000,
exterior4: 10000,
hall: 10000,
room1: 4000,
room2: 4000,
room3: 4000,
room4: 4000,
room5: 4000,
corridor: 4000,
};
let currentUsage = {
exterior: {},
exterior2: {},
exterior3: {},
exterior4: {},
hall: {},
room1: {},
room2: {},
room3: {},
room4: {},
room5: {},
corridor: {},
};
$: currentLoad = Object.entries(currentUsage[type]).reduce(
(prev, [id, val]) => {
prev.load += data[id].load * val;
prev.energy += data[id].energy * val;
return prev;
},
{
load: 0,
energy: minEnergy[type],
},
);
let sortBy = 'ratio';
let sortOrder = false;
async function parseFurnishing() {
const currentType = type.startsWith('exterior') ? 'exterior' : 'interior';
items = Object.values(data)
.filter((e) => e.type === currentType || e.type === '')
.sort((a, b) => {
switch (sortBy) {
case 'ratio':
if (sortOrder) return a.ratio - b.ratio;
else return b.ratio - a.ratio;
case 'energy':
if (sortOrder) return a.energy - b.energy;
else return b.energy - a.energy;
case 'load':
if (sortOrder) return a.load - b.load;
else return b.load - a.load;
case 'using':
if (sortOrder) return (currentUsage[type][a.id] || 0) - (currentUsage[type][b.id] || 0);
else return (currentUsage[type][b.id] || 0) - (currentUsage[type][a.id] || 0);
case 'name':
if (sortOrder) return a.name.localeCompare(b.name);
else return b.name.localeCompare(a.name);
}
});
}
function sort(by) {
if (sortBy === by) {
sortOrder = !sortOrder;
} else {
sortBy = by;
}
parseFurnishing();
}
function changeType(_type) {
type = _type;
sortBy = 'ratio';
sortOrder = false;
parseFurnishing();
max = items[0].ratio;
}
function changeUsage(id, val) {
if (currentUsage[type][id] === undefined) currentUsage[type][id] = 0;
currentUsage[type][id] = Math.max(0, currentUsage[type][id] + val);
if (currentUsage[type][id] === 0) delete currentUsage[type][id];
saveData();
}
async function changeLocale(locale) {
data = (await import(`../../data/furnishing/${locale}.json`)).default;
parseFurnishing();
}
function calculateColor(percentage) {
const hue = (percentage / max) * 120;
return `color: hsl(${hue}, 100%, 60%);`;
}
function calculateColorLoad(percentage) {
const hue = Math.max((1 - percentage) * 120, 0);
return `color: hsl(${hue}, 100%, 60%);`;
}
const saveData = debounce(async () => {
const data = currentUsage;
const prefix = getAccountPrefix();
await updateSave(`${prefix}furnishing`, data);
}, 2000);
async function readLocalData() {
const prefix = getAccountPrefix();
const furnishingData = await readSave(`${prefix}furnishing`);
if (furnishingData !== null) {
currentUsage = {
...currentUsage,
...furnishingData,
};
}
}
onMount(async () => {
parseFurnishing();
max = items[0].ratio;
await readLocalData();
locale.subscribe((val) => {
changeLocale(val);
});
});
</script>
<svelte:head>
<title>Furnishing - Paimon.moe</title>
<meta name="description" content="Genshin Impact Furnishing list with the load and energy values" />
<meta property="og:description" content="Genshin Impact Furnishing list with the load and energy values" />
</svelte:head>
<div class="lg:ml-64 pt-20 lg:px-8 lg:pt-8 max-w-screen-xl">
<div class="px-4 flex md:space-x-2 items-start md:items-center flex-col md:flex-row">
<h1 class="font-display font-black text-3xl md:text-4xl text-white">{$t('furnishing.title')}</h1>
<div class="flex items-center space-x-2">
<div
class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2 group relative"
style={calculateColorLoad(currentLoad.load / maxLoad[type])}
>
<Icon size={0.7} path={mdiInformationOutline} />
{$t('furnishing.load')}
{currentLoad.load} / {maxLoad[type]}
<div
class="hidden group-hover:block absolute left-0 transform translate-y-full
bg-white rounded-xl z-50 text-gray-900 px-4 py-2 w-screen max-w-xs md:max-w-sm"
style="bottom: -10px;"
>
<p>{$t('furnishing.info.0')}</p>
<p>{$t('furnishing.info.1')}</p>
</div>
</div>
<div class="rounded-2xl border-2 border-white border-opacity-25 text-white px-4 py-2">
{$t('furnishing.energy')}
{currentLoad.energy}
</div>
<a href="/furnishing">
<Button>{$t('furnishing.inventory.openSets')}</Button>
</a>
</div>
</div>
<div class="px-4 flex space-x-2 mt-2 mb-2">
<button on:click={() => changeType('hall')} class="pill {type.startsWith('exterior') ? '' : 'active'}">
{$t('furnishing.interior')}
</button>
<button on:click={() => changeType('exterior')} class="pill {type.startsWith('exterior') ? 'active' : ''}">
{$t('furnishing.exterior')}
</button>
</div>
{#if type.startsWith('exterior')}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('exterior')} class="pill {type === 'exterior' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('exterior2')} class="pill {type === 'exterior2' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('exterior3')} class="pill {type === 'exterior3' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('exterior4')} class="pill {type === 'exterior4' ? 'active' : ''}">
{$t('furnishing.exteriorNum', { values: { number: 4 } })}
</button>
</div>
{:else}
<div class="px-4 flex space-x-2 mt-2 mb-4 overflow-x-auto">
<button on:click={() => changeType('hall')} class="pill {type === 'hall' ? 'active' : ''}">
{$t('furnishing.hall')}
</button>
<button on:click={() => changeType('room1')} class="pill {type === 'room1' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 1 } })}
</button>
<button on:click={() => changeType('room2')} class="pill {type === 'room2' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 2 } })}
</button>
<button on:click={() => changeType('room3')} class="pill {type === 'room3' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 3 } })}
</button>
<button on:click={() => changeType('corridor')} class="pill {type === 'corridor' ? 'active' : ''}">
{$t('furnishing.corridor')}
</button>
<button on:click={() => changeType('room4')} class="pill {type === 'room4' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 4 } })}
</button>
<button on:click={() => changeType('room5')} class="pill {type === 'room5' ? 'active' : ''}">
{$t('furnishing.room', { values: { number: 5 } })}
</button>
</div>
{/if}
<div class="flex mt-4 wrapper">
<div class="block overflow-x-auto xl:overflow-x-visible whitespace-no-wrap">
<div class="px-4 table">
<table class="w-full block pl-4 pr-4 py-2 md:pl-8 md:py-4 bg-item rounded-xl">
<tr>
<th />
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('name')}
sort={sortBy === 'name'}
order={sortOrder}
align="left"
>
{$t('furnishing.name')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('energy')}
sort={sortBy === 'energy'}
order={sortOrder}
align="center"
>
{$t('furnishing.energy')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('load')}
sort={sortBy === 'load'}
order={sortOrder}
align="center"
>
{$t('furnishing.load')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('ratio')}
sort={sortBy === 'ratio'}
order={sortOrder}
align="center"
>
{$t('furnishing.ratio')}
</TableHeader>
<TableHeader
className="sticky top-0 bg-item z-30"
on:click={() => sort('using')}
sort={sortBy === 'using'}
order={sortOrder}
align="center"
>
{$t('furnishing.using')}
</TableHeader>
</tr>
{#each items as item (item.id)}
<tr>
<td class="pr-4 h-12">
<img
src="/images/furnishing/{item.id}.png"
alt=""
class="h-12 w-12 image relative"
style="min-width: 3rem;"
/>
</td>
<td class="px-4 text-gray-200">{item.name}</td>
<td class="px-4 text-gray-200 text-center">{item.energy}</td>
<td class="px-4 text-gray-200 text-center">{item.load}</td>
<td class="px-4 text-gray-200 text-center" style={calculateColor(item.ratio)}>{item.ratio.toFixed(2)}</td>
<td class="px-4">
<div
class="flex justify-between items-center border-2 border-white border-opacity-25 rounded-xl text-gray-400"
>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, 1)}>
<Icon path={mdiPlus} />
</button>
<p
class="h-full px-2 text-center {currentUsage[type][item.id] > 0
? 'text-gray-200'
: 'text-gray-700'}"
style="min-width: 40px;"
>
{currentUsage[type][item.id] || 0}
</p>
<button class="hover:text-primary" on:click={() => changeUsage(item.id, -1)}>
<Icon path={mdiMinus} />
</button>
</div>
</td>
</tr>
{/each}
</table>
</div>
</div>
</div>
</div>
<style>
.pill {
@apply rounded-2xl;
@apply border-2;
@apply border-white;
@apply border-opacity-25;
@apply text-white;
@apply px-4;
@apply py-1;
@apply outline-none;
@apply transition;
@apply duration-100;
@apply whitespace-no-wrap;
&:hover {
@apply border-primary;
}
&.active {
@apply bg-primary;
@apply border-primary;
@apply text-background;
}
}
.image[alt]:after {
@apply block absolute top-0 left-0 w-full h-full bg-item;
content: '';
}
</style>

View File

@ -15,6 +15,7 @@
import Discord from './_index/discord.svelte';
import Twitter from './_index/twitter.svelte';
import Achievement from './_index/achievement.svelte';
import Furnishing from './_index/furnishing.svelte';
let refreshLayout;
@ -33,6 +34,7 @@
}, 1);
});
});
</script>
<svelte:head>
@ -55,10 +57,11 @@
<Event on:done={onDone} />
<Item on:done={onDone} />
<Discord on:done={onDone} />
<div class="flex flex-col space-y-4">
<Calculator on:done={onDone} />
<Achievement on:done={onDone} />
</div>
<Calculator on:done={onDone} />
<Achievement on:done={onDone} />
<Twitter on:done={onDone} />
<Furnishing on:done={onDone} />
<!-- <div class="flex flex-col space-y-4">
</div> -->
</Masonry>
</div>

View File

@ -8,12 +8,14 @@
dayjs.extend(duration);
dayjs.extend(timezone);
import { getTimeDifference, server } from '../../stores/server';
import { getTimeDifference, getTimeDifferenceAsia, getTimeOffset, server } from '../../stores/server';
import { eventsData } from '../../data/timeline';
import Checkbox from '../../components/Checkbox.svelte';
import EventItem from './_item.svelte';
import DetailModal from './_detail.svelte';
import { getAccountPrefix } from '../../stores/account';
import { readSave } from '../../stores/saveManager';
const { open: openModal } = getContext('simple-modal');
@ -24,6 +26,7 @@
let showAsLocalTime = true;
let timeDifference = 0;
let timeDifferenceEvent = 0;
let timeDifferenceAsia = 0;
let dayWidth = 50;
const eventHeight = 36;
@ -41,6 +44,8 @@
let browserTimeZone = '';
let firstLoad = true;
function openDetail(event) {
openModal(
DetailModal,
@ -56,7 +61,12 @@
}
function convertToDate(e, i) {
const start = dayjs(e.start, 'YYYY-MM-DD HH:mm:ss').subtract(timeDifferenceEvent, 'minute');
let start;
if (e.timezoneDependent) {
start = dayjs(e.start, 'YYYY-MM-DD HH:mm:ss').subtract(timeDifferenceAsia, 'minute');
} else {
start = dayjs(e.start, 'YYYY-MM-DD HH:mm:ss').subtract(timeDifferenceEvent, 'minute');
}
const end = dayjs(e.end, 'YYYY-MM-DD HH:mm:ss').subtract(timeDifferenceEvent, 'minute');
const duration = end.diff(start, 'day', true);
@ -140,13 +150,16 @@
async function toggleLocalTime() {
const diff = getTimeDifference();
console.log('toggle local time', showAsLocalTime);
if (showAsLocalTime) {
timeDifferenceEvent = diff;
timeDifference = 0;
timeDifferenceAsia = getTimeDifferenceAsia();
} else {
timeDifferenceEvent = 0;
timeDifference = diff;
timeDifferenceAsia = (8 - getTimeOffset()) * 60;
}
today = dayjs().add(timeDifference, 'minute');
@ -167,22 +180,36 @@
top: 0,
behavior: 'smooth',
});
firstLoad = false;
}
onMount(async () => {
await toggleLocalTime();
const prefix = getAccountPrefix();
const serverSave = await readSave(`${prefix}server`);
if (serverSave !== null) {
console.log(serverSave);
server.set(serverSave);
}
browserTimeZone = dayjs.tz.guess();
toggleLocalTime();
const interval = setInterval(() => {
today = dayjs().add(timeDifference, 'minute');
}, 1000);
browserTimeZone = dayjs.tz.guess();
return () => {
clearInterval(interval);
};
});
function onCheckLocalTime() {
if (!firstLoad) {
toggleLocalTime();
}
}
function transformScroll(event) {
if (!event.deltaY) {
return;
@ -195,7 +222,8 @@
}
$: todayOffset = Math.abs(firstDay.diff(today, 'day', true));
$: showAsLocalTime, toggleLocalTime();
$: showAsLocalTime, onCheckLocalTime();
</script>
<svelte:head>
@ -214,7 +242,9 @@
<h1 class="font-display px-4 md:px-8 font-black text-5xl text-white">{$t('timeline.title')}</h1>
{#if !loading}
<div class="px-4 md:px-8 text-white select-none">
<Checkbox bind:checked={showAsLocalTime}>{$t('timeline.localTime')} ({browserTimeZone} - {$server} Server)</Checkbox>
<Checkbox bind:checked={showAsLocalTime}>
{$t('timeline.localTime')} ({browserTimeZone} - {$server} Server)
</Checkbox>
</div>
<div class="w-full overflow-x-auto px-4 md:px-8" bind:this={timelineContainer} on:wheel={transformScroll}>
<div
@ -307,4 +337,5 @@
background: rgba(0, 0, 0, 0.35);
@apply rounded-xl;
}
</style>

View File

@ -270,6 +270,7 @@
$: $todos, updateSummary();
$: columnCount, updateId();
</script>
<svelte:head>
@ -456,7 +457,7 @@
</Button>
</div>
<table class="w-full">
{#each Object.entries(todo.resources) as [id, amount]}
{#each Object.entries(todo.resources).sort((a, b) => b[1] - a[1]) as [id, amount]}
<tr>
<td class="text-right border-b border-gray-700 py-1">
<span class={`${amount === 0 ? 'line-through text-gray-600' : 'text-white'} mr-2 whitespace-no-wrap`}>
@ -496,4 +497,5 @@
@apply border-b-0;
}
}
</style>

View File

@ -0,0 +1,123 @@
<script context="module">
import data from '../../data/weapons/en.json';
import { weaponList } from '../../data/weaponList.js';
export async function preload(page) {
const { id } = page.params;
const weapon = data[id];
const materials = weaponList[id].ascension[0].items;
return { id, weapon, materials };
}
</script>
<script>
import { mdiCircle, mdiStar } from '@mdi/js';
import { locale, t } from 'svelte-i18n';
import Icon from '../../components/Icon.svelte';
import { onMount } from 'svelte';
const rarity = {
1: 'text-white',
2: 'text-green-400',
3: 'text-primary',
4: 'text-rare-from',
5: 'text-legendary-from',
};
/* prettier-ignore */
const showedIndex = [1, 6, 11, 16, 20, 21, 26, 31, 36, 41, 42, 47, 52, 53, 58, 63, 64, 69, 74, 75, 80, 85, 86, 91, 96];
const level = [1, 5, 10, 15, 20, 20, 25, 30, 35, 40, 40, 45, 50, 50, 55, 60, 60, 65, 70, 70, 75, 80, 80, 85, 90];
const ascen = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6];
const ascenSpan = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3];
export let id;
export let weapon;
// export let materials;
async function changeLocale(locale) {
const _data = await import(`../../data/weapons/${locale}.json`);
weapon = _data.default[id];
}
onMount(async () => {
locale.subscribe((val) => {
changeLocale(val);
});
});
$: multiplier = weapon.secondary.name === 'em' ? 1 : 100;
$: suffix = weapon.secondary.name === 'em' ? '' : '%';
</script>
<svelte:head>
<title>{weapon.name} - Paimon.moe</title>
</svelte:head>
<div class="lg:ml-64 pt-20 lg:pt-8 max-w-screen-xl md:pl-4">
<div class="flex flex-col-reverse xl:flex-row items-start">
<div class="px-4">
<h1 class="font-display font-black text-4xl md:text-5xl text-white">{weapon.name}</h1>
<div class="{rarity[weapon.rarity]} text-2xl flex items-center z-0 -mt-2 md:-mt-4">
{#each [...new Array(weapon.rarity)] as _}
<Icon path={mdiStar} />
{/each}
<Icon path={mdiCircle} size={0.4} className="mx-2 mt-1" color="white" />
<p class="text-base text-white font-semibold mt-1">{$t(`weapon.${weapon.type}`)}</p>
</div>
<p class="text-gray-200">{weapon.description}</p>
{#if weapon.skill.name}
<div class="p-4 rounded-xl bg-item flex flex-col mt-4">
<p class="font-black font-display text-xl text-white">{weapon.skill.name}</p>
<p class="skill-description text-white">{@html weapon.skill.description}</p>
</div>
{/if}
<div class="mt-4 block overflow-x-auto whitespace-no-wrap md:w-auto">
<div style="width: min-content;">
<div class="table max-w-full rounded-xl border border-gray-200 border-opacity-25">
<table class="text-gray-200 w-full">
<tr>
<td class="text-center whitespace-no-wrap border-gray-700 font-semibold px-2">
{$t('weapon.asc')}
</td>
<td class="text-center whitespace-no-wrap border-gray-700 font-semibold px-2">
{$t('weapon.lvl')}
</td>
<td class="text-center whitespace-no-wrap border-gray-700 font-semibold px-2">
{$t('weapon.baseAtk')}
</td>
{#if weapon.secondary.name}
<td class="text-center whitespace-no-wrap border-gray-700 font-semibold px-2">
{$t(`weapon.${weapon.secondary.name}`)}
</td>
{/if}
</tr>
{#each showedIndex as index, i}
<tr>
{#if [0, 5, 10, 13, 16, 19, 22].includes(i)}
<td rowspan={ascenSpan[i]} class="text-center border-t border-gray-700 px-2">{ascen[i]}</td>
{/if}
<td class="text-center border-t border-gray-700 px-2">{level[i]}</td>
<td class="text-center border-t border-gray-700 px-2">{Math.round(weapon.atk[index])}</td>
{#if weapon.secondary.stats}
<td class="text-center border-t border-gray-700 px-2">
{Math.round(weapon.secondary.stats[index] * multiplier)}{suffix}
</td>
{/if}
</tr>
{/each}
</table>
</div>
</div>
</div>
</div>
<img class="w-64 h-64 ml-4 max-w-full" src="/images/weapons/{id}.png" alt={weapon.name} />
</div>
</div>
<style>
td:not(:last-child) {
@apply border-r;
}
</style>

View File

@ -0,0 +1,165 @@
<script context="module">
import data from '../../data/weapons/en.json';
export async function preload() {
return { data };
}
</script>
<script>
import { onMount } from 'svelte';
import { locale, t } from 'svelte-i18n';
import TableHeader from '../../components/Table/TableHeader.svelte';
export let data;
let weaponList = [];
let sortBy = 'name';
let sortOrder = true;
const rarity = {
2: 'text-green-400',
3: 'text-primary',
4: 'text-rare-from',
5: 'text-legendary-from',
};
function sort(by) {
if (sortBy === by) {
sortOrder = !sortOrder;
} else {
sortBy = by;
}
}
function process() {
const _weapons = [];
for (const [id, weapon] of Object.entries(data)) {
if (['amber_bead', 'ebony_bow', 'quartz', 'the_flagstaff', 'freedom-sworn'].includes(id)) continue;
_weapons.push({
id,
name: weapon.name,
type: weapon.type,
rarity: weapon.rarity,
atk: weapon.atk[weapon.atk.length - 1].toFixed(0),
secondary: weapon.secondary.name
? `${$t(`weapon.${weapon.secondary.name}`)} ${(
weapon.secondary.stats[weapon.secondary.stats.length - 1] * (weapon.secondary.name === 'em' ? 1 : 100)
).toFixed(0)}${weapon.secondary.name === 'em' ? '' : '%'}`
: '-',
});
}
weaponList = _weapons;
}
process();
$: weapons = weaponList.sort((a, b) => {
switch (sortBy) {
case 'name':
if (sortOrder) {
return a.name.localeCompare(b.name);
} else {
return b.name.localeCompare(a.name);
}
case 'type':
if (sortOrder) {
return a.type.localeCompare(b.type);
} else {
return b.type.localeCompare(a.type);
}
case 'rarity':
if (sortOrder) {
return a.rarity - b.rarity;
} else {
return b.rarity - a.rarity;
}
case 'atk':
if (sortOrder) {
return a.atk - b.atk;
} else {
return b.atk - a.atk;
}
case 'secondary':
if (sortOrder) {
return a.secondary.localeCompare(b.secondary);
} else {
return b.secondary.localeCompare(a.secondary);
}
}
});
async function changeLocale(locale) {
const _data = await import(`../../data/weapons/${locale}.json`);
data = _data.default;
process();
}
onMount(async () => {
locale.subscribe((val) => {
changeLocale(val);
});
});
</script>
<svelte:head>
<title>Weapon List - Paimon.moe</title>
<meta name="description" content="Genshin Impact Weapon list and stats" />
<meta property="og:description" content="Genshin Impact Weapon list and stats" />
</svelte:head>
<div class="lg:ml-64 pt-20 lg:pt-8">
<h1 class="font-display px-4 md:px-8 font-black text-5xl text-white">{$t('weapon.title')}</h1>
<p class="text-gray-400 px-4 md:px-8 font-medium pb-4" style="margin-top: -1rem;">
{$t('weapon.subtitle')}
</p>
<div class="block overflow-x-auto whitespace-no-wrap pb-8">
<div class="px-4 md:px-8 table max-w-full">
<table class="w-full block p-4 bg-item rounded-xl">
<thead>
<TableHeader />
<TableHeader on:click={() => sort('name')} sort={sortBy === 'name'} order={sortOrder}>
{$t('weapon.name')}
</TableHeader>
<TableHeader on:click={() => sort('type')} sort={sortBy === 'type'} order={sortOrder}>
{$t('weapon.type')}
</TableHeader>
<TableHeader on:click={() => sort('rarity')} sort={sortBy === 'rarity'} order={sortOrder}>
{$t('weapon.rarity')}
</TableHeader>
<TableHeader on:click={() => sort('atk')} sort={sortBy === 'atk'} order={sortOrder}>
{$t('weapon.atk')}
</TableHeader>
<TableHeader on:click={() => sort('secondary')} sort={sortBy === 'secondary'} order={sortOrder}>
{$t('weapon.secondary')}
</TableHeader>
</thead>
<tbody class="text-white">
{#each weapons as weapon (weapon.id)}
<a href="/weapons/{weapon.id}" style="display: contents;">
<tr class="cursor-pointer hover:bg-background">
<td class="border-gray-700 border-t py-1 text-center h-12">
<img src="/images/weapons/{weapon.id}.png" alt={weapon.type} class="h-full w-auto inline" />
</td>
<td class="border-gray-700 border-t py-1 pl-4 pr-2">
{weapon.name}
</td>
<td class="border-gray-700 border-t py-1 text-center h-8">
<img src="/images/weapons/{weapon.type}.png" alt={weapon.type} class="h-full w-auto inline" />
</td>
<td class="border-gray-700 border-t py-1 text-center text-2xl {rarity[weapon.rarity]}"></td>
<td class="border-gray-700 border-t py-1 px-1 text-center">
{weapon.atk}
</td>
<td class="border-gray-700 border-t py-1 pl-4">
{weapon.secondary}
</td>
</tr>
</a>
{/each}
</tbody>
</table>
</div>
</div>
</div>

View File

@ -44,9 +44,11 @@
let oldest = dayjs().year(2200);
if (data !== null) {
newest = dayjs(data.pulls[data.pulls.length - 1].time);
oldest = dayjs(data.pulls[0].time);
pulls = data.pulls;
if (data.pulls.length > 0) {
newest = dayjs(data.pulls[data.pulls.length - 1].time);
oldest = dayjs(data.pulls[0].time);
pulls = data.pulls;
}
}
return {
@ -105,12 +107,14 @@
time: e[1],
type: e[0],
pity: 0,
manualInput: true,
}));
const end = append.map((e) => ({
id: e[2],
time: e[1],
type: e[0],
pity: 0,
manualInput: true,
}));
const combined = [...beginning, ...data, ...end];
@ -230,7 +234,7 @@
'character-event': 'Character Event Wish',
'weapon-event': 'Weapon Event Wish',
standard: 'Permanent Wish',
beginners: "Novice Wishes",
beginners: 'Novice Wishes',
};
const weapons = Object.values(weaponList);

View File

@ -5,11 +5,12 @@
import Item from './_item.svelte';
import ItemNew from './_itemNew.svelte';
let showOld = [false, false];
let showOld = [false, false, false, false];
function showOldTally(index) {
showOld[index] = true;
}
</script>
<svelte:head>
@ -25,6 +26,13 @@
</p>
<div class="px-4 md:px-8">
<ItemNew type="character" banner={banners.characters[13]} id={300014} featured={['klee']} />
<ItemNew
type="weapon"
banner={banners.weapons[12]}
id={400013}
featured={['lost_prayer_to_the_sacred_winds', 'skyward_pride']}
/>
<ItemNew type="character" banner={banners.characters[12]} id={300013} featured={['eula']} />
<ItemNew
type="weapon"
@ -32,10 +40,23 @@
id={400012}
featured={['song_of_broken_pines', 'aquila_favonia']}
/>
<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']} />
<ItemNew type="character" banner={banners.standard[0]} id={200001} />
{#if showOld[0]}
<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']}
/>
{:else}
<Button on:click={() => showOldTally(0)}>
{$t('wish.tally.show')}
{banners.characters[11].name} & {banners.weapons[10].name}
</Button>
<div class="mb-2" />
{/if}
{#if showOld[1]}
<Item type="character" banner={banners.characters[10]} id={300011} featured={['tartaglia', 'rosaria']} />
<Item
type="weapon"
@ -44,13 +65,13 @@
featured={['skyward_harp', 'lost_prayer_to_the_sacred_winds']}
/>
{:else}
<Button on:click={() => showOldTally(0)}>
<Button on:click={() => showOldTally(1)}>
{$t('wish.tally.show')}
{banners.characters[10].name} & {banners.weapons[9].name}
</Button>
<div class="mb-2" />
{/if}
{#if showOld[1]}
{#if showOld[2]}
<Item type="character" banner={banners.characters[9]} id={300010} featured={['venti']} />
<Item
type="weapon"
@ -59,17 +80,17 @@
featured={['skyward_harp', 'lost_prayer_to_the_sacred_winds']}
/>
{:else}
<Button on:click={() => showOldTally(1)}>
<Button on:click={() => showOldTally(2)}>
{$t('wish.tally.show')}
{banners.characters[9].name} & {banners.weapons[8].name}
</Button>
<div class="mb-2" />
{/if}
{#if showOld[2]}
{#if showOld[3]}
<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(2)}>
<Button on:click={() => showOldTally(3)}>
{$t('wish.tally.show')}
{banners.characters[8].name} & {banners.weapons[7].name}
</Button>

View File

@ -21,14 +21,21 @@ const weekdays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'frida
export const getTimeOffset = () => {
return timeOffset[get(server)];
}
};
export const getTimeDifference = () => {
const now = dayjs();
const local = now.utcOffset();
const serverTime = now.utcOffset(timeOffset[get(server)]).utcOffset();
return serverTime - local;
}
};
export const getTimeDifferenceAsia = () => {
const now = dayjs();
const local = now.utcOffset();
const serverTime = now.utcOffset(timeOffset.Asia).utcOffset();
return serverTime - local;
};
export const getCurrentDay = () => {
const time = dayjs().utcOffset(timeOffset[get(server)]);

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Some files were not shown because too many files have changed in this diff Show More