Add furnishing sets

This commit is contained in:
Made Baruna 2021-06-12 16:10:35 +07:00
parent 29bd9dc517
commit d12257812e
No known key found for this signature in database
GPG key ID: 5AA5DA16AA5DCEAD
101 changed files with 901 additions and 311 deletions
src
static/images/furnishing
a_guide_in_the_summer_woods.pnga_messenger_in_the_summer_woods.pngballad-spinning_windwheel.pngcloudy_haze_bed.pngcountryside_tea_stand.pngembroidered_curtains.pngembroidered_lantern_lofty_grandeur.pngfeather-light_praise.pngfloral_screen_jade_and_gold.pngglazed_porcelain_tea_set_embracing_truth.pnghalf-constructed_fence.pnglantern-lit_stage_crescendo.pngmulti-seat_round_pine_table.pngnewly_grown_grapevines.pngpure_gorgeous_summer.pngrainproof_fir_cargo_container.png
sets
silk_curtains_fetching_the_sanguine_sky.pngsimple_wooden_fence.pngsoil-carrying_wooden_barrel.pngstone_cup-shaped_pool.pngsturdy_wooden_barrel.pngsummer_forest_banquet.pngtall_wooden_pole.pngtasseled_lantern_deck_the_streets.pngtemporary_work_quarters.pngthe_adventurers_treasures.pngthe_blue_oceans_treasure.pngthundering_heavens_drum.pngwitchs_chorus.png

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

@ -604,7 +604,24 @@
"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",

View file

@ -568,6 +568,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

@ -1,161 +1,128 @@
<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: 10000,
exterior2: 10000,
exterior3: 10000,
exterior4: 10000,
hall: 10000,
room1: 4000,
room2: 4000,
room3: 4000,
corridor: 4000,
};
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) => {
@ -163,203 +130,127 @@
});
});
// $: 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;
&:hover {
@apply border-primary;
}
&.active {
@apply bg-primary;
@apply border-primary;
@apply text-background;
}
.popup {
@apply text-sm pt-1 hidden p-2 rounded-xl;
}
.image[alt]:after {
@apply block absolute top-0 left-0 w-full h-full bg-item;
content: '';
.furnishing-item:focus {
@apply w-full;
.popup {
@apply block;
}
}
</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: fit-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: fit-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>

Binary file not shown.

After

(image error) Size: 8.1 KiB

Binary file not shown.

After

(image error) Size: 7.8 KiB

Binary file not shown.

Before

(image error) Size: 31 KiB

After

(image error) Size: 28 KiB

Binary file not shown.

Before

(image error) Size: 27 KiB

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 40 KiB

Binary file not shown.

After

(image error) Size: 5.3 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 5.8 KiB

Binary file not shown.

After

(image error) Size: 7.3 KiB

Binary file not shown.

After

(image error) Size: 24 KiB

Binary file not shown.

After

(image error) Size: 18 KiB

Binary file not shown.

After

(image error) Size: 37 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 281 KiB

Binary file not shown.

After

(image error) Size: 360 KiB

Binary file not shown.

After

(image error) Size: 343 KiB

Binary file not shown.

After

(image error) Size: 342 KiB

Binary file not shown.

After

(image error) Size: 424 KiB

Binary file not shown.

After

(image error) Size: 319 KiB

Binary file not shown.

After

(image error) Size: 323 KiB

Binary file not shown.

After

(image error) Size: 347 KiB

Binary file not shown.

After

(image error) Size: 355 KiB

Binary file not shown.

After

(image error) Size: 389 KiB

Binary file not shown.

After

(image error) Size: 377 KiB

Binary file not shown.

After

(image error) Size: 357 KiB

Binary file not shown.

After

(image error) Size: 423 KiB

Binary file not shown.

After

(image error) Size: 359 KiB

Binary file not shown.

After

(image error) Size: 329 KiB

Binary file not shown.

After

(image error) Size: 371 KiB

Binary file not shown.

After

(image error) Size: 358 KiB

Binary file not shown.

After

(image error) Size: 332 KiB

Binary file not shown.

After

(image error) Size: 399 KiB

Binary file not shown.

After

(image error) Size: 284 KiB

Binary file not shown.

After

(image error) Size: 405 KiB

Binary file not shown.

After

(image error) Size: 419 KiB

Binary file not shown.

After

(image error) Size: 627 KiB

Binary file not shown.

After

(image error) Size: 416 KiB

Binary file not shown.

After

(image error) Size: 374 KiB

Binary file not shown.

After

(image error) Size: 395 KiB

Binary file not shown.

After

(image error) Size: 428 KiB

Binary file not shown.

After

(image error) Size: 304 KiB

Binary file not shown.

After

(image error) Size: 641 KiB

Binary file not shown.

After

(image error) Size: 368 KiB

Binary file not shown.

After

(image error) Size: 415 KiB

Binary file not shown.

After

(image error) Size: 379 KiB

Binary file not shown.

After

(image error) Size: 328 KiB

Binary file not shown.

After

(image error) Size: 392 KiB

Binary file not shown.

After

(image error) Size: 295 KiB

Binary file not shown.

After

(image error) Size: 375 KiB

Binary file not shown.

After

(image error) Size: 350 KiB

Binary file not shown.

After

(image error) Size: 364 KiB

Binary file not shown.

After

(image error) Size: 308 KiB

Binary file not shown.

After

(image error) Size: 383 KiB

Binary file not shown.

After

(image error) Size: 343 KiB

Binary file not shown.

After

(image error) Size: 389 KiB

Binary file not shown.

After

(image error) Size: 398 KiB

Binary file not shown.

After

(image error) Size: 307 KiB

Binary file not shown.

After

(image error) Size: 330 KiB

Binary file not shown.

After

(image error) Size: 19 KiB

Binary file not shown.

After

(image error) Size: 18 KiB

Binary file not shown.

After

(image error) Size: 6.8 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 16 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 5.1 KiB

Binary file not shown.

After

(image error) Size: 5.6 KiB

Binary file not shown.

After

(image error) Size: 40 KiB

Binary file not shown.

After

(image error) Size: 30 KiB

Binary file not shown.

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 32 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

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