Add fate calculator

pull/1/head
Made Baruna 2021-07-22 02:53:11 +07:00
parent 51418557ac
commit c586a95872
No known key found for this signature in database
GPG Key ID: 5AA5DA16AA5DCEAD
6 changed files with 349 additions and 0 deletions

View File

@ -372,6 +372,7 @@
"titleCharacter": "Character Calculator",
"titleResin": "Resin Calculator",
"titleFriendship": "Friendship Exp Calculator",
"titleFate": "Fate Price Calculator",
"goto": "Go To {where}",
"howToUse": "How to Use",
"guide": {
@ -462,6 +463,19 @@
"result": "To reach Friendship level 10 if you complete all daily commission and use 180 resin everyday",
"resultSerenitea": "If you also place the character in the Serenitea Pot (Max Adeptal Energy)",
"resultDay": "{result} Days"
},
"fate": {
"currency": "Select Currency...",
"firstTime": "First Time",
"amount": "Amount",
"genesis": "Genesis",
"first": "Bonus2x",
"total": "Total",
"price": "Price",
"totalGenesis": "Total Genesis",
"totalPrice": "Total Price",
"calculate": "Calculate how many Genesis Crystal I can get with {currency}{value}",
"calculateFate": "Calculate how much money I need for {value}"
}
},
"items": {

View File

@ -424,6 +424,19 @@
"result": "Untuk mencapai Friendship level 10 jika kamu menyelesaikan semua daily commission dan menggunakan 180 resin tiap hari",
"resultSerenitea": "Jika kamu juga menaruh karakternya di Serenitea Pot (Adeptal Energy Max)",
"resultDay": "{result} Hari"
},
"fate": {
"currency": "Pilih Mata Uang...",
"firstTime": "Pertama Kali",
"amount": "Jumlah",
"genesis": "Genesis",
"first": "Bonus2x",
"total": "Total",
"price": "Price",
"totalGenesis": "Total Genesis",
"totalPrice": "Total Harga",
"calculate": "Hitung berapa banyak Genesis Crystal yang bisa didapatkan dengan {currency}{value}",
"calculateFate": "Hitung berapa uang yang diperlukan untuk {value}"
}
},
"items": {

View File

@ -0,0 +1,306 @@
<script>
import { fade } from 'svelte/transition';
import { mdiCheck, mdiClose } from '@mdi/js';
import { t } from 'svelte-i18n';
import Button from '../../components/Button.svelte';
import Check from '../../components/Check.svelte';
import Icon from '../../components/Icon.svelte';
import Input from '../../components/Input.svelte';
import Select from '../../components/Select.svelte';
const numberFormat = Intl.NumberFormat('en', {
maximumFractionDigits: 2,
minimumFractionDigits: 0,
});
const values = [
{ amount: 60, bonus: 0, firstTime: true },
{ amount: 300, bonus: 30, firstTime: true },
{ amount: 980, bonus: 110, firstTime: true },
{ amount: 1980, bonus: 260, firstTime: true },
{ amount: 3280, bonus: 600, firstTime: true },
{ amount: 6480, bonus: 1600, firstTime: true },
];
const prices = {
USD: {
currency: '$',
values: [0.99, 4.99, 14.99, 29.99, 49.99, 99.99],
},
IDR: {
currency: 'Rp',
values: [16000, 79000, 249000, 479000, 799000, 1599000],
},
EUR: {
currency: '€',
values: [1.19, 5.49, 16.99, 32.99, 54.99, 109.99],
},
BRL: {
currency: 'R$',
values: [4.9, 27.9, 84.9, 169.9, 279.9, 549.9],
},
MYR: {
currency: 'RM',
values: [3.9, 19.9, 59.9, 129.9, 199.9, 399.9],
},
GBP: {
currency: '£',
values: [0.99, 4.99, 14.99, 29.99, 49.99, 99.99],
},
CNY: {
currency: '¥',
values: [6.0, 30.0, 98.0, 198.0, 328.0, 648.0],
},
Custom: {
currency: 'Custom',
values: [0.99, 4.99, 14.99, 29.99, 49.99, 99.99],
},
};
let usable = [];
let currencyLabel = '';
let usedPrices = [];
const currencies = [
{ label: 'USD ($)', value: 'USD' },
{ label: 'IDR (Rp)', value: 'IDR' },
{ label: 'EUR (€)', value: 'EUR' },
{ label: 'BRL (R$)', value: 'BRL' },
{ label: 'MYR (RM)', value: 'MYR' },
{ label: 'GBP (£)', value: 'GBP' },
{ label: 'CNY (¥)', value: 'CNY' },
{ label: 'Custom', value: 'Custom' },
];
let selected = null;
let money = 200;
let fate = 80;
let result = null;
let resultTotal = null;
let resultTotalPrice = null;
function onCurrencyChange() {
usedPrices = prices[selected.value].values.slice();
currencyLabel = prices[selected.value].currency;
onChange();
}
function onChange() {
result = null;
}
function calculateUsable() {
usable = usable
.slice()
.map((e) => {
const total = e.amount + (e.firstTime ? e.amount : e.bonus);
return { ...e, total, perItem: e.price / total };
})
.sort((a, b) => a.perItem - b.perItem);
}
async function calculate() {
onChange();
usable = values.slice().map((e, i) => ({ ...e, price: usedPrices[i] }));
calculateUsable();
let currentMoney = money;
const used = [];
let total = 0;
let totalPrice = 0;
while (usable.length > 0) {
const currentUsable = usable[0];
if (currentMoney - currentUsable.price >= 0) {
currentMoney -= currentUsable.price;
total += currentUsable.amount + (currentUsable.firstTime ? currentUsable.amount : currentUsable.bonus);
totalPrice += currentUsable.price;
const usedItem = used.find((e) => e.amount === currentUsable.amount && e.firstTime === currentUsable.firstTime);
if (usedItem) {
usedItem.qty++;
} else {
used.push({ ...currentUsable, qty: 1 });
}
if (currentUsable.firstTime) {
currentUsable.firstTime = false;
calculateUsable();
}
} else {
usable.shift();
}
}
result = used;
resultTotal = total;
resultTotalPrice = totalPrice;
}
async function calculateFate() {
onChange();
usable = values.slice().map((e, i) => ({ ...e, price: usedPrices[i] }));
calculateUsable();
const usableTemp = usable.slice();
let currentGenesis = fate * 160;
const used = [];
let total = 0;
let totalPrice = 0;
while (currentGenesis > 0 && usable.length > 0) {
const currentUsable = usable[0];
const totalGenesis =
currentUsable.amount + (currentUsable.firstTime ? currentUsable.amount : currentUsable.bonus);
if (currentGenesis - totalGenesis >= 0) {
currentGenesis -= totalGenesis;
total += totalGenesis;
totalPrice += currentUsable.price;
const usedItem = used.find((e) => e.amount === currentUsable.amount && e.firstTime === currentUsable.firstTime);
if (usedItem) {
usedItem.qty++;
} else {
used.push({ ...currentUsable, qty: 1 });
}
if (currentUsable.firstTime) {
currentUsable.firstTime = false;
usableTemp.find((e) => e.amount === currentUsable.amount).firstTime = false;
calculateUsable();
}
} else {
usable.shift();
}
}
let min = Number.MAX_SAFE_INTEGER;
let current = null;
for (const u of usableTemp) {
const usageAmount = u.amount + (u.firstTime ? u.amount : u.bonus);
if (Math.abs(currentGenesis - usageAmount) < min) {
current = u;
min = Math.abs(currentGenesis - usageAmount);
}
}
total += current.amount + (current.firstTime ? current.amount : current.bonus);
totalPrice += current.price;
const usedItem = used.find((e) => e.amount === current.amount && e.firstTime === current.firstTime);
if (usedItem) {
usedItem.qty++;
} else {
used.push({ ...current, qty: 1 });
}
result = used;
resultTotal = total;
resultTotalPrice = totalPrice;
}
</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>
<Select
options={currencies}
on:change={onCurrencyChange}
bind:selected
placeholder={$t('calculator.fate.currency')}
/>
{#if selected !== null}
<div class="mt-2">
{#each values as value, i}
<div class="mb-1 rounded-xl border-background border p-2">
<div class="flex flex-row items-center mb-1">
<img src="/images/genesis_crystal.png" alt="Genesis Crystal" class="w-6 mr-2" />
<p class="text-white">
{numberFormat.format(value.amount)} + {numberFormat.format(
value.firstTime ? value.amount : value.bonus,
)} = {numberFormat.format(value.amount + (value.firstTime ? value.amount : value.bonus))}
</p>
</div>
<div class="flex flex-row items-center">
<div class="w-32">
<Input className="text-center" bind:value={usedPrices[i]} type="number" />
</div>
<div class="flex flex-row items-center">
<Check inverted bind:checked={value.firstTime} on:change={onChange} />
<p class="text-white">{$t('calculator.fate.firstTime')}</p>
</div>
</div>
</div>
{/each}
</div>
{/if}
</div>
{#if selected !== null}
<div>
<Input placeholder="Total Money" bind:value={money} type="number" on:change={onChange} />
<div class="mb-1" />
<Button className="w-full" on:click={calculate}>
{$t('calculator.fate.calculate', { values: { currency: currencyLabel, value: numberFormat.format(money) } })}
</Button>
<p class="text-white my-6 text-center">OR</p>
<Input placeholder="Total Fate" bind:value={fate} type="number" on:change={onChange} />
<div class="mb-1" />
<Button className="w-full" on:click={calculateFate}>
{$t('calculator.fate.calculateFate', { values: { value: fate } })}
<img class="ml-1 w-6 inline" src="/images/intertwined_fate.png" alt="Fate" />
</Button>
</div>
{/if}
{#if result !== null}
<div
transition:fade={{ duration: 100 }}
class="rounded-xl bg-background p-4 block md:inline-block"
style="height: fit-content; width: fit-content;"
>
<table>
<tr>
<th class="text-white pr-2 text-right">{$t('calculator.fate.amount')}</th>
<th class="text-white pr-2 text-right">{$t('calculator.fate.genesis')}</th>
<th class="text-white pr-2 text-center">{$t('calculator.fate.first')}</th>
<th class="text-white pr-2 text-right">{$t('calculator.fate.total')}</th>
<th class="text-white text-right">{$t('calculator.fate.price')}</th>
</tr>
{#each result as res}
<tr>
<td class="border-t border-gray-700 text-white pr-2 text-right">
{res.qty}
<Icon size={0.5} path={mdiClose} />
</td>
<td class="border-t border-gray-700 text-white pr-2 text-right">{res.amount}</td>
<td class="border-t border-gray-700 text-white pr-2 text-center">
<Icon size={0.8} path={res.firstTime ? mdiCheck : mdiClose} />
</td>
<td class="border-t border-gray-700 text-white pr-2 text-right">
{numberFormat.format((res.amount + (res.firstTime ? res.amount : res.bonus)) * res.qty)}
</td>
<td class="border-t border-gray-700 text-white text-right">
{numberFormat.format(res.price * res.qty)}
</td>
</tr>
{/each}
<tr>
<td class="border-t border-gray-700 text-white text-right whitespace-no-wrap" colspan={5}>
{$t('calculator.fate.totalGenesis')}
{numberFormat.format(resultTotal)}
<img class="mr-1 w-6 inline" src="/images/genesis_crystal.png" alt="Genesis" />
({Math.floor(resultTotal / 160)}
<img class="w-6 inline" src="/images/intertwined_fate.png" alt="Fate" />)
</td>
</tr>
<tr>
<td class="border-t border-gray-700 text-white text-right whitespace-no-wrap" colspan={5}>
{$t('calculator.fate.totalPrice')}
{currencyLabel}{numberFormat.format(resultTotalPrice)}
</td>
</tr>
</table>
</div>
{/if}
</div>
</div>

View File

@ -9,6 +9,7 @@
import LevelUpTable from './_characterTable.svelte';
import ResinCalculator from './_resin.svelte';
import FriendshipCalculator from './_friendship.svelte';
import FatesCalculator from './_fates.svelte';
import ResinTable from './_resinTable.svelte';
import Button from '../../components/Button.svelte';
import Icon from '../../components/Icon.svelte';
@ -129,6 +130,21 @@
</h1>
</div>
<FriendshipCalculator />
<div
id="friendship"
class="flex flex-col items-center md:flex-row-reverse md:justify-end md:items-start lg:items-center mt-8 mb-2"
>
<Button on:click={() => findPos('character')}>
<Icon size={0.8} path={mdiArrowUp} />
{$t('calculator.goto', { values: { where: $t('calculator.titleCharacter') } })}
</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"
>
{$t('calculator.titleFate')}
</h1>
</div>
<FatesCalculator />
<div class="mt-8 space-y-4 grid md:grid-cols-2 md:gap-4 md:space-y-0">
<LevelUpTable />
<ResinTable />

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB