Add character calculator
163
src/components/CharacterSelect.svelte
Normal file
|
@ -0,0 +1,163 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import VirtualList from './VirtualList.svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { mdiChevronDown } from '@mdi/js';
|
||||
|
||||
import Icon from './Icon.svelte';
|
||||
import { characters as characterList } from '../data/charactersAscension';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let placeholder = 'Select character...';
|
||||
|
||||
export let selected = null;
|
||||
export let hoveredIndex = -1;
|
||||
|
||||
let label = '';
|
||||
let focused = false;
|
||||
let container;
|
||||
let input;
|
||||
let search = '';
|
||||
|
||||
function toggleOptions() {
|
||||
focused = !focused;
|
||||
|
||||
if (focused) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function select(val) {
|
||||
selected = val;
|
||||
focused = false;
|
||||
hoveredIndex = -1;
|
||||
|
||||
dispatch('change');
|
||||
}
|
||||
|
||||
function selectIndex(val) {
|
||||
selected = filteredCharacter[val][1];
|
||||
focused = false;
|
||||
hoveredIndex = -1;
|
||||
|
||||
dispatch('change');
|
||||
}
|
||||
|
||||
function onWindowClick(event) {
|
||||
if (!container) return;
|
||||
const eventTarget = event.path && event.path.length > 0 ? event.path[0] : event.target;
|
||||
if (container.contains(eventTarget)) return;
|
||||
focused = false;
|
||||
hoveredIndex = -1;
|
||||
}
|
||||
|
||||
function onHover(index) {
|
||||
hoveredIndex = index;
|
||||
}
|
||||
|
||||
function onInput(event) {
|
||||
search = event.target.value;
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
search = '';
|
||||
filteredCharacter = characters;
|
||||
maxItemRow = 6;
|
||||
}
|
||||
|
||||
function onKeyDown(event) {
|
||||
if (!focused) return;
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
hoveredIndex = hoveredIndex === filteredCharacter.length - 1 ? 0 : hoveredIndex + 1;
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
hoveredIndex = hoveredIndex === 0 ? filteredCharacter.length - 1 : hoveredIndex - 1;
|
||||
break;
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
if (hoveredIndex >= 0 && hoveredIndex < filteredCharacter.length) selectIndex(hoveredIndex);
|
||||
break;
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
focused = false;
|
||||
hoveredIndex = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$: characters = Object.entries(characterList).sort((a, b) => a[1].name.localeCompare(b[1].name));
|
||||
|
||||
$: filteredCharacter = characters.filter((e) => e[1].name.toLowerCase().includes(search));
|
||||
$: maxItemRow = Math.min(6, filteredCharacter.length);
|
||||
|
||||
$: nothingSelected = selected === null;
|
||||
$: if (!nothingSelected) {
|
||||
label = selected.name;
|
||||
}
|
||||
$: focused, clearSearch();
|
||||
|
||||
$: classes = focused ? 'border-primary' : 'border-transparent';
|
||||
$: iconClasses = focused ? 'transform rotate-180' : '';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.hovered {
|
||||
@apply text-white !important;
|
||||
@apply bg-primary;
|
||||
}
|
||||
|
||||
.options {
|
||||
max-height: calc(50vh);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:window on:click={onWindowClick} on:keydown={onKeyDown} />
|
||||
|
||||
<div class="select-none relative" bind:this={container}>
|
||||
<div
|
||||
class={`flex w-full relative items-center px-4 bg-background rounded-2xl h-14 focus-within:outline-none
|
||||
focus-within:border-primary border-2 border-transparent ease-in duration-100 ${classes}`}
|
||||
on:click={toggleOptions}>
|
||||
{#if !nothingSelected}
|
||||
<img class="w-6 h-6 mr-2" src={`/images/characters/${selected.id}.png`} alt={selected.name} />
|
||||
{/if}
|
||||
<input
|
||||
bind:this={input}
|
||||
class={`bg-transparent focus:outline-none h-full ${nothingSelected ? 'text-gray-500' : 'text-white'}`}
|
||||
{placeholder}
|
||||
value={nothingSelected || focused ? search : label}
|
||||
on:input={onInput} />
|
||||
<Icon path={mdiChevronDown} color="white" className={`absolute right-0 mr-4 duration-100 ease-in ${iconClasses}`} />
|
||||
</div>
|
||||
{#if focused}
|
||||
<div
|
||||
transition:fade={{ duration: 100 }}
|
||||
class="options bg-item rounded-2xl absolute mt-2 pl-2 w-full min-h-full z-50 flex flex-col text-white shadow-xl border border-background">
|
||||
{#if filteredCharacter.length}
|
||||
<VirtualList
|
||||
itemHeight={48}
|
||||
height={`${maxItemRow * 48 + 16}px`}
|
||||
items={filteredCharacter}
|
||||
let:item={[id, character]}
|
||||
let:index>
|
||||
<span
|
||||
on:click={() => select(character)}
|
||||
on:mouseenter={() => onHover(index)}
|
||||
class={`p-3 rounded-xl cursor-pointer flex mr-2
|
||||
${index === 0 ? 'mt-2' : ''}
|
||||
${index === characters.length ? 'mb-2' : ''}
|
||||
${!nothingSelected && selected.id === id ? 'text-primary font-semibold' : ''}
|
||||
${hoveredIndex === index ? 'hovered' : ''}`}>
|
||||
<img class="w-6 h-6 mr-2" src={`/images/characters/${id}.png`} alt={character.name} />
|
||||
<span class="flex-1">{character.name}</span>
|
||||
</span>
|
||||
</VirtualList>
|
||||
{:else}<span class="p-3 rounded-xl cursor-pointer flex mr-2 my-2"> Character not found </span>{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
83
src/data/characterExp.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
export const characterExp = [
|
||||
0,
|
||||
1000,
|
||||
2325,
|
||||
4025,
|
||||
6175,
|
||||
8800,
|
||||
11950,
|
||||
15675,
|
||||
20025,
|
||||
25025,
|
||||
30725,
|
||||
37175,
|
||||
44400,
|
||||
52450,
|
||||
61375,
|
||||
71200,
|
||||
81950,
|
||||
93675,
|
||||
106400,
|
||||
120175,
|
||||
135050,
|
||||
151850,
|
||||
169850,
|
||||
189100,
|
||||
209650,
|
||||
231525,
|
||||
254775,
|
||||
279425,
|
||||
305525,
|
||||
333100,
|
||||
362200,
|
||||
392850,
|
||||
425100,
|
||||
458975,
|
||||
494525,
|
||||
531775,
|
||||
570750,
|
||||
611500,
|
||||
654075,
|
||||
698500,
|
||||
744800,
|
||||
795425,
|
||||
848125,
|
||||
902900,
|
||||
959800,
|
||||
1018875,
|
||||
1080150,
|
||||
1143675,
|
||||
1209475,
|
||||
1277600,
|
||||
1348075,
|
||||
1424575,
|
||||
1503625,
|
||||
1585275,
|
||||
1669550,
|
||||
1756500,
|
||||
1846150,
|
||||
1938550,
|
||||
2033725,
|
||||
2131725,
|
||||
2232600,
|
||||
2341550,
|
||||
2453600,
|
||||
2568775,
|
||||
2687100,
|
||||
2808625,
|
||||
2933400,
|
||||
3061475,
|
||||
3192875,
|
||||
3327650,
|
||||
3465825,
|
||||
3614525,
|
||||
3766900,
|
||||
3922975,
|
||||
4082800,
|
||||
4246400,
|
||||
4413825,
|
||||
4585125,
|
||||
4760350,
|
||||
4939525,
|
||||
5122700,
|
||||
];
|
1434
src/data/charactersAscension.js
Normal file
|
@ -1,32 +1,19 @@
|
|||
export const itemList = {
|
||||
unknown: {
|
||||
id: 'unknown',
|
||||
name: 'unknown',
|
||||
},
|
||||
unknown: { id: 'unknown', name: 'unknown' },
|
||||
none: { id: 'none', name: 'none' },
|
||||
|
||||
fetters_of_the_dandelion_gladiator: {
|
||||
id: 'fetters_of_the_dandelion_gladiator',
|
||||
name: 'Fetters of the Dandelion Gladiator',
|
||||
},
|
||||
chaos_device: {
|
||||
id: 'chaos_device',
|
||||
name: 'Chaos Device',
|
||||
},
|
||||
divining_scroll: {
|
||||
id: 'divining_scroll',
|
||||
name: 'Divining Scroll',
|
||||
},
|
||||
chaos_device: { id: 'chaos_device', name: 'Chaos Device' },
|
||||
divining_scroll: { id: 'divining_scroll', name: 'Divining Scroll' },
|
||||
chains_of_the_dandelion_gladiator: {
|
||||
id: 'chains_of_the_dandelion_gladiator',
|
||||
name: 'Chains of the Dandelion Gladiator',
|
||||
},
|
||||
chaos_circuit: {
|
||||
id: 'chaos_circuit',
|
||||
name: 'Chaos Circuit',
|
||||
},
|
||||
sealed_scroll: {
|
||||
id: 'sealed_scroll',
|
||||
name: 'Sealed Scroll',
|
||||
},
|
||||
chaos_circuit: { id: 'chaos_circuit', name: 'Chaos Circuit' },
|
||||
sealed_scroll: { id: 'sealed_scroll', name: 'Sealed Scroll' },
|
||||
shackles_of_the_dandelion_gladiator: {
|
||||
id: 'shackles_of_the_dandelion_gladiator',
|
||||
name: 'Shackles of the Dandelion Gladiator',
|
||||
|
@ -39,10 +26,7 @@ export const itemList = {
|
|||
id: 'dead_ley_line_branches',
|
||||
name: 'Dead Ley Line Branches',
|
||||
},
|
||||
slime_condensate: {
|
||||
id: 'slime_condensate',
|
||||
name: 'Slime Condensate',
|
||||
},
|
||||
slime_condensate: { id: 'slime_condensate', name: 'Slime Condensate' },
|
||||
boreal_wolfs_cracked_tooth: {
|
||||
id: 'boreal_wolfs_cracked_tooth',
|
||||
name: "Boreal Wolf's Cracked Tooth",
|
||||
|
@ -51,18 +35,12 @@ export const itemList = {
|
|||
id: 'dead_ley_line_leaves',
|
||||
name: 'Dead Ley Line Leaves',
|
||||
},
|
||||
slime_secretions: {
|
||||
id: 'slime_secretions',
|
||||
name: 'Slime Secretions',
|
||||
},
|
||||
slime_secretions: { id: 'slime_secretions', name: 'Slime Secretions' },
|
||||
boreal_wolfs_broken_fang: {
|
||||
id: 'boreal_wolfs_broken_fang',
|
||||
name: "Boreal Wolf's Broken Fang",
|
||||
},
|
||||
ley_line_sprouts: {
|
||||
id: 'ley_line_sprouts',
|
||||
name: 'Ley Line Sprouts',
|
||||
},
|
||||
ley_line_sprouts: { id: 'ley_line_sprouts', name: 'Ley Line Sprouts' },
|
||||
slime_concentrate: {
|
||||
id: 'slime_concentrate',
|
||||
name: 'Slime Concentrate',
|
||||
|
@ -75,26 +53,17 @@ export const itemList = {
|
|||
id: 'dead_ley_line_branch',
|
||||
name: 'Dead Ley Line Branch',
|
||||
},
|
||||
firm_arrowhead: {
|
||||
id: 'firm_arrowhead',
|
||||
name: 'Firm Arrowhead',
|
||||
},
|
||||
firm_arrowhead: { id: 'firm_arrowhead', name: 'Firm Arrowhead' },
|
||||
weathered_arrowhead: {
|
||||
id: 'weathered_arrowhead',
|
||||
name: 'Weathered Arrowhead',
|
||||
},
|
||||
chaos_core: {
|
||||
id: 'chaos_core',
|
||||
name: 'Chaos Core',
|
||||
},
|
||||
chaos_core: { id: 'chaos_core', name: 'Chaos Core' },
|
||||
dream_of_the_dandelion_gladiator: {
|
||||
id: 'dream_of_the_dandelion_gladiator',
|
||||
name: 'Dream of the Dandelion Gladiator',
|
||||
},
|
||||
sharp_arrowhead: {
|
||||
id: 'sharp_arrowhead',
|
||||
name: 'Sharp Arrowhead',
|
||||
},
|
||||
sharp_arrowhead: { id: 'sharp_arrowhead', name: 'Sharp Arrowhead' },
|
||||
luminous_sands_from_guyun: {
|
||||
id: 'luminous_sands_from_guyun',
|
||||
name: 'Luminous Sands from Guyun',
|
||||
|
@ -119,10 +88,7 @@ export const itemList = {
|
|||
id: 'sergeants_insignia',
|
||||
name: "Sergeant's Insignia",
|
||||
},
|
||||
relic_from_guyun: {
|
||||
id: 'relic_from_guyun',
|
||||
name: 'Relic from Guyun',
|
||||
},
|
||||
relic_from_guyun: { id: 'relic_from_guyun', name: 'Relic from Guyun' },
|
||||
inspectors_sacrificial_knife: {
|
||||
id: 'inspectors_sacrificial_knife',
|
||||
name: "Inspector's Sacrificial Knife",
|
||||
|
@ -139,10 +105,7 @@ export const itemList = {
|
|||
id: 'tile_of_decarabians_tower',
|
||||
name: "Tile of Decarabian's Tower",
|
||||
},
|
||||
heavy_horn: {
|
||||
id: 'heavy_horn',
|
||||
name: 'Heavy Horn',
|
||||
},
|
||||
heavy_horn: { id: 'heavy_horn', name: 'Heavy Horn' },
|
||||
debris_of_decarabians_city: {
|
||||
id: 'debris_of_decarabians_city',
|
||||
name: "Debris of Decarabian's City",
|
||||
|
@ -179,18 +142,12 @@ export const itemList = {
|
|||
id: 'mist_veiled_mercury_elixir',
|
||||
name: 'Mist Veiled Mercury Elixir',
|
||||
},
|
||||
mist_grass: {
|
||||
id: 'mist_grass',
|
||||
name: 'Mist Grass',
|
||||
},
|
||||
mist_grass: { id: 'mist_grass', name: 'Mist Grass' },
|
||||
mist_veiled_gold_elixir: {
|
||||
id: 'mist_veiled_gold_elixir',
|
||||
name: 'Mist Veiled Gold Elixir',
|
||||
},
|
||||
mist_grass_wick: {
|
||||
id: 'mist_grass_wick',
|
||||
name: 'Mist Grass Wick',
|
||||
},
|
||||
mist_grass_wick: { id: 'mist_grass_wick', name: 'Mist Grass Wick' },
|
||||
mist_veiled_primo_elixir: {
|
||||
id: 'mist_veiled_primo_elixir',
|
||||
name: 'Mist Veiled Primo Elixir',
|
||||
|
@ -203,10 +160,7 @@ export const itemList = {
|
|||
id: 'fragile_bone_shard',
|
||||
name: 'Fragile Bone Shard',
|
||||
},
|
||||
damaged_mask: {
|
||||
id: 'damaged_mask',
|
||||
name: 'Damaged Mask',
|
||||
},
|
||||
damaged_mask: { id: 'damaged_mask', name: 'Damaged Mask' },
|
||||
piece_of_aerosiderite: {
|
||||
id: 'piece_of_aerosiderite',
|
||||
name: 'Piece of Aerosiderite',
|
||||
|
@ -215,10 +169,7 @@ export const itemList = {
|
|||
id: 'sturdy_bone_shard',
|
||||
name: 'Sturdy Bone Shard',
|
||||
},
|
||||
stained_mask: {
|
||||
id: 'stained_mask',
|
||||
name: 'Stained Mask',
|
||||
},
|
||||
stained_mask: { id: 'stained_mask', name: 'Stained Mask' },
|
||||
bit_of_aerosiderite: {
|
||||
id: 'bit_of_aerosiderite',
|
||||
name: 'Bit of Aerosiderite',
|
||||
|
@ -227,10 +178,7 @@ export const itemList = {
|
|||
id: 'fossilized_bone_shard',
|
||||
name: 'Fossilized Bone Shard',
|
||||
},
|
||||
ominous_mask: {
|
||||
id: 'ominous_mask',
|
||||
name: 'Ominous Mask',
|
||||
},
|
||||
ominous_mask: { id: 'ominous_mask', name: 'Ominous Mask' },
|
||||
chunk_of_aerosiderite: {
|
||||
id: 'chunk_of_aerosiderite',
|
||||
name: 'Chunk of Aerosiderite',
|
||||
|
@ -255,18 +203,12 @@ export const itemList = {
|
|||
id: 'shimmering_nectar',
|
||||
name: 'Shimmering Nectar',
|
||||
},
|
||||
energy_nectar: {
|
||||
id: 'energy_nectar',
|
||||
name: 'Energy Nectar',
|
||||
},
|
||||
energy_nectar: { id: 'energy_nectar', name: 'Energy Nectar' },
|
||||
mist_flower_pollen: {
|
||||
id: 'mist_flower_pollen',
|
||||
name: 'Mist Flower Pollen',
|
||||
},
|
||||
seal_scroll: {
|
||||
id: 'seal_scroll',
|
||||
name: 'Seal Scroll',
|
||||
},
|
||||
seal_scroll: { id: 'seal_scroll', name: 'Seal Scroll' },
|
||||
black_copper_horn: {
|
||||
id: 'black_copper_horn',
|
||||
name: 'Black Copper Horn',
|
||||
|
@ -275,4 +217,139 @@ export const itemList = {
|
|||
id: 'historic_arrowhead',
|
||||
name: 'Historic Arrowhead',
|
||||
},
|
||||
agnidus_agate_sliver: {
|
||||
id: 'agnidus_agate_sliver',
|
||||
name: 'Agnidus Agate Sliver',
|
||||
},
|
||||
small_lamp_grass: { id: 'small_lamp_grass', name: 'Small Lamp Grass' },
|
||||
agnidus_agate_fragment: {
|
||||
id: 'agnidus_agate_fragment',
|
||||
name: 'Agnidus Agate Fragment',
|
||||
},
|
||||
everflame_seed: { id: 'everflame_seed', name: 'Everflame Seed' },
|
||||
agnidus_agate_chunk: {
|
||||
id: 'agnidus_agate_chunk',
|
||||
name: 'Agnidus Agate Chunk',
|
||||
},
|
||||
agnidus_agate_gemstone: {
|
||||
id: 'agnidus_agate_gemstone',
|
||||
name: 'Agnidus Agate Gemstone',
|
||||
},
|
||||
varunada_lazurite_sliver: {
|
||||
id: 'varunada_lazurite_sliver',
|
||||
name: 'Varunada Lazurite Sliver',
|
||||
},
|
||||
philanemo_mushroom: {
|
||||
id: 'philanemo_mushroom',
|
||||
name: 'Philanemo Mushroom',
|
||||
},
|
||||
varunada_lazurite_fragment: {
|
||||
id: 'varunada_lazurite_fragment',
|
||||
name: 'Varunada Lazurite Fragment',
|
||||
},
|
||||
cleansing_heart: { id: 'cleansing_heart', name: 'Cleansing Heart' },
|
||||
varunada_lazurite_chunk: {
|
||||
id: 'varunada_lazurite_chunk',
|
||||
name: 'Varunada Lazurite Chunk',
|
||||
},
|
||||
varunada_lazurite_gemstone: {
|
||||
id: 'varunada_lazurite_gemstone',
|
||||
name: 'Varunada Lazurite Gemstone',
|
||||
},
|
||||
vajrada_amethyst_sliver: {
|
||||
id: 'vajrada_amethyst_sliver',
|
||||
name: 'Vajrada Amethyst Sliver',
|
||||
},
|
||||
noctilucous_jade: { id: 'noctilucous_jade', name: 'Noctilucous Jade' },
|
||||
vajrada_amethyst_fragment: {
|
||||
id: 'vajrada_amethyst_fragment',
|
||||
name: 'Vajrada Amethyst Fragment',
|
||||
},
|
||||
lightning_prism: { id: 'lightning_prism', name: 'Lightning Prism' },
|
||||
vajrada_amethyst_chunk: {
|
||||
id: 'vajrada_amethyst_chunk',
|
||||
name: 'Vajrada Amethyst Chunk',
|
||||
},
|
||||
vajrada_amethyst_gemstone: {
|
||||
id: 'vajrada_amethyst_gemstone',
|
||||
name: 'Vajrada Amethyst Gemstone',
|
||||
},
|
||||
windwheel_aster: { id: 'windwheel_aster', name: 'Windwheel Aster' },
|
||||
shivada_jade_sliver: {
|
||||
id: 'shivada_jade_sliver',
|
||||
name: 'Shivada Jade Sliver',
|
||||
},
|
||||
cor_lapis: { id: 'cor_lapis', name: 'Cor Lapis' },
|
||||
shivada_jade_fragment: {
|
||||
id: 'shivada_jade_fragment',
|
||||
name: 'Shivada Jade Fragment',
|
||||
},
|
||||
hoarfrost_core: { id: 'hoarfrost_core', name: 'Hoarfrost Core' },
|
||||
shivada_jade_chunk: {
|
||||
id: 'shivada_jade_chunk',
|
||||
name: 'Shivada Jade Chunk',
|
||||
},
|
||||
shivada_jade_gemstone: {
|
||||
id: 'shivada_jade_gemstone',
|
||||
name: 'Shivada Jade Gemstone',
|
||||
},
|
||||
vayuda_turquoise_sliver: {
|
||||
id: 'vayuda_turquoise_sliver',
|
||||
name: 'Vayuda Turquoise Sliver',
|
||||
},
|
||||
dandelion_seed: { id: 'dandelion_seed', name: 'Dandelion Seed' },
|
||||
vayuda_turquoise_fragment: {
|
||||
id: 'vayuda_turquoise_fragment',
|
||||
name: 'Vayuda Turquoise Fragment',
|
||||
},
|
||||
hurricane_seed: { id: 'hurricane_seed', name: 'Hurricane Seed' },
|
||||
vayuda_turquoise_chunk: {
|
||||
id: 'vayuda_turquoise_chunk',
|
||||
name: 'Vayuda Turquoise Chunk',
|
||||
},
|
||||
vayuda_turquoise_gemstone: {
|
||||
id: 'vayuda_turquoise_gemstone',
|
||||
name: 'Vayuda Turquoise Gemstone',
|
||||
},
|
||||
calla_lily: { id: 'calla_lily', name: 'Calla Lily' },
|
||||
valberry: { id: 'valberry', name: 'Valberry' },
|
||||
prithiva_topaz_sliver: {
|
||||
id: 'prithiva_topaz_sliver',
|
||||
name: 'Prithiva Topaz Sliver',
|
||||
},
|
||||
glaze_lily: { id: 'glaze_lily', name: 'Glaze Lily' },
|
||||
prithiva_topaz_fragment: {
|
||||
id: 'prithiva_topaz_fragment',
|
||||
name: 'Prithiva Topaz Fragment',
|
||||
},
|
||||
basalt_pillar: { id: 'basalt_pillar', name: 'Basalt Pillar' },
|
||||
prithiva_topaz_chunk: {
|
||||
id: 'prithiva_topaz_chunk',
|
||||
name: 'Prithiva Topaz Chunk',
|
||||
},
|
||||
prithiva_topaz_gemstone: {
|
||||
id: 'prithiva_topaz_gemstone',
|
||||
name: 'Prithiva Topaz Gemstone',
|
||||
},
|
||||
violetgrass: { id: 'violetgrass', name: 'Violetgrass' },
|
||||
wolfhook: { id: 'wolfhook', name: 'Wolfhook' },
|
||||
brilliant_diamond_sliver: {
|
||||
id: 'brilliant_diamond_sliver',
|
||||
name: 'Brilliant Diamond Sliver',
|
||||
},
|
||||
brilliant_diamond_fragment: {
|
||||
id: 'brilliant_diamond_fragment',
|
||||
name: 'Brilliant Diamond Fragment',
|
||||
},
|
||||
brilliant_diamond_chunk: {
|
||||
id: 'brilliant_diamond_chunk',
|
||||
name: 'Brilliant Diamond Chunk',
|
||||
},
|
||||
brilliant_diamond_gemstone: {
|
||||
id: 'brilliant_diamond_gemstone',
|
||||
name: 'Brilliant Diamond Gemstone',
|
||||
},
|
||||
cecilia: { id: 'cecilia', name: 'Cecilia' },
|
||||
jueyun_chili: { id: 'jueyun_chili', name: 'Jueyun Chili' },
|
||||
silk_flower: { id: 'silk_flower', name: 'Silk Flower' },
|
||||
};
|
||||
|
|
395
src/routes/calculator/_character.svelte
Normal file
|
@ -0,0 +1,395 @@
|
|||
<script>
|
||||
import { fade } from 'svelte/transition';
|
||||
import { mdiClose, mdiInformationOutline } from '@mdi/js';
|
||||
|
||||
import Input from '../../components/Input.svelte';
|
||||
import AscensionSelector from '../../components/AscensionSelector.svelte';
|
||||
import CharacterSelect from '../../components/CharacterSelect.svelte';
|
||||
import Checkbox from '../../components/Checkbox.svelte';
|
||||
import Check from '../../components/Check.svelte';
|
||||
import Button from '../../components/Button.svelte';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
import { characterExp } from '../../data/characterExp';
|
||||
|
||||
let resources = [
|
||||
{ selected: true, disabled: false, image: '/images/items/heros_wit.png', label: "Hero's Wit", value: '20000' },
|
||||
{
|
||||
selected: true,
|
||||
disabled: false,
|
||||
image: '/images/items/adventurers_experience.png',
|
||||
label: "Adventurer's Experience",
|
||||
value: '5000',
|
||||
},
|
||||
{
|
||||
selected: true,
|
||||
disabled: false,
|
||||
image: '/images/items/wanderes_advice.png',
|
||||
label: "Wanderer's Advice",
|
||||
value: '1000',
|
||||
},
|
||||
];
|
||||
|
||||
let withAscension = true;
|
||||
|
||||
let selectedCharacter = null;
|
||||
|
||||
let currentLevel = '';
|
||||
let currentExp = '';
|
||||
let currentAscension = 0;
|
||||
|
||||
let intendedAscension = 0;
|
||||
let intendedLevel = '';
|
||||
|
||||
let minAscension = 0;
|
||||
let minIntendedAscension = 0;
|
||||
let ascensionResouce = {};
|
||||
let unknownList = {};
|
||||
let currentMax = null;
|
||||
let moraNeeded = 0;
|
||||
let changed = false;
|
||||
|
||||
let numberFormat = Intl.NumberFormat();
|
||||
|
||||
$: usedResource = resources.filter((e) => e.selected).sort((a, b) => b.value - a.value);
|
||||
$: currentAscension, updateIntendedAscension();
|
||||
$: currentLevel, updateMinAscension();
|
||||
$: intendedLevel, updateMinIntendedAscension();
|
||||
|
||||
$: canCalculate =
|
||||
(withAscension ? selectedCharacter !== null : true) &&
|
||||
intendedLevel >= currentLevel &&
|
||||
intendedAscension >= currentAscension &&
|
||||
currentLevel !== '' &&
|
||||
currentLevel > 0 &&
|
||||
currentLevel <= 80 &&
|
||||
intendedLevel !== '' &&
|
||||
intendedLevel > 0 &&
|
||||
intendedLevel <= 80;
|
||||
|
||||
function updateIntendedAscension() {
|
||||
intendedAscension = Math.max(currentAscension, intendedAscension);
|
||||
}
|
||||
|
||||
function updateMinAscension() {
|
||||
if (currentLevel > 80) {
|
||||
minAscension = 6;
|
||||
} else if (currentLevel > 70) {
|
||||
minAscension = 5;
|
||||
} else if (currentLevel > 60) {
|
||||
minAscension = 4;
|
||||
} else if (currentLevel > 50) {
|
||||
minAscension = 3;
|
||||
} else if (currentLevel > 40) {
|
||||
minAscension = 2;
|
||||
} else if (currentLevel > 20) {
|
||||
minAscension = 1;
|
||||
} else {
|
||||
minAscension = 0;
|
||||
}
|
||||
|
||||
currentAscension = Math.max(currentAscension, minAscension);
|
||||
}
|
||||
|
||||
function updateMinIntendedAscension() {
|
||||
if (intendedLevel > 80) {
|
||||
minIntendedAscension = 6;
|
||||
} else if (intendedLevel > 70) {
|
||||
minIntendedAscension = 5;
|
||||
} else if (intendedLevel > 60) {
|
||||
minIntendedAscension = 4;
|
||||
} else if (intendedLevel > 50) {
|
||||
minIntendedAscension = 3;
|
||||
} else if (intendedLevel > 40) {
|
||||
minIntendedAscension = 2;
|
||||
} else if (intendedLevel > 20) {
|
||||
minIntendedAscension = 1;
|
||||
} else {
|
||||
minIntendedAscension = 0;
|
||||
}
|
||||
|
||||
intendedAscension = Math.max(intendedAscension, minIntendedAscension);
|
||||
}
|
||||
|
||||
function onChange() {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
function getSuffix(val) {
|
||||
switch (val) {
|
||||
case 1:
|
||||
return 'st';
|
||||
case 2:
|
||||
return 'nd';
|
||||
case 3:
|
||||
return 'rd';
|
||||
case 4:
|
||||
return 'th';
|
||||
}
|
||||
}
|
||||
|
||||
function calculateAscension() {
|
||||
const current = Math.max(currentAscension, 0);
|
||||
const target = intendedAscension;
|
||||
|
||||
console.log(selectedCharacter.ascension);
|
||||
|
||||
const result = selectedCharacter.ascension.slice(current, target).reduce(
|
||||
(sum, character, index) => {
|
||||
if (character.mora === 0) {
|
||||
if (unknownList[index + current] === undefined) {
|
||||
unknownList[index + current] = ['Mora amount'];
|
||||
}
|
||||
}
|
||||
|
||||
const mora = sum.mora + character.mora;
|
||||
const items = sum.items;
|
||||
|
||||
for (const [i, item] of character.items.entries()) {
|
||||
if (item.item.id === 'unknown') {
|
||||
if (unknownList[index + current] === undefined) {
|
||||
unknownList[index + current] = [];
|
||||
}
|
||||
|
||||
unknownList[index + current].push(`${i + 1}${getSuffix(i + 1)} material`);
|
||||
}
|
||||
|
||||
if (items[item.item.id] === undefined) {
|
||||
items[item.item.id] = { ...item.item, amount: 0 };
|
||||
}
|
||||
items[item.item.id].amount += item.amount;
|
||||
}
|
||||
|
||||
return { items, mora };
|
||||
},
|
||||
{
|
||||
mora: 0,
|
||||
items: {},
|
||||
},
|
||||
);
|
||||
|
||||
moraNeeded = moraNeeded + result.mora;
|
||||
ascensionResouce = result.items;
|
||||
|
||||
console.log(ascensionResouce);
|
||||
}
|
||||
|
||||
function calculate() {
|
||||
unknownList = {};
|
||||
ascensionResouce = {};
|
||||
|
||||
const values = resources
|
||||
.filter((e) => e.selected)
|
||||
.map((e) => e.value)
|
||||
.sort((a, b) => b - a);
|
||||
const items = values.map(() => 0);
|
||||
|
||||
const target = characterExp[intendedLevel - 1] - (characterExp[currentLevel - 1] + currentExp);
|
||||
let current = target;
|
||||
let max = [];
|
||||
|
||||
moraNeeded = (Math.floor(target / 1000) * 1000) / 5;
|
||||
|
||||
items[0] = Math.ceil(current / values[0]);
|
||||
max.push({
|
||||
usage: [...items],
|
||||
over: current - items[0] * values[0],
|
||||
});
|
||||
items[0] = Math.ceil(current / values[0]);
|
||||
|
||||
items[0] -= 1;
|
||||
items[1] = Math.ceil((current - items[0] * values[0]) / values[1]);
|
||||
|
||||
max.push({
|
||||
usage: [...items],
|
||||
over: current - (items[0] * values[0] + items[1] * values[1]),
|
||||
});
|
||||
|
||||
function process(usage, start) {
|
||||
let i = start;
|
||||
if (i === values.length - 1) return;
|
||||
while (usage[i] > 0) {
|
||||
usage[i]--;
|
||||
|
||||
usage[i + 1] = 0;
|
||||
let currentTotal = usage.reduce((total, e, f) => {
|
||||
total += e * values[f];
|
||||
return total;
|
||||
}, 0);
|
||||
usage[i + 1] = Math.ceil((target - currentTotal) / values[i + 1]);
|
||||
|
||||
currentTotal = usage.reduce((total, e, f) => {
|
||||
total += e * values[f];
|
||||
return total;
|
||||
}, 0);
|
||||
max.push({
|
||||
usage: [...usage],
|
||||
over: target - currentTotal,
|
||||
});
|
||||
|
||||
if (usage[i] === 0) i++;
|
||||
if (i === values.length - 1) break;
|
||||
process([...usage], start + 1);
|
||||
}
|
||||
}
|
||||
|
||||
process([...items], 1);
|
||||
|
||||
currentMax = max.sort((a, b) => b.over - a.over)[0];
|
||||
|
||||
if (withAscension) {
|
||||
calculateAscension();
|
||||
}
|
||||
|
||||
changed = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="bg-item rounded-xl p-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<div class="grid gap-2">
|
||||
<Check on:change={onChange} bind:checked={withAscension}>Calculate Ascension Material?</Check>
|
||||
{#if withAscension}
|
||||
<CharacterSelect on:change={onChange} bind:selected={selectedCharacter} placeholder="Select character" />
|
||||
{/if}
|
||||
|
||||
<div class="grid gap-2">
|
||||
<p class="text-white text-center mt-3">Current Character Level, Exp, & Ascension</p>
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={1}
|
||||
max={80}
|
||||
bind:value={currentLevel}
|
||||
placeholder="Input current character level..." />
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={0}
|
||||
bind:value={currentExp}
|
||||
placeholder="Input current character exp..." />
|
||||
{#if withAscension}
|
||||
<AscensionSelector min={minAscension} bind:value={currentAscension} on:change={onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<p class="text-white text-center mt-3">Intended Character Level & Ascension</p>
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={currentLevel}
|
||||
max={80}
|
||||
bind:value={intendedLevel}
|
||||
placeholder="Input intended character level..." />
|
||||
{#if withAscension}
|
||||
<AscensionSelector
|
||||
min={Math.max(currentAscension, minIntendedAscension)}
|
||||
bind:value={intendedAscension}
|
||||
on:change={onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col pl-1">
|
||||
<p class="text-white text-center md:text-left mb-1">Resource to Use</p>
|
||||
{#each resources as res}
|
||||
<div class="mb-1">
|
||||
<Checkbox disabled={res.disabled} bind:checked={res.selected} on:change={onChange}>
|
||||
<span class="text-white">
|
||||
{#if res.image}
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={res.image} alt={res.label} />
|
||||
</span>
|
||||
{/if}
|
||||
{res.label}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="md:col-span-2 xl:col-span-1">
|
||||
<Button disabled={!canCalculate} className="block w-full md:w-auto" on:click={calculate}>Calculate</Button>
|
||||
{#if currentMax !== null && !changed}
|
||||
{#if Object.keys(unknownList).length > 0}
|
||||
<div class="border-2 border-red-400 rounded-xl mt-2 p-4 md:inline-block">
|
||||
<p class="font-bold flex items-center text-red-400">
|
||||
<Icon className="mr-1 mb-1" path={mdiInformationOutline} />
|
||||
There are some unknown information
|
||||
</p>
|
||||
{#each Object.entries(unknownList) as [title, values]}
|
||||
<p class="text-red-400">Ascension level {Number(title) + 1}</p>
|
||||
<ul>
|
||||
{#each values as val}
|
||||
<li class="pl-4 text-red-400">- {val}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div transition:fade={{ duration: 100 }} class="bg-background rounded-xl p-4 mt-2 block md:inline-block">
|
||||
<table>
|
||||
{#each usedResource as res, i}
|
||||
{#if currentMax.usage[i] > 0}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{currentMax.usage[i]}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
{#if res.image}
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={res.image} alt={res.label} />
|
||||
</span>
|
||||
{/if}
|
||||
{res.label}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
{#each Object.entries(ascensionResouce) as [id, item]}
|
||||
{#if item.amount > 0}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{item.amount}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={`/images/items/${id}.png`} alt={item.name} />
|
||||
</span>
|
||||
{item.name}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{numberFormat.format(moraNeeded)}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src="/images/mora.png" alt="Mora" />
|
||||
</span>
|
||||
Mora (approximate)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{#if currentMax.over < 0}
|
||||
<tr>
|
||||
<td />
|
||||
<td class="text-red-400 py-1">{currentMax.over * -1} EXP Wasted</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -2,16 +2,16 @@
|
|||
import { fade } from 'svelte/transition';
|
||||
import { mdiStar, mdiClose, mdiInformationOutline } from '@mdi/js';
|
||||
|
||||
import Select from '../components/Select.svelte';
|
||||
import Input from '../components/Input.svelte';
|
||||
import AscensionSelector from '../components/AscensionSelector.svelte';
|
||||
import WeaponSelect from '../components/WeaponSelect.svelte';
|
||||
import Checkbox from '../components/Checkbox.svelte';
|
||||
import Check from '../components/Check.svelte';
|
||||
import Button from '../components/Button.svelte';
|
||||
import Icon from '../components/Icon.svelte';
|
||||
import Select from '../../components/Select.svelte';
|
||||
import Input from '../../components/Input.svelte';
|
||||
import AscensionSelector from '../../components/AscensionSelector.svelte';
|
||||
import WeaponSelect from '../../components/WeaponSelect.svelte';
|
||||
import Checkbox from '../../components/Checkbox.svelte';
|
||||
import Check from '../../components/Check.svelte';
|
||||
import Button from '../../components/Button.svelte';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
import { weaponExp } from '../data/weaponExp';
|
||||
import { weaponExp } from '../../data/weaponExp';
|
||||
|
||||
let weaponsRarity = [
|
||||
{ label: '3 Star', value: 3 },
|
||||
|
@ -250,165 +250,158 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Calculator - Paimon.moe</title>
|
||||
</svelte:head>
|
||||
<div class="pt-20 lg:ml-64 lg:pt-8 p-8">
|
||||
<h1 class="font-display font-black text-5xl text-white mb-2">Calculator</h1>
|
||||
<div class="bg-item rounded-xl p-4">
|
||||
<!-- <h2 class="font-display font-bold text-2xl text-white mb-2">Weapon Level Calculator</h2> -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<div class="grid gap-2">
|
||||
<Check on:change={onChange} bind:checked={withAscension}>Calculate Ascension Material?</Check>
|
||||
{#if !withAscension}
|
||||
<Select
|
||||
on:change={onChange}
|
||||
bind:selected={rarity}
|
||||
icon={mdiStar}
|
||||
options={weaponsRarity}
|
||||
placeholder="Select weapon rarity" />
|
||||
{:else}
|
||||
<WeaponSelect on:change={onChange} bind:selected={selectedWeapon} placeholder="Select weapon" />
|
||||
{/if}
|
||||
<div class="bg-item rounded-xl p-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<div class="grid gap-2">
|
||||
<Check on:change={onChange} bind:checked={withAscension}>Calculate Ascension Material?</Check>
|
||||
{#if !withAscension}
|
||||
<Select
|
||||
on:change={onChange}
|
||||
bind:selected={rarity}
|
||||
icon={mdiStar}
|
||||
options={weaponsRarity}
|
||||
placeholder="Select weapon rarity" />
|
||||
{:else}
|
||||
<WeaponSelect on:change={onChange} bind:selected={selectedWeapon} placeholder="Select weapon" />
|
||||
{/if}
|
||||
|
||||
<div class="grid gap-2">
|
||||
<p class="text-white text-center mt-3">Current Weapon Level, Exp, & Ascension</p>
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={1}
|
||||
max={80}
|
||||
bind:value={currentLevel}
|
||||
placeholder="Input current weapon level..." />
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={0}
|
||||
bind:value={currentExp}
|
||||
placeholder="Input current weapon exp..." />
|
||||
{#if withAscension}
|
||||
<AscensionSelector min={minAscension} bind:value={currentAscension} on:change={onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<p class="text-white text-center mt-3">Intended Weapon Level & Ascension</p>
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={currentLevel}
|
||||
max={80}
|
||||
bind:value={intendedLevel}
|
||||
placeholder="Input intended weapon level..." />
|
||||
{#if withAscension}
|
||||
<AscensionSelector
|
||||
min={Math.max(currentAscension, minIntendedAscension)}
|
||||
bind:value={intendedAscension}
|
||||
on:change={onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<p class="text-white text-center mt-3">Current Weapon Level, Exp, & Ascension</p>
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={1}
|
||||
max={80}
|
||||
bind:value={currentLevel}
|
||||
placeholder="Input current weapon level..." />
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={0}
|
||||
bind:value={currentExp}
|
||||
placeholder="Input current weapon exp..." />
|
||||
{#if withAscension}
|
||||
<AscensionSelector min={minAscension} bind:value={currentAscension} on:change={onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<p class="text-white text-center mt-3">Intended Weapon Level & Ascension</p>
|
||||
<Input
|
||||
on:change={onChange}
|
||||
type="number"
|
||||
min={currentLevel}
|
||||
max={80}
|
||||
bind:value={intendedLevel}
|
||||
placeholder="Input intended weapon level..." />
|
||||
{#if withAscension}
|
||||
<AscensionSelector
|
||||
min={Math.max(currentAscension, minIntendedAscension)}
|
||||
bind:value={intendedAscension}
|
||||
on:change={onChange} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col pl-1">
|
||||
<p class="text-white text-center md:text-left mb-1">Resource to Use</p>
|
||||
{#each resources as res}
|
||||
<div class="mb-1">
|
||||
<Checkbox disabled={res.disabled} bind:checked={res.selected} on:change={onChange}>
|
||||
<span class="text-white">
|
||||
{#if res.image}
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={res.image} alt={res.label} />
|
||||
</span>
|
||||
{/if}
|
||||
{res.label}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="md:col-span-2 xl:col-span-1">
|
||||
<Button disabled={!canCalculate} className="block w-full md:w-auto" on:click={calculate}>Calculate</Button>
|
||||
{#if currentMax !== null && !changed}
|
||||
{#if Object.keys(unknownList).length > 0}
|
||||
<div class="border-2 border-red-400 rounded-xl mt-2 p-4 md:inline-block">
|
||||
<p class="font-bold flex items-center text-red-400">
|
||||
<Icon className="mr-1 mb-1" path={mdiInformationOutline} />
|
||||
There are some unknown information
|
||||
</p>
|
||||
{#each Object.entries(unknownList) as [title, values]}
|
||||
<p class="text-red-400">Ascension level {Number(title) + 1}</p>
|
||||
<ul>
|
||||
{#each values as val}
|
||||
<li class="pl-4 text-red-400">- {val}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div transition:fade={{ duration: 100 }} class="bg-background rounded-xl p-4 mt-2 block md:inline-block">
|
||||
<table>
|
||||
{#each usedResource as res, i}
|
||||
{#if currentMax.usage[i] > 0}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{currentMax.usage[i]}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
{#if res.image}
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={res.image} alt={res.label} />
|
||||
</span>
|
||||
{/if}
|
||||
{res.label}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
{#each Object.entries(ascensionResouce) as [id, item]}
|
||||
{#if item.amount > 0}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{item.amount}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={`/images/items/${id}.png`} alt={item.name} />
|
||||
</span>
|
||||
{item.name}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{numberFormat.format(moraNeeded)}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src="/images/mora.png" alt="Mora" />
|
||||
</span>
|
||||
Mora (approximate ±40)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{#if currentMax.over < 0}
|
||||
<tr>
|
||||
<td />
|
||||
<td class="text-red-400 py-1">{currentMax.over * -1} EXP Wasted</td>
|
||||
</tr>
|
||||
</div>
|
||||
<div class="flex flex-col pl-1">
|
||||
<p class="text-white text-center md:text-left mb-1">Resource to Use</p>
|
||||
{#each resources as res}
|
||||
<div class="mb-1">
|
||||
<Checkbox disabled={res.disabled} bind:checked={res.selected} on:change={onChange}>
|
||||
<span class="text-white">
|
||||
{#if res.image}
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={res.image} alt={res.label} />
|
||||
</span>
|
||||
{/if}
|
||||
</table>
|
||||
{res.label}
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="md:col-span-2 xl:col-span-1">
|
||||
<Button disabled={!canCalculate} className="block w-full md:w-auto" on:click={calculate}>Calculate</Button>
|
||||
{#if currentMax !== null && !changed}
|
||||
{#if Object.keys(unknownList).length > 0}
|
||||
<div class="border-2 border-red-400 rounded-xl mt-2 p-4 md:inline-block">
|
||||
<p class="font-bold flex items-center text-red-400">
|
||||
<Icon className="mr-1 mb-1" path={mdiInformationOutline} />
|
||||
There are some unknown information
|
||||
</p>
|
||||
{#each Object.entries(unknownList) as [title, values]}
|
||||
<p class="text-red-400">Ascension level {Number(title) + 1}</p>
|
||||
<ul>
|
||||
{#each values as val}
|
||||
<li class="pl-4 text-red-400">- {val}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div transition:fade={{ duration: 100 }} class="bg-background rounded-xl p-4 mt-2 block md:inline-block">
|
||||
<table>
|
||||
{#each usedResource as res, i}
|
||||
{#if currentMax.usage[i] > 0}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{currentMax.usage[i]}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
{#if res.image}
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={res.image} alt={res.label} />
|
||||
</span>
|
||||
{/if}
|
||||
{res.label}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
{#each Object.entries(ascensionResouce) as [id, item]}
|
||||
{#if item.amount > 0}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{item.amount}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src={`/images/items/${id}.png`} alt={item.name} />
|
||||
</span>
|
||||
{item.name}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
<tr>
|
||||
<td class="text-right border-b border-gray-700 py-1">
|
||||
<span class="text-white mr-2 whitespace-no-wrap">{numberFormat.format(moraNeeded)}
|
||||
<Icon size={0.5} path={mdiClose} /></span>
|
||||
</td>
|
||||
<td class="border-b border-gray-700 py-1">
|
||||
<span class="text-white">
|
||||
<span class="w-6 inline-block">
|
||||
<img class="h-6 inline-block mr-1" src="/images/mora.png" alt="Mora" />
|
||||
</span>
|
||||
Mora (approximate ±40)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{#if currentMax.over < 0}
|
||||
<tr>
|
||||
<td />
|
||||
<td class="text-red-400 py-1">{currentMax.over * -1} EXP Wasted</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
55
src/routes/calculator/index.svelte
Normal file
|
@ -0,0 +1,55 @@
|
|||
<script>
|
||||
import { mdiArrowDown, mdiArrowUp } from '@mdi/js';
|
||||
|
||||
import WeaponCalculator from './_weapon.svelte';
|
||||
import CharacterCalculator from './_character.svelte';
|
||||
import Button from '../../components/Button.svelte';
|
||||
import Icon from '../../components/Icon.svelte';
|
||||
|
||||
let weaponCalc;
|
||||
let characterCalc;
|
||||
|
||||
export function scroll(type) {
|
||||
const elementPosition =
|
||||
type === 'character' ? characterCalc.getBoundingClientRect().top : weaponCalc.getBoundingClientRect().top;
|
||||
const headerOffset = 80;
|
||||
const offsetPosition = elementPosition - headerOffset;
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Calculator - Paimon.moe</title>
|
||||
</svelte:head>
|
||||
<div class="pt-20 lg:ml-64 lg:pt-8 p-8">
|
||||
<div
|
||||
class="flex flex-col items-center md:flex-row-reverse md:justify-end md:items-start lg:items-center mb-2"
|
||||
bind:this={weaponCalc}>
|
||||
<Button on:click={() => scroll('character')}>
|
||||
<Icon size={0.8} path={mdiArrowDown} />
|
||||
Go To Character Calculator
|
||||
</Button>
|
||||
<h1
|
||||
class="font-display font-black text-center mt-2 md:mt-0 md:mr-2 xl:mr-8 text-3xl lg:text-left lg:text-5xl text-white">
|
||||
Weapon Calculator
|
||||
</h1>
|
||||
</div>
|
||||
<WeaponCalculator />
|
||||
<div
|
||||
class="flex flex-col items-center md:flex-row-reverse md:justify-end md:items-start lg:items-center mt-8 mb-2"
|
||||
bind:this={characterCalc}>
|
||||
<Button on:click={() => scroll('weapon')}>
|
||||
<Icon size={0.8} path={mdiArrowUp} />
|
||||
Go To Weapon Calculator
|
||||
</Button>
|
||||
<h1
|
||||
class="font-display font-black text-center mt-2 md:mt-0 md:mr-2 xl:mr-8 text-3xl lg:text-left lg:text-5xl text-white">
|
||||
Character Calculator
|
||||
</h1>
|
||||
</div>
|
||||
<CharacterCalculator />
|
||||
</div>
|
BIN
static/images/characters/traveler.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
static/images/items/adventurers_experience.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
static/images/items/agnidus_agate_chunk.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
static/images/items/agnidus_agate_fragment.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
static/images/items/agnidus_agate_gemstone.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
static/images/items/agnidus_agate_sliver.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
static/images/items/basalt_pillar.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
static/images/items/brilliant_diamond_chunk.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
static/images/items/brilliant_diamond_fragment.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
static/images/items/brilliant_diamond_gemstone.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
static/images/items/brilliant_diamond_sliver.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
static/images/items/calla_lily.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
static/images/items/cecilia.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
static/images/items/cleansing_heart.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
static/images/items/cor_lapis.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
static/images/items/dandelion_seed.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
static/images/items/everflame_seed.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
static/images/items/glaze_lily.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
static/images/items/heros_wit.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
static/images/items/hoarfrost_core.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
static/images/items/hurricane_seed.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
static/images/items/jueyun_chili.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
static/images/items/lightning_prism.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
static/images/items/noctilucous_jade.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
static/images/items/philanemo_mushroom.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
static/images/items/prithiva_topaz_chunk.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
static/images/items/prithiva_topaz_fragment.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
static/images/items/prithiva_topaz_gemstone.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
static/images/items/prithiva_topaz_sliver.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
static/images/items/shivada_jade_chunk.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
static/images/items/shivada_jade_fragment.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
static/images/items/shivada_jade_gemstone.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
static/images/items/shivada_jade_sliver.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
static/images/items/silk_flower.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
static/images/items/small_lamp_grass.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
static/images/items/vajrada_amethyst_chunk.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
static/images/items/vajrada_amethyst_fragment.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
static/images/items/vajrada_amethyst_gemstone.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
static/images/items/vajrada_amethyst_sliver.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
static/images/items/valberry.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
static/images/items/varunada_lazurite_chunk.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
static/images/items/varunada_lazurite_fragment.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
static/images/items/varunada_lazurite_gemstone.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
static/images/items/varunada_lazurite_sliver.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
static/images/items/vayuda_turquoise_chunk.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
static/images/items/vayuda_turquoise_fragment.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
static/images/items/vayuda_turquoise_gemstone.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
static/images/items/vayuda_turquoise_sliver.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
static/images/items/violetgrass.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
static/images/items/wanderes_advice.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
static/images/items/windwheel_aster.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
static/images/items/wolfhook.png
Normal file
After Width: | Height: | Size: 54 KiB |