feat(#22): Add sprites
@@ -37,4 +37,4 @@ User authentication is handled by [Supabase Auth](https://supabase.com/auth).
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
The living dex tracker's sprite collection is taken from [pokesprite](https://github.com/msikma/pokesprite), which is licensed under [the MIT license](https://github.com/msikma/pokesprite/blob/master/LICENSE).
|
The living dex tracker's sprite collection is taken from [PokéAPI Sprites](https://github.com/PokeAPI/sprites), which is licensed under [the Creative Commons CC0 1.0 Universal license](https://github.com/PokeAPI/sprites/blob/master/LICENCE.txt).
|
||||||
|
|||||||
@@ -1,35 +1,97 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import pokeApiPokemon from '$lib/helpers/pokeapi-pokemon.json';
|
||||||
|
|
||||||
|
export let pokemonName: string;
|
||||||
export let pokedexNumber: string | number;
|
export let pokedexNumber: string | number;
|
||||||
export let form: string | undefined;
|
export let form: string | undefined;
|
||||||
|
export let shiny: boolean | undefined = false;
|
||||||
|
|
||||||
let imagePath = null as string | null;
|
let imagePath = null as string | null;
|
||||||
|
let blah = '';
|
||||||
|
|
||||||
async function fetchImagePath() {
|
function setImagePath() {
|
||||||
try {
|
let rootFolder =
|
||||||
let url = `/api/sprite?pokedexNumber=${pokedexNumber}`;
|
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home';
|
||||||
if (form) {
|
// let rootFolder = 'sprites/home';
|
||||||
url += `&form=${form}`;
|
|
||||||
|
if (form === 'Female') {
|
||||||
|
rootFolder += '/female';
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url);
|
if (shiny) {
|
||||||
|
rootFolder += '/shiny';
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to fetch image path');
|
|
||||||
}
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
imagePath = `./sprites/regular/${data.spriteFileName}`;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(fetchImagePath);
|
// Remove leading zeros
|
||||||
|
const strippedPokedexNumber = pokedexNumber.toString().replace(/^0+/, '');
|
||||||
|
|
||||||
|
// Sanitise the pokemon name by making it all lowercase and replacing any spaces with hyphens and removing other characters
|
||||||
|
let sanitisedPokemonName = pokemonName.toLowerCase().replace(/[^a-z]/g, '');
|
||||||
|
|
||||||
|
// Get the PokeApi id for the pokemon
|
||||||
|
let pokeApiId;
|
||||||
|
if (form && form.length > 0 && form !== 'Female') {
|
||||||
|
// Sanitise the form by making it all lowercase and replacing spaces with hyphens
|
||||||
|
let sanitisedForm = form
|
||||||
|
.toLowerCase()
|
||||||
|
.replaceAll(' ', '-')
|
||||||
|
.replaceAll('2', 'two')
|
||||||
|
.replaceAll('3', 'three')
|
||||||
|
.replaceAll('4', 'four');
|
||||||
|
|
||||||
|
switch (sanitisedForm) {
|
||||||
|
case 'alolan':
|
||||||
|
sanitisedForm = 'alola';
|
||||||
|
break;
|
||||||
|
case 'galarian':
|
||||||
|
sanitisedForm = 'galar';
|
||||||
|
break;
|
||||||
|
case 'hisuian':
|
||||||
|
sanitisedForm = 'hisui';
|
||||||
|
break;
|
||||||
|
// case 'rainbow-ribbon':
|
||||||
|
// sanitisedForm = 'rainbow-swirl-ribbon-sweet';
|
||||||
|
// break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strippedPokedexNumber == '869') {
|
||||||
|
console.log(sanitisedPokemonName, sanitisedForm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the form is contained in the identifier, use that
|
||||||
|
if (
|
||||||
|
pokeApiPokemon.find(
|
||||||
|
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
pokeApiId = pokeApiPokemon.find(
|
||||||
|
(pokemon) => pokemon.identifier === sanitisedPokemonName + '-' + sanitisedForm
|
||||||
|
)?.id;
|
||||||
|
} else {
|
||||||
|
// If the form is not contained in the identifier, use the species_id
|
||||||
|
pokeApiId = pokeApiPokemon.find(
|
||||||
|
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
|
||||||
|
)?.id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pokeApiId = pokeApiPokemon.find(
|
||||||
|
(pokemon) => pokemon.species_id.toString() === strippedPokedexNumber
|
||||||
|
)?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
imagePath = `${rootFolder}/${pokeApiId}.png`;
|
||||||
|
blah = `${pokeApiId}.png`;
|
||||||
|
// blah = `${strippedPokedexNumber}${form?.length && form !== 'Female' ? '-' + form : ''}.png`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(setImagePath);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if imagePath}
|
{#if imagePath}
|
||||||
|
<!-- {blah} -->
|
||||||
<img src={imagePath} alt="sprite" />
|
<img src={imagePath} alt="sprite" />
|
||||||
{:else}
|
{:else}
|
||||||
<span class="loading loading-spinner loading-xs"></span>
|
<span class="loading loading-spinner loading-xs"></span>
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import { json } from '@sveltejs/kit';
|
|
||||||
import sprites from '$lib/helpers/sprites.json';
|
|
||||||
|
|
||||||
interface PokemonSpritesData {
|
|
||||||
[key: string]: {
|
|
||||||
forms: {
|
|
||||||
[key: string]: {
|
|
||||||
is_prev_gen_icon?: boolean;
|
|
||||||
is_alias_of?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function GET({ url }) {
|
|
||||||
const pokedexNumber = url.searchParams.get('pokedexNumber');
|
|
||||||
const form = url.searchParams.get('form') ?? undefined;
|
|
||||||
|
|
||||||
if (!pokedexNumber) {
|
|
||||||
return json({
|
|
||||||
status: 400,
|
|
||||||
error: 'Pokédex number is required'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const spriteFileName = getPokemonSpriteFileName(pokedexNumber, form);
|
|
||||||
|
|
||||||
if (spriteFileName) {
|
|
||||||
// If spriteUrl is found, return it with status 200
|
|
||||||
return json({
|
|
||||||
status: 200,
|
|
||||||
spriteFileName: spriteFileName
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// If spriteUrl is not found, return 404
|
|
||||||
return json({
|
|
||||||
status: 404,
|
|
||||||
error: `Sprite URL not found for the specified Pokémon with Pokédex number ${pokedexNumber} and form ${form}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPokemonSpriteFileName(number: string, form?: string): string | undefined {
|
|
||||||
const spritesData: PokemonSpritesData = sprites;
|
|
||||||
|
|
||||||
// Check if the Pokémon number exists in the sprites JSON
|
|
||||||
if (spritesData[number] && spritesData[number]['gen-8']) {
|
|
||||||
const forms = spritesData[number]['gen-8'].forms;
|
|
||||||
let formName = '$';
|
|
||||||
|
|
||||||
// If a form is provided and it exists for the Pokémon, set the form name
|
|
||||||
if (form && forms[form]) {
|
|
||||||
formName = form;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the form has an alias
|
|
||||||
if (forms[formName].is_alias_of) {
|
|
||||||
formName = forms[formName].is_alias_of;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = `${spritesData[number].slug.eng.toLowerCase()}${formName !== '$' ? '-' + formName : ''}.png`;
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the Pokémon number doesn't exist or doesn't have gen-8 sprites, return undefined
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
import Pagination from '$lib/components/Pagination.svelte';
|
import Pagination from '$lib/components/Pagination.svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
import type { PokedexEntry } from '$lib/models/PokedexEntry';
|
||||||
|
import PokemonSprite from '$lib/components/PokemonSprite.svelte';
|
||||||
|
|
||||||
let combinedData = null as CombinedData[] | null;
|
let combinedData = null as CombinedData[] | null;
|
||||||
let failedToLoad = false;
|
let failedToLoad = false;
|
||||||
@@ -55,6 +56,8 @@
|
|||||||
<title>Living Dex Tracker - My Boxes</title>
|
<title>Living Dex Tracker - My Boxes</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
<!-- <PokemonSprite pokemonName={'Raichu'} pokedexNumber={26} form={'Alolan'} /> -->
|
||||||
|
|
||||||
<div class="container mx-auto">
|
<div class="container mx-auto">
|
||||||
{#if !localUser}
|
{#if !localUser}
|
||||||
<p>Please <a href="/signin" class="underline text-primary hover:text-secondary">sign in</a></p>
|
<p>Please <a href="/signin" class="underline text-primary hover:text-secondary">sign in</a></p>
|
||||||
@@ -65,7 +68,7 @@
|
|||||||
|
|
||||||
<div class="flex flex-wrap -mx-2">
|
<div class="flex flex-wrap -mx-2">
|
||||||
{#each boxNumbers as boxNumber}
|
{#each boxNumbers as boxNumber}
|
||||||
<div class="mb-8 w-1/2 px-2">
|
<div class="mb-8 md:w-1/2 px-2">
|
||||||
<h2 class="text-xl font-bold mb-4">Box {boxNumber}</h2>
|
<h2 class="text-xl font-bold mb-4">Box {boxNumber}</h2>
|
||||||
<div class="grid grid-cols-6">
|
<div class="grid grid-cols-6">
|
||||||
{#each combinedData as { pokedexEntry, catchRecord }}
|
{#each combinedData as { pokedexEntry, catchRecord }}
|
||||||
@@ -75,11 +78,17 @@
|
|||||||
style="grid-column-start: {pokedexEntry[currentPlacement]
|
style="grid-column-start: {pokedexEntry[currentPlacement]
|
||||||
.column}; grid-row-start: {pokedexEntry[currentPlacement].row}"
|
.column}; grid-row-start: {pokedexEntry[currentPlacement].row}"
|
||||||
>
|
>
|
||||||
|
<PokemonSprite
|
||||||
|
pokemonName={pokedexEntry.pokemon}
|
||||||
|
pokedexNumber={pokedexEntry.pokedexNumber}
|
||||||
|
form={pokedexEntry.form}
|
||||||
|
/>
|
||||||
|
<!-- {pokedexEntry.form ? `(${pokedexEntry.form})` : ''} -->
|
||||||
<!-- <div class="font-bold">
|
<!-- <div class="font-bold">
|
||||||
{pokedexEntry.pokemon}
|
{pokedexEntry.pokemon}
|
||||||
{pokedexEntry.form ? `(${pokedexEntry.form})` : ''}
|
{pokedexEntry.form ? `(${pokedexEntry.form})` : ''}
|
||||||
</div> -->
|
</div> -->
|
||||||
<div>{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</div>
|
<!-- <div>{pokedexEntry.pokedexNumber.toString().padStart(3, '0')}</div> -->
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
Caught: {catchRecord.caught ? 'Yes' : 'No'} <br />
|
Caught: {catchRecord.caught ? 'Yes' : 'No'} <br />
|
||||||
Needs to Evolve: {catchRecord.haveToEvolve ? 'Yes' : 'No'} <br />
|
Needs to Evolve: {catchRecord.haveToEvolve ? 'Yes' : 'No'} <br />
|
||||||
|
|||||||
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 89 KiB |