Add furnishing sets
1
src/data/furnishing/category/en.json
Normal 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"}]
|
1
src/data/furnishing/category/fr.json
Normal 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"}]
|
1
src/data/furnishing/category/id.json
Normal 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"}]
|
1
src/data/furnishing/category/ko.json
Normal 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"}]
|
1
src/data/furnishing/category/pt.json
Normal 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"}]
|
1
src/data/furnishing/category/ru.json
Normal 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"}]
|
1
src/data/furnishing/category/zh.json
Normal 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"}]
|
1
src/data/furnishing/sets/en.json
Normal file
1
src/data/furnishing/sets/fr.json
Normal file
1
src/data/furnishing/sets/id.json
Normal file
1
src/data/furnishing/sets/ko.json
Normal file
1
src/data/furnishing/sets/pt.json
Normal file
1
src/data/furnishing/sets/ru.json
Normal file
1
src/data/furnishing/sets/zh.json
Normal 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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
266
src/routes/furnishing/inventory.svelte
Normal 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>
|
381
src/routes/furnishing/list.svelte
Normal 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>
|
BIN
static/images/furnishing/a_guide_in_the_summer_woods.png
Normal file
After ![]() (image error) Size: 8.1 KiB |
BIN
static/images/furnishing/a_messenger_in_the_summer_woods.png
Normal file
After ![]() (image error) Size: 7.8 KiB |
Before ![]() (image error) Size: 31 KiB After ![]() (image error) Size: 28 KiB ![]() ![]() |
Before ![]() (image error) Size: 27 KiB After ![]() (image error) Size: 26 KiB ![]() ![]() |
BIN
static/images/furnishing/countryside_tea_stand.png
Normal file
After ![]() (image error) Size: 40 KiB |
BIN
static/images/furnishing/embroidered_curtains.png
Normal file
After ![]() (image error) Size: 5.3 KiB |
BIN
static/images/furnishing/embroidered_lantern_lofty_grandeur.png
Normal file
After ![]() (image error) Size: 30 KiB |
BIN
static/images/furnishing/feather-light_praise.png
Normal file
After ![]() (image error) Size: 27 KiB |
BIN
static/images/furnishing/floral_screen_jade_and_gold.png
Normal file
After ![]() (image error) Size: 34 KiB |
After ![]() (image error) Size: 5.8 KiB |
BIN
static/images/furnishing/half-constructed_fence.png
Normal file
After ![]() (image error) Size: 7.3 KiB |
BIN
static/images/furnishing/lantern-lit_stage_crescendo.png
Normal file
After ![]() (image error) Size: 24 KiB |
BIN
static/images/furnishing/multi-seat_round_pine_table.png
Normal file
After ![]() (image error) Size: 18 KiB |
BIN
static/images/furnishing/newly_grown_grapevines.png
Normal file
After ![]() (image error) Size: 37 KiB |
BIN
static/images/furnishing/pure_gorgeous_summer.png
Normal file
After ![]() (image error) Size: 32 KiB |
BIN
static/images/furnishing/rainproof_fir_cargo_container.png
Normal file
After ![]() (image error) Size: 22 KiB |
BIN
static/images/furnishing/sets/adventurer_camp.png
Normal file
After ![]() (image error) Size: 281 KiB |
BIN
static/images/furnishing/sets/amidst_poetic_pondering.png
Normal file
After ![]() (image error) Size: 360 KiB |
BIN
static/images/furnishing/sets/amidst_whispering_winds.png
Normal file
After ![]() (image error) Size: 343 KiB |
BIN
static/images/furnishing/sets/arms_factory.png
Normal file
After ![]() (image error) Size: 342 KiB |
BIN
static/images/furnishing/sets/bird_and_blossom_park.png
Normal file
After ![]() (image error) Size: 424 KiB |
BIN
static/images/furnishing/sets/book_lovers_study.png
Normal file
After ![]() (image error) Size: 319 KiB |
BIN
static/images/furnishing/sets/busy_kitchen.png
Normal file
After ![]() (image error) Size: 323 KiB |
BIN
static/images/furnishing/sets/cargo_transfer_station.png
Normal file
After ![]() (image error) Size: 347 KiB |
BIN
static/images/furnishing/sets/climate_crossover.png
Normal file
After ![]() (image error) Size: 355 KiB |
BIN
static/images/furnishing/sets/cloudy_haze_dream-court.png
Normal file
After ![]() (image error) Size: 389 KiB |
BIN
static/images/furnishing/sets/corner_of_the_teahouse.png
Normal file
After ![]() (image error) Size: 377 KiB |
BIN
static/images/furnishing/sets/cottage_kitchen.png
Normal file
After ![]() (image error) Size: 357 KiB |
BIN
static/images/furnishing/sets/dawn_orchard.png
Normal file
After ![]() (image error) Size: 423 KiB |
After ![]() (image error) Size: 359 KiB |
BIN
static/images/furnishing/sets/ever-popular_eatery.png
Normal file
After ![]() (image error) Size: 329 KiB |
BIN
static/images/furnishing/sets/feiyun_study_room.png
Normal file
After ![]() (image error) Size: 371 KiB |
BIN
static/images/furnishing/sets/festival_market.png
Normal file
After ![]() (image error) Size: 358 KiB |
BIN
static/images/furnishing/sets/frontier_life.png
Normal file
After ![]() (image error) Size: 332 KiB |
BIN
static/images/furnishing/sets/fruit_&_veg_market.png
Normal file
After ![]() (image error) Size: 399 KiB |
After ![]() (image error) Size: 284 KiB |
BIN
static/images/furnishing/sets/gathering_of_gourmets.png
Normal file
After ![]() (image error) Size: 405 KiB |
BIN
static/images/furnishing/sets/glittering_street.png
Normal file
After ![]() (image error) Size: 419 KiB |
BIN
static/images/furnishing/sets/hamlet_house.png
Normal file
After ![]() (image error) Size: 627 KiB |
BIN
static/images/furnishing/sets/harvest_season_stall.png
Normal file
After ![]() (image error) Size: 416 KiB |
BIN
static/images/furnishing/sets/hermits_hut.png
Normal file
After ![]() (image error) Size: 374 KiB |
BIN
static/images/furnishing/sets/idyllic_town.png
Normal file
After ![]() (image error) Size: 395 KiB |
BIN
static/images/furnishing/sets/iter_ad_astra_abyssosque.png
Normal file
After ![]() (image error) Size: 428 KiB |
BIN
static/images/furnishing/sets/lone_and_cautious_adventurer.png
Normal file
After ![]() (image error) Size: 304 KiB |
After ![]() (image error) Size: 641 KiB |
BIN
static/images/furnishing/sets/merchants_working_lunch.png
Normal file
After ![]() (image error) Size: 368 KiB |
BIN
static/images/furnishing/sets/of_hunting_and_dancing.png
Normal file
After ![]() (image error) Size: 415 KiB |
BIN
static/images/furnishing/sets/parlor_cordiality.png
Normal file
After ![]() (image error) Size: 379 KiB |
BIN
static/images/furnishing/sets/plain_liyue_bedroom.png
Normal file
After ![]() (image error) Size: 328 KiB |
BIN
static/images/furnishing/sets/qingce_cloud_residence.png
Normal file
After ![]() (image error) Size: 392 KiB |
BIN
static/images/furnishing/sets/rocks_&_plants.png
Normal file
After ![]() (image error) Size: 295 KiB |
BIN
static/images/furnishing/sets/rural_water_source.png
Normal file
After ![]() (image error) Size: 375 KiB |
BIN
static/images/furnishing/sets/secret_research_lab.png
Normal file
After ![]() (image error) Size: 350 KiB |
BIN
static/images/furnishing/sets/suburbia_lighting.png
Normal file
After ![]() (image error) Size: 364 KiB |
BIN
static/images/furnishing/sets/table_&_chairs.png
Normal file
After ![]() (image error) Size: 308 KiB |
BIN
static/images/furnishing/sets/tea_talk.png
Normal file
After ![]() (image error) Size: 383 KiB |
After ![]() (image error) Size: 343 KiB |
BIN
static/images/furnishing/sets/training_ground.png
Normal file
After ![]() (image error) Size: 389 KiB |
BIN
static/images/furnishing/sets/weapon_forging_station.png
Normal file
After ![]() (image error) Size: 398 KiB |
BIN
static/images/furnishing/sets/well-equipped_study.png
Normal file
After ![]() (image error) Size: 307 KiB |
BIN
static/images/furnishing/sets/wine_o_clock.png
Normal file
After ![]() (image error) Size: 330 KiB |
After ![]() (image error) Size: 19 KiB |
BIN
static/images/furnishing/simple_wooden_fence.png
Normal file
After ![]() (image error) Size: 18 KiB |
BIN
static/images/furnishing/soil-carrying_wooden_barrel.png
Normal file
After ![]() (image error) Size: 6.8 KiB |
BIN
static/images/furnishing/stone_cup-shaped_pool.png
Normal file
After ![]() (image error) Size: 22 KiB |
BIN
static/images/furnishing/sturdy_wooden_barrel.png
Normal file
After ![]() (image error) Size: 16 KiB |
BIN
static/images/furnishing/summer_forest_banquet.png
Normal file
After ![]() (image error) Size: 34 KiB |
BIN
static/images/furnishing/tall_wooden_pole.png
Normal file
After ![]() (image error) Size: 5.1 KiB |
BIN
static/images/furnishing/tasseled_lantern_deck_the_streets.png
Normal file
After ![]() (image error) Size: 5.6 KiB |
BIN
static/images/furnishing/temporary_work_quarters.png
Normal file
After ![]() (image error) Size: 40 KiB |
BIN
static/images/furnishing/the_adventurers_treasures.png
Normal file
After ![]() (image error) Size: 30 KiB |
BIN
static/images/furnishing/the_blue_oceans_treasure.png
Normal file
After ![]() (image error) Size: 28 KiB |
BIN
static/images/furnishing/thundering_heavens_drum.png
Normal file
After ![]() (image error) Size: 32 KiB |
BIN
static/images/furnishing/witchs_chorus.png
Normal file
After ![]() (image error) Size: 23 KiB |