diff --git a/DESIGN_SYSTEM.md b/DESIGN_SYSTEM.md
index f23e2eb..97f0f45 100644
--- a/DESIGN_SYSTEM.md
+++ b/DESIGN_SYSTEM.md
@@ -69,7 +69,7 @@ Le Nebula Design System expose ses primitives publiques avec une classe de famil
Règles :
-- la classe décrit uniquement la famille : `nds-button`, `nds-icon-button`, `nds-input`, `nds-select`, `nds-select-trigger`, `nds-time`, `nds-textarea`, `nds-inline-input`, `nds-switch`, `nds-radio-button-group`, `nds-checkbox`, `nds-quantity-stepper`, `nds-list-row`, `nds-large-icon-picker-trigger`, `nds-event-surface`, `nds-scrollbox` ;
+- la classe décrit uniquement la famille : `nds-button`, `nds-icon-button`, `nds-input`, `nds-range`, `nds-select`, `nds-select-trigger`, `nds-time`, `nds-textarea`, `nds-inline-input`, `nds-switch`, `nds-radio-button-group`, `nds-checkbox`, `nds-quantity-stepper`, `nds-list-row`, `nds-large-icon-picker-trigger`, `nds-event-surface`, `nds-scrollbox` ;
- les patterns transverses utilisent aussi une classe de famille : `nds-panel`, `nds-card`, `nds-card-cover`, `nds-card-body`, `nds-card-actions`, `nds-tool-panel`, `nds-toolbar`, `nds-section-heading`, `nds-page-hero`, `nds-badge`, `nds-chip`, `nds-empty-state`, `nds-inline-notice`, `nds-progress`, `nds-filter-panel`, `nds-reorder-indicator`, `nds-modal`, `nds-modal-backdrop`, `nds-modal-footer`, `nds-drawer-panel`, `nds-drawer-resize-handle`, `nds-popover`, `nds-toast-stack`, `nds-toast` ;
- les variations passent par attributs : `data-size`, `data-variant`, `data-edge`, `data-border`, `data-axis` ;
- la page `/design-system` affiche aussi `data-state` comme axe de comparaison visuelle des états ;
@@ -108,7 +108,7 @@ Les surfaces NDS couvrent les patterns transverses, pas les composants metier co
- `nds-filter-panel` : panneau sticky ou lateral de filtres ; les options utilisent `nds-chip`.
- `nds-empty-state` : etat vide court, avec action optionnelle.
- `nds-inline-notice` : feedback compact inline, avec `data-variant="info|success|warning|danger"`.
-- `nds-progress` : rail de progression ; le remplissage est le premier `span` enfant ou `.nds-progress-bar`.
+- `nds-progress` : rail de progression (taille `md`/défaut : 8 px, `sm` : 4 px, via `data-size`) ; le remplissage est le premier `span` enfant ou `.nds-progress-bar`.
- `nds-modal`, `nds-drawer-panel`, `nds-popover`, `nds-toast` : surfaces d'overlay. Le comportement React reste gere par les composants existants.
Les classes metier existantes peuvent coexister avec ces classes pendant la migration. Quand une classe metier reste necessaire, elle doit porter le layout, la grille ou le positionnement, pas recreer les fonds, bordures, hovers et focus partages.
@@ -784,6 +784,52 @@ Date picker et menus compacts :
- les couleurs de fond des events restent métier et ne sont pas des variants NDS ;
- les fichiers d'outils peuvent conserver les dimensions de grille, le placement du menu et les états métier `disabled`.
+### Curseurs de niveau
+
+- `input[type="range"].nds-range` expose un curseur natif réutilisable, avec `min`, `max`, `step`, `value` et `disabled` standards ;
+- tailles : `md` (44 px, poignée 20 px) et `sm` (36 px, poignée 18 px) ;
+- associer un label explicite et une valeur visible ; conserver les interactions clavier natives ;
+- rail violet, poignée bordée de doré, hover et focus issus des tokens NDS ;
+- la page Aniimo affiche tous les curseurs avec `min="0"`, `max="4"`, `step="1"` : 0 retire la capacité des critères, 1–4 fixe son niveau minimum indépendant ;
+- les règles de filtrage restent dans le composant métier ; le catalogue `/design-system` montre un exemple interactif et l'état désactivé.
+
+### Cartes Aniimo et variantes
+
+- la carte d'entrée du guide utilise `games.json > images.aniimosCover` : le portrait d’Emberpup, recadré sur sa tête dans la couverture commune de 120 px ; des marges internes latérales de 15 % dézooment le portrait sans modifier le cadre ; une description courte conserve le gabarit des autres guides ;
+- MHW et Aniimo partagent les mixins `flip-card-inner`, `flip-card-indicator`, `flip-card-indicator-hover` et `flip-card-face-hover` de `_surfaces.scss` : rotation de 420 ms, surfaces et indicateurs identiques ;
+- les contrôles de retournement Aniimo sont dans chaque face et tournent avec elle ; le focus clavier rejoint le contrôle de la nouvelle face ;
+
+- afficher les portraits dans un cadre compact 3 / 4 plafonné à 250 px de hauteur, avec `object-fit: cover` et cadrage centré ; les fichiers source restent inchangés ;
+- les deux variantes de Petit Esprit de Feu utilisent `object-position: center 20%` pour descendre légèrement le personnage dans son cadre ;
+- la grille utilise un minimum de 220 px pour afficher cinq colonnes à 1920 px, puis réduit le nombre de colonnes sur les petits écrans ; limiter les cartes à 280 px de largeur ; le recto fixe leur hauteur avec le portrait et un pied de 96 px, indépendamment des noms et des variantes ;
+- le nom de forme revient à la ligne au recto, dans le pied fixe de 96 px ;
+- le recto affiche uniquement les icônes d'éléments, centrées dans le pied de carte, avec texte alternatif et infobulle ;
+- les onglets affichent des initiales ou une abréviation de trois lettres pour les formes à un seul mot (PLA, PRA, PRI) ; le nom complet reste accessible via `Tooltip`, au survol et au focus ;
+- placer les onglets au-dessus du contrôle de retournement, avec navigation clavier ; une grille de colonnes égales affiche les sept formes sur une ligne sans couper les libellés ;
+- la forme Prismana est la dernière entrée des variantes dans le JSON ; son onglet reçoit un léger fond et un soulignement arc-en-ciel statiques, sans masquer la sélection ou le focus ;
+- conserver la face affichée lorsqu'une autre variante est sélectionnée ;
+- le verso répartit les éléments et le rôle sur deux colonnes séparées par un trait vertical très discret, centré dans un espacement de 20 px (10 px de chaque côté), avant les compétences ;
+- les badges d'élément et de rôle restent sur une ligne dans leur colonne : icône fixe, padding compact et points de suspension sur les libellés trop longs, avec texte complet au survol ;
+- espacer de 24 px les sections Éléments/Rôle, Compétences de logis et Statistiques au verso ;
+- le verso ajoute les six statistiques et leur total sous les compétences : barres `nds-progress[data-size="sm"]`, valeur numérique alignée à droite, échelle commune égale au maximum du catalogue complet (130 actuellement), indépendante des filtres ;
+- le compteur met en évidence les Aniimos ou les variantes selon le mode de copie ; l’infobulle du bouton précise le contenu copié ;
+- le verso occupe exactement la même surface que le recto ; garder son en-tête fixe et faire défiler les détails dans une zone NDS accessible au clavier ; masquer la face inactive aux technologies d'assistance ;
+- réduire l'animation selon `prefers-reduced-motion` ;
+- les filtres se cumulent par famille et sont évalués sur chaque variante avant regroupement ;
+- les catégories Rôles, Éléments et Compétences de logis partagent un défilement unique via `filter-options`, comme MHW ; des séparateurs discrets délimitent les catégories et leurs en-têtes permettent de les replier sans désactiver leurs sélections ;
+- rôles et éléments sont sur deux colonnes de chips `sm` ; les compétences élémentaires précèdent les autres sans séparation interne, avec icône, texte, curseur et valeur sur une ligne ; un seuil à 0 reste visible mais ne participe pas aux opérations ET/OU ;
+- garder le nombre de critères sélectionnés visible dans l'en-tête, même replié.
+
+### Liaison des guides aux checklists
+
+Les guides à cartes partagent un panneau `guide-checklist-controls` dans la toolbox
+latérale du jeu, au-dessus de ses outils : panneau NDS, select avec label, bouton de dissociation et switch
+« Masquer les cochés ». Le label et le sélecteur occupent toute la largeur ; les actions sont centrées et reviennent à la ligne sur mobile. Le panneau ne concerne que le guide à cartes actuellement ouvert.
+L'état terminé désature et atténue uniquement les portraits (ou les icônes de
+catégorie Diablo), avec une coche accessible « Coché ». Les textes restent
+lisibles et les indicateurs absolus ne modifient pas la taille des cartes.
+Dans les cartes retournables, les indicateurs appartiennent à chaque face.
+
## 18. Scrollbars
Les scrollbars utilisent `scrollbar-control` depuis `website/src/styles/design-system/_controls.scss`.
@@ -836,3 +882,5 @@ Minimum attendu :
- alertes navigateur pour confirmations ou creation ;
- gros blocs explicatifs visibles dans l'app ;
- scroll horizontal sur les tableaux de donnees critiques.
+
+Les chips `nds-chip[data-size="sm"]` utilisent une hauteur minimale de 30 px, un padding de 4 × 8 px, une police `xs` et des icônes/images de 16 px. Le catalogue interactif expose cette taille à côté de la taille par défaut.
diff --git a/docs/FEATURE_CHECKLIST.md b/docs/FEATURE_CHECKLIST.md
index 0bbeb81..6ce9c40 100644
--- a/docs/FEATURE_CHECKLIST.md
+++ b/docs/FEATURE_CHECKLIST.md
@@ -52,6 +52,9 @@ Checklist à suivre lors de l'ajout ou de la modification d'un outil, d'une page
- Vérifier le bouton toolbox et le panneau latéral.
- Vérifier le breadcrumb.
- Vérifier les textes avec retours à la ligne.
+- Pour les guides à cartes, vérifier le sélecteur de checklist dans le panneau latéral de la toolbox liée, les listes partielles et la mise à jour immédiate depuis le panneau latéral. Les guides ne doivent jamais modifier la progression.
+- Vérifier le changement de checklist, la dissociation, la restauration de session, la suppression d'un module et le changement de toolbox. La réinitialisation des filtres conserve la liaison ; une liste devenue sans correspondance reste sélectionnée avec un message.
+- Pour Aniimo, vérifier la copie des espèces ou des formes selon l'affichage, les noms français/anglais, la priorité des formes et le masquage avant regroupement. Compteurs et copie doivent correspondre aux résultats affichés.
## Listes de jeu
diff --git a/docs/STORAGE_SCHEMA.md b/docs/STORAGE_SCHEMA.md
index 71dfc93..02cce4d 100644
--- a/docs/STORAGE_SCHEMA.md
+++ b/docs/STORAGE_SCHEMA.md
@@ -208,6 +208,38 @@ Stockage compact :
## Outil Checklist
+### Association temporaire aux guides
+
+Les pages Aniimo, monstres MHW, faune MHW et affixes Diablo peuvent refléter une
+checklist de la toolbox actuellement associée au jeu. Le sélecteur, la dissociation
+et le masquage se trouvent dans le panneau latéral de cette toolbox, au-dessus des outils. Cette liaison ne modifie
+ni les données de checklist ni les liens persistants jeu/toolbox.
+
+`sessionStorage["sokkog:guide-checklists"]` contient un objet indexé par
+`aniimo/aniimos`, `mhwilds/monsters`, `mhwilds/endemic` ou `diablo4/affixes`.
+Chaque entrée contient `{ toolboxId, moduleId, hideCompleted }`. Aucune liaison
+n'est choisie automatiquement. La navigation et le rechargement la conservent ;
+la dissociation, la suppression du module ou le changement de toolbox la retirent.
+Sans accès au stockage de session, l'état reste en mémoire dans l'application.
+Ces associations ne sont pas exportées et ne se synchronisent pas entre onglets.
+
+La reconnaissance compare les libellés complets sans tenir compte de la casse,
+des accents ni des espaces répétés. Une checklist partielle suffit, avec au moins
+une correspondance dans toutes les données du guide, indépendamment des filtres.
+Les lignes étrangères sont ignorées. Une correspondance est terminée lorsque
+toutes ses lignes atteignent leur propre `qtyTarget` ; les doublons ne sont pas
+additionnés. Renommer une ligne peut supprimer sa correspondance. Une liste encore
+sélectionnée mais sans correspondance reste liée, avec un message explicite.
+
+La copie Aniimo utilise `Nom:1` en affichage regroupé et `Nom — Forme:1` en
+affichage séparé. Les noms et formes français et anglais sont reconnus. Une ligne
+sans forme couvre toutes les variantes ; une ligne spécifique à une forme prend
+priorité sur la ligne générale. Le masquage des cochés précède le regroupement ;
+le style d'une carte groupée dépend de la variante active. La copie et les
+compteurs portent sur les résultats effectivement affichés.
+
+### Données persistantes
+
Type : `checklist`
Format simple, sans catégorie :
diff --git a/e2e/aniimo.spec.js b/e2e/aniimo.spec.js
new file mode 100644
index 0000000..8e093b9
--- /dev/null
+++ b/e2e/aniimo.spec.js
@@ -0,0 +1,344 @@
+// Rôle : vérifie les parcours Aniimo, les variantes, les cartes et les seuils au clavier.
+import { test, expect } from "@playwright/test";
+
+const route = "/#/games/aniimo/aniimos";
+const cards = (page) => page.locator(".aniimo-result");
+const section = (page, name) => page.getByRole("group", { name, exact: true }).filter({ has: page.locator("legend") });
+
+test("Aniimo matches MHW card motion, surfaces and filter controls", async ({ page }) => {
+ test.setTimeout(45000);
+ async function styles(locator, properties) {
+ return locator.evaluate((element, keys) => {
+ const css = globalThis.getComputedStyle(element);
+ return Object.fromEntries(keys.map((key) => [key, css.getPropertyValue(key)]));
+ }, properties);
+ }
+ const motionProperties = ["transition-duration", "transition-timing-function", "transform-style"];
+ const faceProperties = ["background-image", "border-radius", "backface-visibility"];
+ const indicatorProperties = ["width", "height", "background-color", "opacity"];
+ const filterProperties = ["background-color", "border-radius", "min-height", "padding"];
+ await page.goto("/#/games/mhwilds");
+ await expect(page.locator(".game-home-card").first()).toBeVisible();
+ const homeCard = await page.locator(".game-home-card").first().boundingBox();
+ await page.goto("/#/games/aniimo");
+ await expect(page.locator(".aniimo-home-card")).toBeVisible();
+ const aniimoHomeCard = await page.locator(".aniimo-home-card").boundingBox();
+ expect(aniimoHomeCard.width).toBeCloseTo(homeCard.width, 1);
+ expect(aniimoHomeCard.height).toBeCloseTo(homeCard.height, 1);
+ await page.goto("/#/games/mhwilds/monsters");
+ await expect(page.locator(".monster-card").first()).toBeVisible({ timeout: 15000 });
+ const motion = await styles(page.locator(".mhwilds-card-inner").first(), motionProperties);
+ const face = await styles(page.locator(".mhwilds-card-front").first(), faceProperties);
+ const indicator = await styles(page.locator(".mhwilds-flip-indicator").first(), indicatorProperties);
+ const filter = await styles(page.locator(".filter-logic"), filterProperties);
+ await page.goto(route);
+ const card = cards(page).first();
+ await expect(card).toBeVisible();
+ expect(await styles(card.locator(".aniimo-card-inner"), motionProperties)).toEqual(motion);
+ expect(await styles(card.locator(".aniimo-front"), faceProperties)).toEqual(face);
+ expect(await styles(card.locator(".aniimo-front .aniimo-flip"), indicatorProperties)).toEqual(indicator);
+ expect(await styles(page.locator(".filter-logic").first(), filterProperties)).toEqual(filter);
+ await expect(card.locator(".aniimo-face .aniimo-flip")).toHaveCount(2);
+ await expect(card.locator(".aniimo-card > .aniimo-flip")).toHaveCount(0);
+ await card.getByRole("button", { name: /Afficher les capacités/ }).press("Enter");
+ await expect(card.getByRole("button", { name: /Afficher le portrait/ })).toBeFocused();
+ await expect(card.locator(".aniimo-front")).toHaveAttribute("inert", "");
+ await card.getByRole("button", { name: /Afficher le portrait/ }).press("Enter");
+ await expect(card.getByRole("button", { name: /Afficher les capacités/ })).toBeFocused();
+});
+
+test("catalogue, overview and deferred Aniimo loading preserve filters across navigation", async ({ page }) => {
+ const requests = [];
+ page.on("request", (request) => { if (request.url().includes("/data/aniimo/")) requests.push(request.url()); });
+ await page.goto("/#/games");
+ await expect(page.getByRole("heading", { name: "Jeux disponibles" })).toBeVisible();
+ expect(requests).toHaveLength(0);
+ await page.locator('a.game-card-cover[href="#/games/aniimo"]').click();
+ await expect(page.getByRole("heading", { name: "Aniimo", exact: true })).toBeVisible();
+ const cover = page.locator('.aniimo-home-card img');
+ await expect(cover).toHaveAttribute("src", "/static/img/aniimo/emberpup__basic_form.png");
+ await expect(cover).toHaveCSS("object-fit", "cover");
+ await expect(cover).toHaveCSS("object-position", "50% 38%");
+ await expect(cover).toHaveCSS("height", "120px");
+ await page.locator('a[href="#/games/aniimo/aniimos"]').click();
+ await expect(cards(page)).toHaveCount(88);
+ await expect(page.getByRole("status")).toHaveText("88 / 88 Aniimos · 208 / 208 variantes");
+ const switchLabel = page.getByRole("button", { name: "Regrouper les variantes", exact: true }).locator("span:not(.ui-icon)");
+ expect(await switchLabel.evaluate((element) => element.scrollWidth <= element.clientWidth)).toBe(true);
+ await page.getByRole("searchbox", { name: "Nom" }).fill("EMBERPUP");
+ await expect(cards(page)).toHaveCount(1);
+ await page.getByRole("button", { name: "Regrouper les variantes", exact: true }).click();
+ await expect(cards(page)).toHaveCount(3);
+ await page.locator('.game-breadcrumb a[href="#/games/aniimo"]').click();
+ await page.locator('a[href="#/games/aniimo/aniimos"]').click();
+ await expect(cards(page)).toHaveCount(3);
+ await expect(page.getByRole("searchbox")).toHaveValue("EMBERPUP");
+ expect(requests).toHaveLength(3);
+});
+
+test("tabs, flip, matching variants and reset remain independent", async ({ page }) => {
+ await page.goto(route);
+ await page.getByRole("searchbox").fill("emberpup");
+ const card = cards(page).first();
+ await expect(card.getByRole("tab")).toHaveCount(3);
+ await card.getByRole("button", { name: /Afficher les capacités/ }).click();
+ await expect(card.locator(".aniimo-back")).toHaveAttribute("aria-hidden", "false");
+ await card.getByRole("tab", { name: "Hautes-plaines", exact: true }).click();
+ await expect(card).toHaveAttribute("data-form", "highlands");
+ await expect(card.getByRole("button", { name: /Afficher le portrait/ })).toHaveAttribute("aria-pressed", "true");
+ await expect(card.locator(".aniimo-back")).toHaveAttribute("aria-hidden", "false");
+ await expect(card.locator(".aniimo-back .aniimo-attributes").first()).toContainText("Roche");
+ await card.getByRole("tab", { name: "Forme de base", exact: true }).focus();
+ await page.keyboard.press("ArrowRight");
+ await expect(card).toHaveAttribute("data-form", "mountain_woods_form");
+ await expect(card.locator(".aniimo-back")).toHaveAttribute("aria-hidden", "false");
+ await section(page, "Éléments").getByRole("checkbox", { name: "Roche", exact: true }).check();
+ await expect(card.getByRole("tab")).toHaveCount(0);
+ await expect(card).toHaveAttribute("data-form", "highlands");
+ await section(page, "Éléments").getByRole("checkbox", { name: "Eau", exact: true }).check();
+ await expect(page.getByRole("heading", { name: "Aucun résultat" })).toBeVisible();
+ await section(page, "Éléments").getByRole("button", { name: "OU", exact: true }).click();
+ await expect(cards(page)).toHaveCount(1);
+ await page.getByRole("button", { name: "Regrouper les variantes", exact: true }).click();
+ await expect(cards(page)).toHaveCount(1);
+ await page.getByRole("button", { name: "Réinitialiser les filtres", exact: true }).click();
+ await expect(cards(page)).toHaveCount(208);
+ await expect(page.getByRole("button", { name: "Regrouper les variantes", exact: true })).toHaveAttribute("aria-pressed", "false");
+});
+
+test("ability levels use independent native ranges and retain name constraints", async ({ page }) => {
+ await page.goto(route);
+ const abilities = section(page, "Compétences de logis");
+ const carry = page.getByRole("slider", { name: "Porter — Niveau minimum" });
+ const crafting = page.getByRole("slider", { name: "Artisanat — Niveau minimum" });
+ await expect(abilities.getByRole("checkbox")).toHaveCount(0);
+ await expect(carry).toHaveValue("0");
+ await expect(carry).toHaveAttribute("min", "0");
+ await carry.focus();
+ await carry.press("ArrowRight");
+ await carry.press("ArrowRight");
+ await carry.press("ArrowRight");
+ await expect(carry).toHaveValue("3");
+ await expect(crafting).toHaveValue("0");
+ await crafting.focus();
+ await crafting.press("ArrowRight");
+ await crafting.press("ArrowRight");
+ await expect(crafting).toHaveValue("2");
+ await expect(crafting).toHaveCSS("outline-style", "solid");
+ await abilities.getByRole("button", { name: "OU", exact: true }).click();
+ await expect(section(page, "Éléments").getByRole("button", { name: "ET", exact: true })).toHaveAttribute("aria-pressed", "true");
+ await carry.press("Home");
+ await crafting.press("Home");
+ await expect(carry).toHaveAttribute("aria-valuetext", "Sans filtre");
+ await expect(cards(page)).toHaveCount(88);
+ await page.getByRole("searchbox").fill("does-not-exist");
+ await expect(page.getByRole("heading", { name: "Aucun résultat" })).toBeVisible();
+});
+
+test("seven tabs support keyboard navigation and fall back after filtering", async ({ page }) => {
+ await page.goto(route);
+ await page.getByRole("searchbox").fill("iris");
+ const card = cards(page).filter({ has: page.getByRole("heading", { name: "Iris", exact: true }) });
+ await expect(card.getByRole("tab")).toHaveCount(7);
+ const plateau = card.getByRole("tab", { name: "Forme des plateaux", exact: true });
+ await expect(plateau).toHaveText("PLA");
+ await plateau.hover();
+ await expect(page.getByRole("tooltip", { name: "Forme des plateaux", exact: true })).toBeVisible();
+ await page.mouse.move(0, 0);
+ await plateau.focus();
+ await expect(page.getByRole("tooltip", { name: "Forme des plateaux", exact: true })).toBeVisible();
+ const firstTab = await card.getByRole("tab").first().boundingBox();
+ const secondTab = await card.getByRole("tab").nth(1).boundingBox();
+ expect(secondTab.x).toBeGreaterThanOrEqual(firstTab.x + firstTab.width);
+ expect(await card.getByRole("tab").nth(1).evaluate((element) => element.scrollWidth <= element.clientWidth)).toBe(true);
+ await card.getByRole("tab").first().focus();
+ await page.keyboard.press("End");
+ await expect(card).toHaveAttribute("data-form", "prismana_form");
+ await page.keyboard.press("Home");
+ await expect(card).toHaveAttribute("data-form", "basic_form");
+ await card.getByRole("tab", { name: "Forme prismana", exact: true }).click();
+ await expect(card).toHaveAttribute("data-form", "prismana_form");
+ await section(page, "Éléments").getByRole("checkbox", { name: "Herbe", exact: true }).check();
+ await expect(card).toHaveAttribute("data-form", "prismana_form");
+ await section(page, "Éléments").getByRole("checkbox", { name: "Herbe", exact: true }).uncheck();
+ await page.getByRole("searchbox").fill("emberpup");
+ await expect(cards(page)).toHaveAttribute("data-form", "basic_form");
+ await section(page, "Éléments").getByRole("checkbox", { name: "Roche", exact: true }).check();
+ await expect(cards(page)).toHaveAttribute("data-form", "highlands");
+ await section(page, "Éléments").getByRole("checkbox", { name: "Roche", exact: true }).uncheck();
+ await expect(cards(page)).toHaveAttribute("data-form", "highlands");
+});
+
+for (const width of [1280, 390]) {
+ test(`uniform cards, visible variant tabs and scrollable backs at ${width}px`, async ({ page }) => {
+ await page.setViewportSize({ width, height: 844 });
+ await page.emulateMedia({ reducedMotion: "reduce" });
+ await page.goto(route);
+ await expect(cards(page)).toHaveCount(88);
+ const sizes = await page.locator(".aniimo-card").evaluateAll((elements) => elements.map((element) => {
+ const { width, height } = element.getBoundingClientRect();
+ return { width, height };
+ }));
+ expect(Math.max(...sizes.map((size) => size.width)) - Math.min(...sizes.map((size) => size.width))).toBeLessThan(1);
+ expect(Math.max(...sizes.map((size) => size.height)) - Math.min(...sizes.map((size) => size.height))).toBeLessThan(1);
+ await expect(page.locator(".aniimo-front .aniimo-element-icons").first()).toHaveText("");
+ await expect(page.locator(".aniimo-front .aniimo-element-icons img").first()).toHaveAttribute("alt", "Feu");
+ await page.getByRole("searchbox").fill("emberpup");
+ const longFormCard = cards(page).first();
+ const baseHeight = (await longFormCard.boundingBox()).height;
+ await longFormCard.getByRole("tab", { name: "Forme des montagnes boisées", exact: true }).click();
+ const form = longFormCard.locator(".aniimo-front .aniimo-form");
+ await expect(form).toHaveCSS("white-space", "normal");
+ expect(await form.evaluate((element) => element.scrollHeight <= element.clientHeight)).toBe(true);
+ expect((await longFormCard.boundingBox()).height).toBeCloseTo(baseHeight, 1);
+ await longFormCard.getByRole("tab", { name: "Forme de base", exact: true }).click();
+ await page.getByRole("searchbox").fill("");
+ const icons = page.locator(".aniimo-front .aniimo-element-icons").first();
+ const rowBox = await icons.boundingBox();
+ const iconBox = await icons.locator("img").boundingBox();
+ expect(iconBox.x + iconBox.width / 2).toBeCloseTo(rowBox.x + rowBox.width / 2, 1);
+
+ await page.getByRole("searchbox").fill("iris");
+ for (const name of ["Iris", "Valsiris"]) {
+ const card = cards(page).filter({ has: page.getByRole("heading", { name, exact: true }) });
+ const tabs = card.getByRole("tablist");
+ await expect(card.getByRole("tab")).toHaveCount(7);
+ await expect(card.getByRole("tab").last()).toHaveAccessibleName("Forme prismana");
+ expect(await tabs.evaluate((element) => {
+ const box = element.getBoundingClientRect();
+ return element.scrollWidth <= element.clientWidth && [...element.children].every((tab) => {
+ const bounds = tab.getBoundingClientRect();
+ return bounds.left >= box.left && bounds.right <= box.right && tab.scrollWidth <= tab.clientWidth;
+ });
+ })).toBe(true);
+ await expect(card.getByRole("tab").last()).toHaveCSS("background-image", /linear-gradient/);
+ }
+
+ await page.getByRole("searchbox").fill("fenmane");
+ const card = cards(page).first();
+ const before = await card.locator(".aniimo-card").boundingBox();
+ await card.getByRole("button", { name: /Afficher les capacités/ }).click();
+ await card.getByRole("tab", { name: "Forme prismana", exact: true }).click();
+ await expect(card.locator(".aniimo-back")).toHaveAttribute("aria-hidden", "false");
+ const after = await card.locator(".aniimo-card").boundingBox();
+ expect(after.height).toBeCloseTo(before.height, 1);
+ expect(after.width).toBeCloseTo(before.width, 1);
+ const details = card.locator(".aniimo-back-details");
+ await expect(details).toHaveCSS("overflow-y", "auto");
+ await expect(details.locator(".aniimo-abilities li")).toHaveCount(4);
+ if (width === 1280) {
+ expect(await details.evaluate((element) => element.scrollHeight > element.clientHeight)).toBe(true);
+ await details.focus();
+ await details.press("End");
+ await expect.poll(() => details.evaluate((element) => element.scrollTop)).toBeGreaterThan(0);
+ }
+ await card.getByRole("button", { name: /Afficher le portrait/ }).click();
+ expect((await card.locator(".aniimo-card").boundingBox()).height).toBeCloseTo(before.height, 1);
+ });
+}
+
+test("portrait geometry, stable faces and mobile layout", async ({ page }) => {
+ await page.emulateMedia({ reducedMotion: "reduce" });
+ await page.setViewportSize({ width: 390, height: 844 });
+ await page.goto(route);
+ await page.getByRole("searchbox").fill("emberpup");
+ const card = cards(page).first();
+ await card.scrollIntoViewIfNeeded();
+ const portrait = card.locator(".aniimo-portrait img");
+ await expect(portrait).toHaveCSS("object-fit", "cover");
+ await expect(portrait).toHaveCSS("object-position", "50% 50%");
+ await expect(portrait).toHaveJSProperty("naturalWidth", 512);
+ const size = await portrait.boundingBox();
+ expect(size.height).toBeLessThanOrEqual(250);
+ expect(size.height).toBeGreaterThan(240);
+ const before = await card.boundingBox();
+ expect(before.height).toBeLessThan(520);
+ expect(before.width).toBeLessThanOrEqual(280);
+ await card.getByRole("button", { name: /Afficher les capacités/ }).press("Enter");
+ await expect(card.locator(".aniimo-back")).toHaveAttribute("aria-hidden", "false");
+ expect((await card.boundingBox()).height).toBeCloseTo(before.height, 1);
+ const summary = card.locator(".aniimo-back-summary > div");
+ const elementBox = await summary.nth(0).boundingBox();
+ const roleBox = await summary.nth(1).boundingBox();
+ expect(elementBox.y).toBeCloseTo(roleBox.y, 1);
+ expect(elementBox.x + elementBox.width).toBeLessThan(roleBox.x);
+ await expect(card.getByRole("progressbar")).toHaveCount(6);
+ await expect(card.getByRole("progressbar", { name: "PV", exact: true })).toHaveAttribute("aria-valuenow", "67");
+ await expect(card.getByRole("progressbar", { name: "PV", exact: true })).toHaveAttribute("aria-valuemax", "130");
+ await expect(card.getByRole("progressbar", { name: "PV", exact: true })).toHaveCSS("height", "4px");
+ await expect(card.locator(".aniimo-stat-value")).toHaveText(["67", "90", "60", "57", "40", "68"]);
+ await expect(card.locator(".aniimo-stat-total")).toHaveText("Total382");
+ expect(await card.locator(".aniimo-card-inner").evaluate((element) => Number.parseFloat(globalThis.getComputedStyle(element).transitionDuration))).toBeLessThan(0.001);
+ expect(await page.evaluate(() => globalThis.document.documentElement.scrollWidth <= globalThis.innerWidth)).toBe(true);
+});
+
+test("desktop grid shows five columns with the multiple icon and complete stats", async ({ page }) => {
+ await page.setViewportSize({ width: 1920, height: 1080 });
+ await page.goto(route);
+ await expect(cards(page)).toHaveCount(88);
+ const columns = await page.locator(".aniimo-grid").evaluate((element) => globalThis.getComputedStyle(element).gridTemplateColumns.split(" ").length);
+ expect(columns).toBe(5);
+ await expect(page.locator(".ui-icon-multiple")).toHaveCSS("mask-image", /multiples\.svg/);
+ const card = cards(page).first();
+ await card.getByRole("button", { name: /Afficher les capacités/ }).click();
+ await card.locator(".aniimo-back-details").evaluate((element) => { element.scrollTop = element.scrollHeight; });
+ await expect(card.getByRole("progressbar")).toHaveCount(6);
+ await page.screenshot({ path: test.info().outputPath("aniimo-stats-desktop.png") });
+});
+
+test("filter categories share one scroll and retain their selections when collapsed", async ({ page }) => {
+ await page.goto(route);
+ const elements = section(page, "Éléments");
+ const roles = section(page, "Rôles");
+ const abilities = section(page, "Compétences de logis");
+ await expect(page.locator(".aniimo-filter-options")).toHaveCount(3);
+ for (const grid of await page.locator(".aniimo-chip-options").all()) {
+ expect(await grid.evaluate((element) => globalThis.getComputedStyle(element).gridTemplateColumns.split(" ").length)).toBe(2);
+ }
+ const groups = await page.locator(".aniimo-ability-filter").evaluateAll((rows) => rows.map((row) => row.dataset.abilityGroup));
+ expect(groups).toEqual([...groups].sort());
+ await expect(page.locator(".aniimo-filter-toggle").first()).toHaveText("Rôles");
+ await expect(roles.getByRole("checkbox", { name: "SOIN", exact: true })).toHaveCount(1);
+ await expect(roles.getByRole("checkbox", { name: "SOUTIEN", exact: true })).toHaveCount(1);
+ const elementScroll = elements.getByRole("region", { name: "Éléments", exact: true });
+ const scroll = page.locator(".aniimo-filter-sections");
+ await expect(scroll).toHaveCSS("overflow-y", "auto");
+ for (const options of await page.locator(".aniimo-filter-options").all()) {
+ await expect(options).toHaveCSS("overflow-y", "visible");
+ expect(await options.evaluate((element) => element.scrollHeight <= element.clientHeight)).toBe(true);
+ }
+ await scroll.evaluate((element) => { element.scrollTop = element.scrollHeight; });
+ expect(await scroll.evaluate((element) => element.scrollTop)).toBeGreaterThan(0);
+ await elements.getByRole("checkbox", { name: "Roche", exact: true }).check();
+ const results = await page.getByRole("status").textContent();
+ const toggle = elements.getByRole("button", { name: "Éléments", exact: true });
+ await toggle.click();
+ await expect(toggle).toHaveAttribute("aria-expanded", "false");
+ await expect(elementScroll).toBeHidden();
+ await expect(toggle.locator(".aniimo-selected-count")).toHaveText("1");
+ await expect(page.getByRole("status")).toHaveText(results);
+ await expect(roles.getByRole("region")).toBeVisible();
+ await expect(abilities.getByRole("region")).toBeVisible();
+ await toggle.press("Enter");
+ await expect(elements.getByRole("checkbox", { name: "Roche", exact: true })).toBeChecked();
+ const slider = abilities.getByRole("slider", { name: "Porter — Niveau minimum" });
+ await slider.focus();
+ await slider.press("End");
+ await abilities.getByRole("button", { name: "Compétences de logis", exact: true }).click();
+ await expect(slider).toBeHidden();
+ await abilities.getByRole("button", { name: "Compétences de logis", exact: true }).click();
+ await expect(slider).toHaveValue("4");
+});
+
+test("pending and failed data requests show loading and error states", async ({ page }) => {
+ let releaseRequests;
+ const pending = new Promise((resolve) => { releaseRequests = resolve; });
+ await page.route("**/data/aniimo/aniimos.json", async (route) => {
+ await pending;
+ await route.fulfill({ status: 503, body: "unavailable" });
+ });
+ await page.goto(route);
+ await expect(page.getByRole("heading", { name: "Chargement des Aniimos…" })).toBeVisible();
+ releaseRequests();
+ await expect(page.getByRole("heading", { name: "Impossible de charger Aniimo" })).toBeVisible();
+});
diff --git a/e2e/guide-checklist.spec.js b/e2e/guide-checklist.spec.js
new file mode 100644
index 0000000..1dfbf6c
--- /dev/null
+++ b/e2e/guide-checklist.spec.js
@@ -0,0 +1,245 @@
+// Rôle : vérifie copie, liaison de session et progression des checklists dans les guides.
+import { test, expect } from "@playwright/test";
+import { readFileSync } from "node:fs";
+import { resetSokkoStorage } from "./helpers/storage.js";
+import { importChecklistText } from "../website/src/features/toolboxes/modules/textImport/basic.js";
+import { normalizeChecklistData } from "../website/src/features/toolboxes/storage/modules/checklist.js";
+
+const route = "/#/games/aniimo/aniimos";
+const sessionKey = "sokkog:guide-checklists";
+const selector = (page) => page.getByRole("combobox", { name: "Checklist associée", exact: true });
+const hiddenSwitch = (page) => page.getByRole("button", { name: "Masquer les cochés", exact: true });
+const json = (path) => JSON.parse(readFileSync(`website/public/data/${path}`, "utf8"));
+const row = (label, qtyCurrent = 0) => ({ id: label, label, qtyCurrent });
+
+async function seed(page, gameId = "aniimo", lists = {
+ capture: [row("Loufeuteau")],
+ perfect: [row("Loufeuteau", 1)],
+ forms: [row("Loufeuteau — Forme de base", 1), row("Emberpup — Mountain Woods Form")],
+ unrelated: [row("Unrelated")]
+}) {
+ await resetSokkoStorage(page);
+ await page.evaluate(async ({ gameId, lists }) => {
+ await new Promise((resolve, reject) => {
+ const request = globalThis.indexedDB.open("sokkog", 1);
+ request.onsuccess = () => {
+ const db = request.result;
+ const tx = db.transaction(["kv", "modules"], "readwrite");
+ tx.objectStore("kv").put([
+ { id: "guide", name: "Guide toolbox", modules: Object.keys(lists).map((id) => ({ id, type: "checklist", title: id })), moduleColumns: 1 },
+ { id: "other", name: "Other toolbox", modules: [{ id: "outside", type: "checklist", title: "Outside" }] }
+ ], "toolboxes");
+ tx.objectStore("kv").put({ [gameId]: "guide" }, "links");
+ for (const [id, items] of Object.entries(lists)) tx.objectStore("modules").put({ key: `guide:${id}`, data: { items } });
+ tx.objectStore("modules").put({ key: "other:outside", data: { items: [{ id: "outside", label: "Loufeuteau", qtyCurrent: 1 }] } });
+ tx.oncomplete = () => { db.close(); resolve(); };
+ tx.onerror = () => reject(tx.error);
+ };
+ request.onerror = () => reject(request.error);
+ });
+ }, { gameId, lists });
+ await page.reload();
+}
+
+async function readModule(page, moduleId) {
+ return page.evaluate(async (moduleId) => new Promise((resolve, reject) => {
+ const request = globalThis.indexedDB.open("sokkog", 1);
+ request.onsuccess = () => {
+ const db = request.result;
+ const tx = db.transaction("modules", "readonly");
+ const get = tx.objectStore("modules").get(`guide:${moduleId}`);
+ get.onsuccess = () => resolve(get.result.data);
+ tx.oncomplete = () => db.close();
+ tx.onerror = () => reject(tx.error);
+ };
+ }), moduleId);
+}
+
+async function openDrawer(page) {
+ const drawer = page.locator("#toolbox-drawer");
+ if (!await drawer.locator(".drawer-panel").isVisible()) await page.locator(".topbar-actions button").click();
+ await expect(drawer.locator(".drawer-panel")).toBeVisible();
+ return drawer;
+}
+async function closeDrawer(page) {
+ await page.locator("#toolbox-drawer").getByRole("button", { name: "Fermer", exact: true }).click();
+}
+async function choose(page, id) {
+ await openDrawer(page);
+ await selector(page).selectOption(id);
+ await closeDrawer(page);
+}
+async function toggleHidden(page) {
+ await openDrawer(page);
+ await hiddenSwitch(page).click();
+ await closeDrawer(page);
+}
+async function assertLink(page, id, hidden) {
+ await openDrawer(page);
+ await expect(selector(page)).toHaveValue(id);
+ if (id) await expect(hiddenSwitch(page)).toHaveAttribute("aria-pressed", String(hidden));
+ else await expect(hiddenSwitch(page)).toHaveCount(0);
+ await closeDrawer(page);
+}
+
+test("Aniimo copies visible species or forms and round-trips through checklist import", async ({ page, context }) => {
+ await context.grantPermissions(["clipboard-read", "clipboard-write"]);
+ await seed(page);
+ await page.goto(route);
+ await page.getByRole("searchbox").fill("emberpup");
+ await expect(page.locator(".guide-checklist-controls")).toHaveCount(0);
+ const copy = page.locator(".results-copy-button");
+ await expect(copy).toHaveAccessibleName(/sans les variantes/);
+ await expect(page.locator('[data-copy-target="true"]')).toHaveText("1 / 88 Aniimos");
+ await copy.click();
+ await expect.poll(() => page.evaluate(() => globalThis.navigator.clipboard.readText())).toBe("Loufeuteau:1");
+ await page.getByRole("button", { name: "Regrouper les variantes", exact: true }).click();
+ await expect(copy).toHaveAccessibleName(/toutes les variantes/);
+ await expect(page.locator('[data-copy-target="true"]')).toHaveText("3 / 208 variantes");
+ await copy.click();
+ const text = await page.evaluate(() => globalThis.navigator.clipboard.readText());
+ const imported = importChecklistText(text, { uid: (prefix) => `${prefix}-${Math.random()}`, normalizeChecklistData });
+ expect(imported.items.map((item) => item.label)).toEqual(["Loufeuteau — Forme de base", "Loufeuteau — Forme des montagnes boisées", "Loufeuteau — Hautes-plaines"]);
+ expect(imported.items.every((item) => item.qtyTarget === 1 && item.qtyCurrent === 0)).toBe(true);
+ await choose(page, "forms");
+ await toggleHidden(page);
+ await copy.click();
+ expect(await page.evaluate(() => globalThis.navigator.clipboard.readText())).not.toContain("Forme de base");
+ await choose(page, "perfect");
+ await expect(copy).toBeDisabled();
+ await expect(page.getByRole("heading", { name: "Aucun résultat" })).toBeVisible();
+});
+
+for (const width of [1280, 390]) {
+ test(`drawer linking, live completion and session restoration at ${width}px`, async ({ page }) => {
+ await page.setViewportSize({ width, height: 900 });
+ await seed(page);
+ await page.goto(route);
+ await page.getByRole("searchbox").fill("emberpup");
+ const card = page.locator(".aniimo-result");
+ await openDrawer(page);
+ await expect(selector(page)).toHaveValue("");
+ await expect(selector(page).locator("option")).toHaveCount(4);
+ const before = await readModule(page, "forms");
+ await selector(page).selectOption("forms");
+ const panel = page.locator(".guide-checklist-controls");
+ const bounds = await panel.boundingBox();
+ const selectBounds = await selector(page).boundingBox();
+ expect(selectBounds.x + selectBounds.width / 2).toBeCloseTo(bounds.x + bounds.width / 2, 0);
+ await page.screenshot({ path: test.info().outputPath("linked-drawer.png") });
+ await closeDrawer(page);
+ await expect(card).toHaveAttribute("data-checklist-complete", "true");
+ await expect(card.locator(".aniimo-front .guide-checklist-mark")).toHaveAccessibleName("Coché");
+ await toggleHidden(page);
+ await expect(card.getByRole("tab")).toHaveCount(2);
+ await expect(card).toHaveAttribute("data-form", "mountain_woods_form");
+ expect(await readModule(page, "forms")).toEqual(before);
+ await choose(page, "capture");
+ await assertLink(page, "capture", true);
+ await expect(card.getByRole("tab")).toHaveCount(3);
+ const drawer = await openDrawer(page);
+ await drawer.locator('[data-module-type="checklist"]').filter({ has: page.getByRole("button", { name: "Retirer capture", exact: true }) }).getByRole("checkbox", { name: "Terminer Loufeuteau", exact: true }).check();
+ await closeDrawer(page);
+ await expect(card).toHaveCount(0);
+ await page.reload();
+ await assertLink(page, "capture", true);
+ await page.getByRole("searchbox").fill("emberpup");
+ await expect(card).toHaveCount(0);
+ await openDrawer(page);
+ await hiddenSwitch(page).press("Space");
+ await closeDrawer(page);
+ await expect(card).toHaveAttribute("data-checklist-complete", "true");
+ await page.getByRole("button", { name: "Réinitialiser les filtres", exact: true }).click();
+ await openDrawer(page);
+ await expect(selector(page)).toHaveValue("capture");
+ await page.getByRole("button", { name: "Dissocier", exact: true }).click();
+ await expect(selector(page)).toHaveValue("");
+ await expect(hiddenSwitch(page)).toHaveCount(0);
+ await closeDrawer(page);
+ await expect(page.locator('[data-checklist-complete="true"]')).toHaveCount(0);
+ expect(await page.evaluate((key) => globalThis.sessionStorage.getItem(key), sessionKey)).toBeNull();
+ expect(await page.evaluate(() => globalThis.document.documentElement.scrollWidth <= globalThis.innerWidth)).toBe(true);
+ });
+}
+
+for (const [gameId, category, cardClass, dataPath, dataKey] of [
+ ["mhwilds", "monsters", ".monster-card", "mhwilds/monsters.json", "monsters"],
+ ["mhwilds", "endemic", ".endemic-card", "mhwilds/endemic_life.json", "endemicLife"],
+ ["diablo4", "affixes", ".diablo4-affix-card", "diablo4/affixes_types.json", "affixes"]
+]) {
+ test(`${gameId}/${category} links from its drawer and hides completed cards`, async ({ page }) => {
+ const item = json(dataPath)[dataKey][0];
+ const label = gameId === "mhwilds" ? json("mhwilds/i18n/fr.json")[item.name] : item.label;
+ await seed(page, gameId, { progress: [row(label, 1)] });
+ await page.goto(`/#/games/${gameId}/${category}`);
+ await expect(page.locator(cardClass).first()).toBeVisible();
+ const count = await page.locator(cardClass).count();
+ await choose(page, "progress");
+ await expect(page.locator(`${cardClass}[data-checklist-complete="true"]`)).toHaveCount(1);
+ await toggleHidden(page);
+ await expect(page.locator(cardClass)).toHaveCount(count - 1);
+ await page.setViewportSize({ width: 390, height: 844 });
+ await page.reload();
+ await assertLink(page, "progress", true);
+ await expect(page.locator(cardClass)).toHaveCount(count - 1);
+ expect(await page.evaluate(() => globalThis.document.documentElement.scrollWidth <= globalThis.innerWidth)).toBe(true);
+ });
+}
+
+test("renames retain a zero-match selection; changing toolbox clears the association", async ({ page }) => {
+ await seed(page);
+ await page.goto(route);
+ await choose(page, "capture");
+ const drawer = await openDrawer(page);
+ const checklist = drawer.locator('[data-module-type="checklist"]').filter({ has: page.getByRole("button", { name: "Retirer capture", exact: true }) });
+ await checklist.getByRole("button", { name: "Modifier Loufeuteau", exact: true }).click();
+ await checklist.getByRole("textbox", { name: "Libellé", exact: true }).fill("Unrelated now");
+ await checklist.getByRole("textbox", { name: "Libellé", exact: true }).press("Enter");
+ await expect(selector(page)).toHaveValue("capture");
+ await expect(page.getByText("Cette checklist ne contient plus de correspondance avec le guide.")).toBeVisible();
+ await drawer.getByRole("button", { name: "Lier une toolbox", exact: true }).click();
+ const dialog = page.getByRole("dialog", { name: "Lier une toolbox" });
+ await dialog.getByRole("combobox").selectOption("other");
+ await dialog.getByRole("button", { name: "Valider", exact: true }).click();
+ await expect(selector(page)).toHaveValue("");
+ await expect(selector(page).locator("option")).toHaveCount(2);
+ expect(await page.evaluate((key) => globalThis.sessionStorage.getItem(key), sessionKey)).toBeNull();
+});
+
+test("deleting a selected checklist clears its link immediately", async ({ page }) => {
+ await seed(page);
+ await page.goto(route);
+ await choose(page, "capture");
+ await toggleHidden(page);
+ const drawer = await openDrawer(page);
+ await drawer.getByRole("button", { name: "Retirer capture", exact: true }).click();
+ await page.getByRole("dialog").getByRole("button", { name: "Retirer", exact: true }).click();
+ await expect(selector(page)).toHaveValue("");
+ await expect(hiddenSwitch(page)).toHaveCount(0);
+ await page.reload();
+ await assertLink(page, "", false);
+ expect(await page.evaluate((key) => globalThis.sessionStorage.getItem(key), sessionKey)).toBeNull();
+});
+
+test("unavailable session storage retains links across navigation; overview has no guide controls", async ({ page }) => {
+ await seed(page);
+ await page.addInitScript((key) => {
+ for (const method of ["getItem", "setItem", "removeItem"]) {
+ const original = globalThis.Storage.prototype[method];
+ globalThis.Storage.prototype[method] = function (name, ...args) {
+ if (name === key) throw new Error("Session storage unavailable");
+ return original.call(this, name, ...args);
+ };
+ }
+ }, sessionKey);
+ await page.reload();
+ await page.goto(route);
+ await choose(page, "perfect");
+ await toggleHidden(page);
+ await page.goto("/#/games/aniimo");
+ await openDrawer(page);
+ await expect(page.locator(".guide-checklist-controls")).toHaveCount(0);
+ await closeDrawer(page);
+ await page.goto(route);
+ await assertLink(page, "perfect", true);
+});
diff --git a/e2e/nds-controls.spec.js b/e2e/nds-controls.spec.js
index 088fb5a..d127dda 100644
--- a/e2e/nds-controls.spec.js
+++ b/e2e/nds-controls.spec.js
@@ -2,6 +2,23 @@
import { test, expect } from "@playwright/test";
import { mountStyles, computed, expectPseudo } from "./fixtures/compiled-styles.js";
+test("native ranges support public sizes, keyboard thresholds and disabled states", async ({ page }) => {
+ for (const [size, height] of [["md", 44], ["sm", 36]]) {
+ await mountStyles(page, ``);
+ const range = page.getByRole("slider", { name: "Niveau minimum" });
+ await expect(range).toHaveCSS("height", `${height}px`);
+ await range.focus();
+ await range.press("End");
+ await expect(range).toHaveValue("4");
+ await range.press("ArrowLeft");
+ await expect(range).toHaveValue("3");
+ await expect(range).toHaveCSS("outline-style", "solid");
+ await range.evaluate((element) => { element.disabled = true; });
+ await expect(range).toBeDisabled();
+ await expect(range).toHaveCSS("cursor", "not-allowed");
+ }
+});
+
test("fields, selects and date triggers keep public sizes and keyboard focus", async ({ page }) => {
for (const [size, height, textareaHeight] of [["md", 44, 108], ["sm", 36, 88], ["xs", 32, 72]]) {
await mountStyles(page, `
diff --git a/e2e/nds-patterns.spec.js b/e2e/nds-patterns.spec.js
index 79f2b9e..7ea1d7a 100644
--- a/e2e/nds-patterns.spec.js
+++ b/e2e/nds-patterns.spec.js
@@ -48,12 +48,25 @@ test("badges, notices, progress and chips expose meaningful visual states", asyn
await expect(progress.locator("span")).toHaveCSS("width", "99px");
await progress.locator("span").evaluate((element) => { element.style.width = "100%"; });
await expect(progress.locator("span")).toHaveCSS("width", "198px");
+ await progress.evaluate((element) => { element.dataset.size = "sm"; });
+ await expect(progress).toHaveCSS("height", "4px");
}
const chip = page.locator(".nds-chip");
await expect(chip).toHaveCSS("background-color", "rgba(5, 7, 17, 0.32)");
await chip.locator("input").check();
await page.mouse.move(0, 0);
await expect(chip).toHaveCSS("background-color", "rgba(139, 92, 246, 0.14)");
+ await chip.evaluate((element) => {
+ element.dataset.size = "sm";
+ const image = globalThis.document.createElement("img");
+ image.src = "/static/icons/multiples.svg";
+ image.alt = "";
+ element.append(image);
+ });
+ await expect(chip).toHaveCSS("min-height", "30px");
+ await expect(chip).toHaveCSS("font-size", "12px");
+ await expect(chip.locator("img")).toHaveCSS("width", "16px");
+ await expect(chip.locator("img")).toHaveCSS("height", "16px");
});
for (const width of [1280, 390]) {
diff --git a/tests/aniimo.test.mjs b/tests/aniimo.test.mjs
new file mode 100644
index 0000000..8ad368d
--- /dev/null
+++ b/tests/aniimo.test.mjs
@@ -0,0 +1,114 @@
+// Rôle : vérifie les données Aniimo et le filtrage des variantes avant regroupement.
+import { readFile, access } from "node:fs/promises";
+import assert from "node:assert/strict";
+import { test } from "node:test";
+import { createAniimoFilters, filterAniimoVariants, groupAniimoVariants, prepareAniimoData } from "../website/src/features/games/aniimo/utils.js";
+import { loadAniimoData } from "../website/src/features/games/loaders.js";
+import { validateAniimoData } from "./helpers/data-validation.mjs";
+
+const readJson = async (path) => JSON.parse(await readFile(`website/public/data/${path}`, "utf8"));
+const [aniimos, fr, en] = await Promise.all([readJson("aniimo/aniimos.json"), readJson("aniimo/i18n/fr.json"), readJson("aniimo/i18n/en.json")]);
+const data = prepareAniimoData(aniimos, fr, en);
+
+test("Aniimo attributes have valid stats and matching totals", () => {
+ validateAniimoData(aniimos, [fr, en]);
+ const invalid = structuredClone(aniimos[0]);
+ invalid.variations[0].attributes.total++;
+ assert.throws(() => validateAniimoData([invalid], [fr, en]), /Stats total/);
+ invalid.variations[0].attributes.stats.hp = -1;
+ assert.throws(() => validateAniimoData([invalid], [fr, en]), /nonnegative integers/);
+});
+const filter = (overrides, variants = data.variants) => filterAniimoVariants(variants, { ...createAniimoFilters(), ...overrides });
+
+test("Aniimo data has valid variants, translations, portraits and icons", async () => {
+ validateAniimoData(aniimos, [fr, en]);
+ const game = (await readJson("games.json")).games.find((game) => game.id === "aniimo");
+ assert.ok(game);
+ assert.equal(game.images.aniimosCover, "/static/img/aniimo/emberpup__basic_form.png");
+ const assets = new Set([game.images.cardCover, game.images.heroBg, game.images.aniimosCover, ...data.variants.map((variant) => variant.image)]);
+ for (const option of Object.values(data.filterOptions).flat()) assets.add(`/static/img/aniimo/${option}.png`);
+ await Promise.all([...assets].map((asset) => access(`website/public${asset}`)));
+ for (const variant of data.variants) {
+ const png = await readFile(`website/public${variant.image}`);
+ assert.equal(png.readUInt32BE(16), 512);
+ assert.equal(png.readUInt32BE(20), 840);
+ }
+});
+
+test("validation rejects duplicate forms, missing translations and invalid levels", () => {
+ const entry = structuredClone(aniimos[0]);
+ entry.variations.push(entry.variations[0]);
+ assert.throws(() => validateAniimoData([entry], [fr, en]), /Duplicate form/);
+ assert.throws(() => validateAniimoData(aniimos, [{}]), /must be a string/);
+ for (const level of [0, 5, 1.5, "3"]) {
+ const invalid = structuredClone(aniimos[0]);
+ invalid.variations[0].homeland_abilities[0].level = level;
+ assert.throws(() => validateAniimoData([invalid], [fr, en]), /integer between 1 and 4/);
+ }
+});
+
+test("name searches accept French, English, accents and case", () => {
+ assert.deepEqual(filter({ name: " LOUFEUTEAU " }).map((v) => v.key), filter({ name: "Emberpup" }).map((v) => v.key));
+ assert.ok(filter({ name: "celestia" }).some((variant) => variant.name === "celestis"));
+ assert.ok(filter({ name: "pomoeuf" }).some((variant) => variant.name === "pomegg"));
+ assert.deepEqual(filter({ name: "inexistant-xyz" }), []);
+});
+
+test("Prismana remains the final variation in data and grouped results", () => {
+ for (const group of groupAniimoVariants(data.variants)) {
+ if (group.some((variant) => variant.form === "prismana_form")) assert.equal(group.at(-1).form, "prismana_form");
+ }
+ const entry = structuredClone(aniimos.find((aniimo) => aniimo.variations.length > 1 && aniimo.variations.at(-1).form === "prismana_form"));
+ entry.variations.unshift(entry.variations.pop());
+ assert.throws(() => validateAniimoData([entry], [fr, en]), /Prismana must be last/);
+});
+
+const fixture = prepareAniimoData([{
+ id: "001", name: "example", elements: ["rock", "water"], role: "support",
+ variations: [
+ { form: "rock_form", elements: ["rock"], role: "dps", homeland_abilities: [{ type: "carry", level: 3 }] },
+ { form: "water_form", elements: ["water"], role: "heal", homeland_abilities: [{ type: "crafting", level: 2 }] },
+ { form: "both_form", elements: ["rock", "water"], role: "dps", homeland_abilities: [{ type: "carry", level: 4 }, { type: "crafting", level: 1 }] }
+ ]
+}], {}, {}).variants;
+
+test("element AND never borrows elements or roles from the parent or sibling variants", () => {
+ assert.deepEqual(filter({ elements: ["rock", "water"] }, fixture).map((v) => v.form), ["both_form"]);
+ assert.equal(filter({ elements: ["rock", "water"], elementsLogic: "or" }, fixture).length, 3);
+ assert.equal(filter({ roles: ["support"] }, fixture).length, 0);
+ assert.equal(filter({ roles: ["dps", "heal"] }, fixture).length, 3);
+});
+
+test("ability thresholds remain independent, inclusive and constrained to a single variant", () => {
+ assert.equal(filter({ abilities: { carry: 3, crafting: 2 } }, fixture).length, 0);
+ assert.equal(filter({ abilities: { carry: 3, crafting: 2 }, abilitiesLogic: "or" }, fixture).length, 3);
+ assert.deepEqual(filter({ abilities: { carry: 4, crafting: 2 }, abilitiesLogic: "or" }, fixture).map((v) => v.form), ["water_form", "both_form"]);
+ assert.deepEqual(filter({ abilities: { carry: 3, crafting: 1 } }, fixture).map((v) => v.form), ["both_form"]);
+ assert.equal(filter({ elements: ["rock"], roles: ["heal"], abilitiesLogic: "or", abilities: { carry: 1, crafting: 1 } }, fixture).length, 0);
+});
+
+test("grouping preserves order and contains only variants that pass all filters", () => {
+ const visible = filter({ elements: ["water"] }, fixture);
+ const groups = groupAniimoVariants(visible);
+ assert.equal(groups.length, 1);
+ assert.deepEqual(groups[0].map((v) => v.form), ["water_form", "both_form"]);
+ assert.deepEqual(groupAniimoVariants([]), []);
+ assert.equal(groupAniimoVariants(data.variants).length, aniimos.length);
+});
+
+test("loader passes cancellation to all requests and reports failed responses", async (context) => {
+ const calls = [];
+ const controller = new AbortController();
+ const bodies = [aniimos, fr, en];
+ context.mock.method(globalThis, "fetch", async (url, options) => {
+ calls.push({ url, options });
+ const body = bodies[calls.length - 1];
+ return { ok: true, json: async () => body };
+ });
+ const result = await loadAniimoData(controller.signal);
+ assert.equal(result.variants.length, data.variants.length);
+ assert.equal(calls.length, 3);
+ assert.ok(calls.every((call) => call.options.signal === controller.signal));
+ globalThis.fetch.mock.mockImplementation(async () => ({ ok: false }));
+ await assert.rejects(loadAniimoData(controller.signal), /indisponibles/);
+});
diff --git a/tests/guide-checklist.test.mjs b/tests/guide-checklist.test.mjs
new file mode 100644
index 0000000..a7d94ca
--- /dev/null
+++ b/tests/guide-checklist.test.mjs
@@ -0,0 +1,54 @@
+// Rôle : vérifie les correspondances des guides et la validité des associations de session.
+import assert from "node:assert/strict";
+import test from "node:test";
+import { aniimoChecklistEntries, matchGuideChecklist, normalizeGuideLabel, validGuideAssociations } from "../website/src/features/games/guideChecklist.js";
+
+const variants = [{ key: "1:base", name: "emberpup", form: "base" }, { key: "1:mountain", name: "emberpup", form: "mountain" }];
+const entries = aniimoChecklistEntries(variants, { emberpup: "Loufeuteau", base: "Forme de base", mountain: "Montagnes" }, { emberpup: "Emberpup", base: "Basic form", mountain: "Mountain form" });
+
+test("full names normalize accents, case and whitespace without approximate matching", () => {
+ assert.equal(normalizeGuideLabel(" ŒUF Écarlate \n"), "oeuf ecarlate");
+ const result = matchGuideChecklist([{ key: "egg", labels: ["Œuf écarlate"] }], { items: [
+ { label: "OEUF ECARLATE", qtyCurrent: 1 }, { label: "Œuf", qtyCurrent: 0 }
+ ] });
+ assert.equal(result.count, 1);
+ assert.ok(result.completed.has("egg"));
+ assert.equal(matchGuideChecklist(entries, { items: [{ label: "Loufeu", qtyCurrent: 1 }] }).count, 0);
+});
+
+test("species completion covers forms, but a specific French or English row takes priority", () => {
+ const data = { sections: [{ items: [
+ { label: "EMBERPUP", qtyCurrent: 1 },
+ { label: "Loufeuteau — forme de base", qtyCurrent: 0 }
+ ] }] };
+ const result = matchGuideChecklist(entries, data);
+ assert.equal(result.count, 2);
+ assert.deepEqual([...result.completed], ["1:mountain"]);
+ const english = matchGuideChecklist(entries, { items: [{ label: "Emberpup — Basic form", qtyCurrent: 1 }] });
+ assert.equal(english.count, 1);
+ assert.deepEqual([...english.completed], ["1:base"]);
+});
+
+test("all duplicate lines must reach their own target, including translated aliases", () => {
+ const data = { items: [
+ { label: "Loufeuteau", qtyTarget: 3, qtyCurrent: 2 },
+ { label: "Emberpup", qtyCurrent: 1 },
+ { label: "Unrelated", qtyCurrent: 1 }
+ ] };
+ assert.equal(matchGuideChecklist(entries, data).completed.size, 0);
+ data.items[0].qtyCurrent = 3;
+ assert.equal(matchGuideChecklist(entries, data).completed.size, 2);
+ data.items.push({ label: "Loufeuteau" });
+ assert.equal(matchGuideChecklist(entries, data).completed.size, 0);
+ assert.equal(matchGuideChecklist(entries, {}).count, 0);
+});
+
+test("session associations require the current linked toolbox and an existing checklist", () => {
+ const toolbox = { id: "tb", modules: [{ id: "capture", type: "checklist" }, { id: "notes", type: "notepad" }] };
+ const association = { toolboxId: "tb", moduleId: "capture", hideCompleted: true };
+ const state = { "aniimo/aniimos": association, "mhwilds/monsters": { ...association, moduleId: "notes" }, unknown: association };
+ assert.deepEqual(validGuideAssociations(state, [toolbox], { aniimo: "tb", mhwilds: "tb" }), { "aniimo/aniimos": association });
+ assert.deepEqual(validGuideAssociations(state, [toolbox], { aniimo: "other" }), {});
+ assert.deepEqual(validGuideAssociations(state, [], { aniimo: "tb" }), {});
+ assert.deepEqual(validGuideAssociations({ "aniimo/aniimos": null }, [toolbox], { aniimo: "tb" }), {});
+});
diff --git a/tests/helpers/data-validation.mjs b/tests/helpers/data-validation.mjs
index 5fec647..ea35d56 100644
--- a/tests/helpers/data-validation.mjs
+++ b/tests/helpers/data-validation.mjs
@@ -5,6 +5,48 @@ const SNAKE_CASE_RE = /^[a-z0-9]+(?:_[a-z0-9]+)*$/;
const PHYSICAL_HITZONE_KEYS = ["cut", "blunt", "ammo"];
const ELEMENTAL_HITZONE_KEYS = ["fire", "water", "thunder", "ice", "dragon"];
+export function validateAniimoData(aniimos, dictionaries) {
+ assert.ok(Array.isArray(aniimos) && aniimos.length > 0, "Aniimo data must contain entries");
+ const ids = new Set();
+ const names = new Set();
+ function translated(key) {
+ assert.match(key, SNAKE_CASE_RE);
+ for (const dictionary of dictionaries) assertNonEmptyString(dictionary, key);
+ }
+ for (const aniimo of aniimos) {
+ assert.match(aniimo.id, /^\d+$/);
+ assert.ok(!ids.has(aniimo.id), `Duplicate Aniimo id: ${aniimo.id}`);
+ assert.ok(!names.has(aniimo.name), `Duplicate Aniimo name: ${aniimo.name}`);
+ ids.add(aniimo.id);
+ names.add(aniimo.name);
+ translated(aniimo.name);
+ assert.ok(Array.isArray(aniimo.variations) && aniimo.variations.length > 0);
+ const forms = new Set();
+ for (const variation of aniimo.variations) {
+ const stats = variation.attributes?.stats;
+ assert.deepEqual(Object.keys(stats || {}).sort(), ["atk", "break", "hp", "m_def", "p_def", "regen"], "Aniimo must have six stats");
+ assert.ok(Object.values(stats).every((value) => Number.isInteger(value) && value >= 0), "Stats must be nonnegative integers");
+ assert.equal(variation.attributes.total, Object.values(stats).reduce((sum, value) => sum + value, 0), "Stats total must equal their sum");
+ assert.ok(!forms.has(variation.form), `Duplicate form: ${aniimo.name}/${variation.form}`);
+ forms.add(variation.form);
+ translated(variation.form);
+ translated(variation.role);
+ assertStringArray(variation, "elements");
+ assert.equal(new Set(variation.elements).size, variation.elements.length);
+ variation.elements.forEach(translated);
+ assert.ok(Array.isArray(variation.homeland_abilities));
+ const types = new Set();
+ for (const ability of variation.homeland_abilities) {
+ translated(ability.type);
+ assert.ok(!types.has(ability.type), `Duplicate ability: ${ability.type}`);
+ types.add(ability.type);
+ assert.ok(Number.isInteger(ability.level) && ability.level >= 1 && ability.level <= 4, "Ability level must be an integer between 1 and 4");
+ }
+ }
+ if (forms.has("prismana_form")) assert.equal(aniimo.variations.at(-1).form, "prismana_form", `Prismana must be last: ${aniimo.name}`);
+ }
+}
+
function valueAt(object, path) {
return path.split(".").reduce((value, key) => value?.[key], object);
}
@@ -47,6 +89,53 @@ export function validateSiteContent(site) {
"gamesPage.description",
"gamesPage.emptyTitle",
"gamesPage.emptyText",
+ "guideChecklist.label",
+ "guideChecklist.untitled",
+ "guideChecklist.none",
+ "guideChecklist.linkToolbox",
+ "guideChecklist.noCompatible",
+ "guideChecklist.noMatches",
+ "guideChecklist.matches",
+ "guideChecklist.unlink",
+ "guideChecklist.hideCompleted",
+ "guideChecklist.completed",
+ "aniimo.copyChecklist",
+ "aniimo.copyVariantsChecklist",
+ "aniimo.statsTitle",
+ "aniimo.statsTotal",
+ "aniimo.stats.hp",
+ "aniimo.stats.atk",
+ "aniimo.stats.p_def",
+ "aniimo.stats.m_def",
+ "aniimo.stats.break",
+ "aniimo.stats.regen",
+ "aniimo.title",
+ "aniimo.description",
+ "aniimo.homeDescription",
+ "aniimo.filters",
+ "aniimo.name",
+ "aniimo.searchPlaceholder",
+ "aniimo.elements",
+ "aniimo.roles",
+ "aniimo.role",
+ "aniimo.abilities",
+ "aniimo.minimum",
+ "aniimo.abilityLevelsHint",
+ "aniimo.abilityDisabled",
+ "aniimo.level",
+ "aniimo.match",
+ "aniimo.and",
+ "aniimo.or",
+ "aniimo.reset",
+ "aniimo.groupVariants",
+ "aniimo.variants",
+ "aniimo.results",
+ "aniimo.emptyTitle",
+ "aniimo.emptyText",
+ "aniimo.loading",
+ "aniimo.error",
+ "aniimo.showFront",
+ "aniimo.showBack",
"library.eyebrow",
"library.title",
"library.description",
diff --git a/website/public/data/aniimo/aniimos.json b/website/public/data/aniimo/aniimos.json
new file mode 100644
index 0000000..a85d9fa
--- /dev/null
+++ b/website/public/data/aniimo/aniimos.json
@@ -0,0 +1,7802 @@
+[
+ {
+ "id": "001",
+ "name": "emberpup",
+ "elements": [
+ "fire"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 382,
+ "stats": {
+ "hp": 67,
+ "break": 40,
+ "atk": 90,
+ "m_def": 57,
+ "p_def": 60,
+ "regen": 68
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 382,
+ "stats": {
+ "hp": 67,
+ "break": 40,
+ "atk": 90,
+ "m_def": 57,
+ "p_def": 60,
+ "regen": 68
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 1
+ },
+ {
+ "type": "fire",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 382,
+ "stats": {
+ "hp": 67,
+ "break": 40,
+ "atk": 90,
+ "m_def": 57,
+ "p_def": 60,
+ "regen": 68
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "002",
+ "name": "flameruff",
+ "elements": [
+ "fire"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 456,
+ "stats": {
+ "hp": 85,
+ "break": 45,
+ "atk": 111,
+ "m_def": 63,
+ "p_def": 72,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 456,
+ "stats": {
+ "hp": 85,
+ "break": 45,
+ "atk": 111,
+ "m_def": 63,
+ "p_def": 72,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 1
+ },
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 456,
+ "stats": {
+ "hp": 85,
+ "break": 45,
+ "atk": 111,
+ "m_def": 63,
+ "p_def": 72,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "003",
+ "name": "scorchhowl",
+ "elements": [
+ "fire"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 511,
+ "stats": {
+ "hp": 95,
+ "break": 52,
+ "atk": 119,
+ "m_def": 75,
+ "p_def": 80,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 511,
+ "stats": {
+ "hp": 95,
+ "break": 52,
+ "atk": 119,
+ "m_def": 75,
+ "p_def": 80,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "thunderstorm_form",
+ "elements": [
+ "fire",
+ "electric"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 2
+ },
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 511,
+ "stats": {
+ "hp": 95,
+ "break": 52,
+ "atk": 119,
+ "m_def": 75,
+ "p_def": 80,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 2
+ },
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 511,
+ "stats": {
+ "hp": 95,
+ "break": 52,
+ "atk": 119,
+ "m_def": 75,
+ "p_def": 80,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 4
+ },
+ {
+ "type": "carry",
+ "level": 4
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 511,
+ "stats": {
+ "hp": 95,
+ "break": 52,
+ "atk": 119,
+ "m_def": 75,
+ "p_def": 80,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "004",
+ "name": "inferlupa",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 541,
+ "stats": {
+ "hp": 95,
+ "break": 108,
+ "atk": 89,
+ "m_def": 80,
+ "p_def": 70,
+ "regen": 99
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 4
+ },
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 4
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 541,
+ "stats": {
+ "hp": 95,
+ "break": 108,
+ "atk": 89,
+ "m_def": 80,
+ "p_def": 70,
+ "regen": 99
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "005",
+ "name": "celestis",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 429,
+ "stats": {
+ "hp": 81,
+ "break": 46,
+ "atk": 106,
+ "m_def": 58,
+ "p_def": 58,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "climb",
+ "level": 1
+ },
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "006",
+ "name": "stellarys",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 95,
+ "break": 54,
+ "atk": 125,
+ "m_def": 69,
+ "p_def": 68,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "deluge_form",
+ "elements": [
+ "dark",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 2
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 95,
+ "break": 54,
+ "atk": 125,
+ "m_def": 69,
+ "p_def": 68,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "ice",
+ "dark"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 4
+ },
+ {
+ "type": "dark",
+ "level": 4
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 95,
+ "break": 54,
+ "atk": 125,
+ "m_def": 69,
+ "p_def": 68,
+ "regen": 94
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "007",
+ "name": "chirpi",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 452,
+ "stats": {
+ "hp": 97,
+ "break": 43,
+ "atk": 83,
+ "m_def": 69,
+ "p_def": 67,
+ "regen": 93
+ }
+ }
+ },
+ {
+ "form": "beach_form",
+ "elements": [
+ "water",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 452,
+ "stats": {
+ "hp": 97,
+ "break": 43,
+ "atk": 83,
+ "m_def": 69,
+ "p_def": 67,
+ "regen": 93
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 452,
+ "stats": {
+ "hp": 97,
+ "break": 43,
+ "atk": 83,
+ "m_def": 69,
+ "p_def": 67,
+ "regen": 93
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "008",
+ "name": "tromber",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 531,
+ "stats": {
+ "hp": 113,
+ "break": 51,
+ "atk": 119,
+ "m_def": 81,
+ "p_def": 79,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "beach_form",
+ "elements": [
+ "water",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 531,
+ "stats": {
+ "hp": 113,
+ "break": 51,
+ "atk": 119,
+ "m_def": 81,
+ "p_def": 79,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 2
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 531,
+ "stats": {
+ "hp": 113,
+ "break": 51,
+ "atk": 119,
+ "m_def": 81,
+ "p_def": 79,
+ "regen": 88
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "flight",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "009",
+ "name": "cornet",
+ "elements": [
+ "wind"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 54,
+ "atk": 121,
+ "m_def": 70,
+ "p_def": 74,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "beach_form",
+ "elements": [
+ "water",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 54,
+ "atk": 121,
+ "m_def": 70,
+ "p_def": 74,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 2
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 54,
+ "atk": 121,
+ "m_def": 70,
+ "p_def": 74,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "electric",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 4
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 54,
+ "atk": 121,
+ "m_def": 70,
+ "p_def": 74,
+ "regen": 94
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 3
+ }
+ ],
+ "mobility": [
+ {
+ "name": "flight",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "010",
+ "name": "tubster",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 108,
+ "break": 107,
+ "atk": 80,
+ "m_def": 109,
+ "p_def": 66,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "beach_form",
+ "elements": [
+ "water",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "wind",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 108,
+ "break": 107,
+ "atk": 80,
+ "m_def": 109,
+ "p_def": 66,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 2
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 108,
+ "break": 107,
+ "atk": 80,
+ "m_def": 109,
+ "p_def": 66,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "011",
+ "name": "iris",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "forest_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "plateau_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "grassland_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 397,
+ "stats": {
+ "hp": 69,
+ "break": 50,
+ "atk": 101,
+ "m_def": 53,
+ "p_def": 53,
+ "regen": 71
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "terrain_interaction",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "012",
+ "name": "irisal",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "forest_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "plateau_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "grassland_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 4
+ },
+ {
+ "type": "leisure",
+ "level": 4
+ }
+ ],
+ "role": "dps",
+ "attributes": {
+ "total": 504,
+ "stats": {
+ "hp": 100,
+ "break": 52,
+ "atk": 118,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 94
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "terrain_interaction",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "013",
+ "name": "skippy",
+ "elements": [
+ "water"
+ ],
+ "role": "heal",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 402,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 70,
+ "m_def": 76,
+ "p_def": 66,
+ "regen": 60
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 402,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 70,
+ "m_def": 76,
+ "p_def": 66,
+ "regen": 60
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 402,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 70,
+ "m_def": 76,
+ "p_def": 66,
+ "regen": 60
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "014",
+ "name": "pranky",
+ "elements": [
+ "water"
+ ],
+ "role": "heal",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 482,
+ "stats": {
+ "hp": 109,
+ "break": 50,
+ "atk": 85,
+ "m_def": 98,
+ "p_def": 66,
+ "regen": 74
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 482,
+ "stats": {
+ "hp": 109,
+ "break": 50,
+ "atk": 85,
+ "m_def": 98,
+ "p_def": 66,
+ "regen": 74
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 482,
+ "stats": {
+ "hp": 109,
+ "break": 50,
+ "atk": 85,
+ "m_def": 98,
+ "p_def": 66,
+ "regen": 74
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "015",
+ "name": "glacy",
+ "elements": [
+ "ice",
+ "water"
+ ],
+ "role": "heal",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "ice",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 535,
+ "stats": {
+ "hp": 120,
+ "break": 50,
+ "atk": 95,
+ "m_def": 110,
+ "p_def": 72,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "ice",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 535,
+ "stats": {
+ "hp": 120,
+ "break": 50,
+ "atk": 95,
+ "m_def": 110,
+ "p_def": 72,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 535,
+ "stats": {
+ "hp": 120,
+ "break": 50,
+ "atk": 95,
+ "m_def": 110,
+ "p_def": 72,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 4
+ },
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 4
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 535,
+ "stats": {
+ "hp": 120,
+ "break": 50,
+ "atk": 95,
+ "m_def": 110,
+ "p_def": 72,
+ "regen": 88
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "016",
+ "name": "leafy",
+ "elements": [
+ "grass",
+ "water"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass",
+ "water"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 2
+ },
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 535,
+ "stats": {
+ "hp": 120,
+ "break": 50,
+ "atk": 95,
+ "m_def": 82,
+ "p_def": 82,
+ "regen": 106
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "017",
+ "name": "nimbi",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 451,
+ "stats": {
+ "hp": 80,
+ "break": 43,
+ "atk": 84,
+ "m_def": 82,
+ "p_def": 69,
+ "regen": 93
+ }
+ }
+ },
+ {
+ "form": "cloudy_mist_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 451,
+ "stats": {
+ "hp": 80,
+ "break": 43,
+ "atk": 84,
+ "m_def": 82,
+ "p_def": 69,
+ "regen": 93
+ }
+ }
+ },
+ {
+ "form": "deluge_form",
+ "elements": [
+ "electric",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 451,
+ "stats": {
+ "hp": 80,
+ "break": 43,
+ "atk": 84,
+ "m_def": 82,
+ "p_def": 69,
+ "regen": 93
+ }
+ }
+ },
+ {
+ "form": "plateau_form",
+ "elements": [
+ "ice",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 451,
+ "stats": {
+ "hp": 80,
+ "break": 43,
+ "atk": 84,
+ "m_def": 82,
+ "p_def": 69,
+ "regen": 93
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": [
+ {
+ "name": "enhanced_jump",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "018",
+ "name": "turbo",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 518,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 110,
+ "m_def": 96,
+ "p_def": 80,
+ "regen": 87
+ }
+ }
+ },
+ {
+ "form": "cloudy_mist_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 518,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 110,
+ "m_def": 96,
+ "p_def": 80,
+ "regen": 87
+ }
+ }
+ },
+ {
+ "form": "deluge_form",
+ "elements": [
+ "electric",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 2
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 518,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 110,
+ "m_def": 96,
+ "p_def": 80,
+ "regen": 87
+ }
+ }
+ },
+ {
+ "form": "plateau_form",
+ "elements": [
+ "ice",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 518,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 110,
+ "m_def": 96,
+ "p_def": 80,
+ "regen": 87
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "dark",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 4
+ },
+ {
+ "type": "wind",
+ "level": 4
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 518,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 110,
+ "m_def": 96,
+ "p_def": 80,
+ "regen": 87
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "flight",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "019",
+ "name": "dreaple",
+ "elements": [
+ "dark"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 520,
+ "stats": {
+ "hp": 100,
+ "break": 50,
+ "atk": 110,
+ "m_def": 103,
+ "p_def": 77,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "020",
+ "name": "hummin",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 467,
+ "stats": {
+ "hp": 94,
+ "break": 80,
+ "atk": 68,
+ "m_def": 94,
+ "p_def": 71,
+ "regen": 60
+ }
+ }
+ },
+ {
+ "form": "mountain_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 467,
+ "stats": {
+ "hp": 94,
+ "break": 80,
+ "atk": 68,
+ "m_def": 94,
+ "p_def": 71,
+ "regen": 60
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "021",
+ "name": "witchin",
+ "elements": [
+ "dark",
+ "grass"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark",
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 2
+ },
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 2
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 542,
+ "stats": {
+ "hp": 115,
+ "break": 50,
+ "atk": 88,
+ "m_def": 90,
+ "p_def": 84,
+ "regen": 115
+ }
+ }
+ },
+ {
+ "form": "mountain_form",
+ "elements": [
+ "dark",
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 2
+ },
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 2
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 542,
+ "stats": {
+ "hp": 115,
+ "break": 50,
+ "atk": 88,
+ "m_def": 90,
+ "p_def": 84,
+ "regen": 115
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "dark",
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 4
+ },
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 4
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 542,
+ "stats": {
+ "hp": 115,
+ "break": 50,
+ "atk": 88,
+ "m_def": 90,
+ "p_def": 84,
+ "regen": 115
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "lighting",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "022",
+ "name": "tuckin",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 106,
+ "break": 100,
+ "atk": 80,
+ "m_def": 110,
+ "p_def": 83,
+ "regen": 71
+ }
+ }
+ },
+ {
+ "form": "mountain_form",
+ "elements": [
+ "grass",
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 2
+ },
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 106,
+ "break": 100,
+ "atk": 80,
+ "m_def": 110,
+ "p_def": 83,
+ "regen": 71
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "023",
+ "name": "budclaw",
+ "elements": [
+ "grass",
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass",
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 1
+ },
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 454,
+ "stats": {
+ "hp": 76,
+ "break": 84,
+ "atk": 64,
+ "m_def": 65,
+ "p_def": 93,
+ "regen": 72
+ }
+ }
+ },
+ {
+ "form": "bay_form",
+ "elements": [
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 454,
+ "stats": {
+ "hp": 76,
+ "break": 84,
+ "atk": 64,
+ "m_def": 65,
+ "p_def": 93,
+ "regen": 72
+ }
+ }
+ },
+ {
+ "form": "beach_form",
+ "elements": [
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 454,
+ "stats": {
+ "hp": 76,
+ "break": 84,
+ "atk": 64,
+ "m_def": 65,
+ "p_def": 93,
+ "regen": 72
+ }
+ }
+ },
+ {
+ "form": "mudflat_form",
+ "elements": [
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 454,
+ "stats": {
+ "hp": 76,
+ "break": 84,
+ "atk": 64,
+ "m_def": 65,
+ "p_def": 93,
+ "regen": 72
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "024",
+ "name": "shrubclaw",
+ "elements": [
+ "grass",
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass",
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 3
+ },
+ {
+ "type": "grass",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 95,
+ "break": 104,
+ "atk": 80,
+ "m_def": 77,
+ "p_def": 109,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "bay_form",
+ "elements": [
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 95,
+ "break": 104,
+ "atk": 80,
+ "m_def": 77,
+ "p_def": 109,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "beach_form",
+ "elements": [
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 95,
+ "break": 104,
+ "atk": 80,
+ "m_def": 77,
+ "p_def": 109,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "mudflat_form",
+ "elements": [
+ "rock"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 95,
+ "break": 104,
+ "atk": 80,
+ "m_def": 77,
+ "p_def": 109,
+ "regen": 85
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "025",
+ "name": "geoclaw",
+ "elements": [
+ "ice"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "ice"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "role": "break",
+ "attributes": {
+ "total": 550,
+ "stats": {
+ "hp": 93,
+ "break": 103,
+ "atk": 80,
+ "m_def": 79,
+ "p_def": 113,
+ "regen": 82
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "026",
+ "name": "sparki",
+ "elements": [
+ "fire"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 435,
+ "stats": {
+ "hp": 69,
+ "break": 41,
+ "atk": 100,
+ "m_def": 67,
+ "p_def": 60,
+ "regen": 98
+ }
+ }
+ },
+ {
+ "form": "forest_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 435,
+ "stats": {
+ "hp": 69,
+ "break": 41,
+ "atk": 100,
+ "m_def": 67,
+ "p_def": 60,
+ "regen": 98
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 435,
+ "stats": {
+ "hp": 69,
+ "break": 41,
+ "atk": 100,
+ "m_def": 67,
+ "p_def": 60,
+ "regen": 98
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 435,
+ "stats": {
+ "hp": 69,
+ "break": 41,
+ "atk": 100,
+ "m_def": 67,
+ "p_def": 60,
+ "regen": 98
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "027",
+ "name": "flamerion",
+ "elements": [
+ "fire"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 113,
+ "m_def": 89,
+ "p_def": 70,
+ "regen": 115
+ }
+ }
+ },
+ {
+ "form": "forest_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 113,
+ "m_def": 89,
+ "p_def": 70,
+ "regen": 115
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 113,
+ "m_def": 89,
+ "p_def": 70,
+ "regen": 115
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ }
+ ],
+ "role": "regen",
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 80,
+ "break": 50,
+ "atk": 113,
+ "m_def": 89,
+ "p_def": 70,
+ "regen": 115
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "028",
+ "name": "flutternym",
+ "elements": [
+ "wind"
+ ],
+ "role": "heal",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 437,
+ "stats": {
+ "hp": 94,
+ "break": 41,
+ "atk": 81,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 437,
+ "stats": {
+ "hp": 94,
+ "break": 41,
+ "atk": 81,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "rock",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 437,
+ "stats": {
+ "hp": 94,
+ "break": 41,
+ "atk": 81,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "dark",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 437,
+ "stats": {
+ "hp": 94,
+ "break": 41,
+ "atk": 81,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": [
+ {
+ "name": "stealth",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "029",
+ "name": "gracewing",
+ "elements": [
+ "wind"
+ ],
+ "role": "heal",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 532,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 98,
+ "m_def": 85,
+ "p_def": 85,
+ "regen": 104
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "grass",
+ "level": 2
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 532,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 98,
+ "m_def": 85,
+ "p_def": 85,
+ "regen": 104
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "rock",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 2
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 532,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 98,
+ "m_def": 85,
+ "p_def": 85,
+ "regen": 104
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "dark",
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 2
+ }
+ ],
+ "role": "heal",
+ "attributes": {
+ "total": 532,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 98,
+ "m_def": 85,
+ "p_def": 85,
+ "regen": 104
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "stealth",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "030",
+ "name": "somniwing",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "prismana_form",
+ "elements": [
+ "grass",
+ "wind"
+ ],
+ "role": "regen",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 4
+ },
+ {
+ "type": "grass",
+ "level": 4
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 538,
+ "stats": {
+ "hp": 111,
+ "break": 50,
+ "atk": 92,
+ "m_def": 85,
+ "p_def": 85,
+ "regen": 115
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "031",
+ "name": "eko",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "role": "support",
+ "attributes": {
+ "total": 427,
+ "stats": {
+ "hp": 70,
+ "break": 42,
+ "atk": 108,
+ "m_def": 57,
+ "p_def": 70,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "032",
+ "name": "eklue",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 510,
+ "stats": {
+ "hp": 100,
+ "break": 49,
+ "atk": 120,
+ "m_def": 67,
+ "p_def": 83,
+ "regen": 91
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "033",
+ "name": "budsquire",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 428,
+ "stats": {
+ "hp": 82,
+ "break": 43,
+ "atk": 107,
+ "m_def": 59,
+ "p_def": 59,
+ "regen": 78
+ }
+ }
+ },
+ {
+ "form": "rosetower_woods_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 428,
+ "stats": {
+ "hp": 82,
+ "break": 43,
+ "atk": 107,
+ "m_def": 59,
+ "p_def": 59,
+ "regen": 78
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "034",
+ "name": "thornblade",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 53,
+ "atk": 121,
+ "m_def": 73,
+ "p_def": 74,
+ "regen": 92
+ }
+ }
+ },
+ {
+ "form": "rosetower_woods_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 53,
+ "atk": 121,
+ "m_def": 73,
+ "p_def": 74,
+ "regen": 92
+ }
+ }
+ },
+ {
+ "form": "thunderstorm_form",
+ "elements": [
+ "electric",
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 2
+ },
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 53,
+ "atk": 121,
+ "m_def": 73,
+ "p_def": 74,
+ "regen": 92
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "grass",
+ "water"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "grass",
+ "level": 4
+ },
+ {
+ "type": "crafting",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 96,
+ "break": 53,
+ "atk": 121,
+ "m_def": 73,
+ "p_def": 74,
+ "regen": 92
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "035",
+ "name": "melloblum",
+ "elements": [
+ "grass"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 95,
+ "break": 91,
+ "atk": 113,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 78
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "grass"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "grass",
+ "level": 4
+ },
+ {
+ "type": "crafting",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 95,
+ "break": 91,
+ "atk": 113,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 78
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "036",
+ "name": "pomegg",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 455,
+ "stats": {
+ "hp": 74,
+ "break": 91,
+ "atk": 72,
+ "m_def": 65,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 455,
+ "stats": {
+ "hp": 74,
+ "break": 91,
+ "atk": 72,
+ "m_def": 65,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 455,
+ "stats": {
+ "hp": 74,
+ "break": 91,
+ "atk": 72,
+ "m_def": 65,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 455,
+ "stats": {
+ "hp": 74,
+ "break": 91,
+ "atk": 72,
+ "m_def": 65,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "037",
+ "name": "pomawk",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 87,
+ "break": 107,
+ "atk": 90,
+ "m_def": 76,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ },
+ {
+ "form": "sea_of_flowers_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 87,
+ "break": 107,
+ "atk": 90,
+ "m_def": 76,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 87,
+ "break": 107,
+ "atk": 90,
+ "m_def": 76,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "grass"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 87,
+ "break": 107,
+ "atk": 90,
+ "m_def": 76,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "038",
+ "name": "dewy",
+ "elements": [
+ "dark"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "perfumery",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ },
+ {
+ "type": "leisure",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 441,
+ "stats": {
+ "hp": 71,
+ "break": 69,
+ "atk": 94,
+ "m_def": 55,
+ "p_def": 58,
+ "regen": 94
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "039",
+ "name": "fragrancier",
+ "elements": [
+ "dark"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "perfumery",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 519,
+ "stats": {
+ "hp": 84,
+ "break": 81,
+ "atk": 111,
+ "m_def": 65,
+ "p_def": 68,
+ "regen": 110
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "flight",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "040",
+ "name": "wisptis",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 423,
+ "stats": {
+ "hp": 77,
+ "break": 43,
+ "atk": 112,
+ "m_def": 54,
+ "p_def": 60,
+ "regen": 77
+ }
+ }
+ },
+ {
+ "form": "forest_form",
+ "elements": [
+ "dark",
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 1
+ },
+ {
+ "type": "grass",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 423,
+ "stats": {
+ "hp": 77,
+ "break": 43,
+ "atk": 112,
+ "m_def": 54,
+ "p_def": 60,
+ "regen": 77
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 1
+ },
+ {
+ "type": "fire",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 423,
+ "stats": {
+ "hp": 77,
+ "break": 43,
+ "atk": 112,
+ "m_def": 54,
+ "p_def": 60,
+ "regen": 77
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": [
+ {
+ "name": "stealth",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "041",
+ "name": "ignitis",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 77,
+ "regen": 91
+ }
+ }
+ },
+ {
+ "form": "forest_form",
+ "elements": [
+ "dark",
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 3
+ },
+ {
+ "type": "grass",
+ "level": 2
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 77,
+ "regen": 91
+ }
+ }
+ },
+ {
+ "form": "highlands",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 3
+ },
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 77,
+ "regen": 91
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "crafting",
+ "level": 4
+ },
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 77,
+ "regen": 91
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "stealth",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "042",
+ "name": "bonesky",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 377,
+ "stats": {
+ "hp": 71,
+ "break": 38,
+ "atk": 88,
+ "m_def": 57,
+ "p_def": 56,
+ "regen": 67
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "ice",
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 377,
+ "stats": {
+ "hp": 71,
+ "break": 38,
+ "atk": 88,
+ "m_def": 57,
+ "p_def": 56,
+ "regen": 67
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "043",
+ "name": "fenrier",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 453,
+ "stats": {
+ "hp": 86,
+ "break": 45,
+ "atk": 105,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 81
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 453,
+ "stats": {
+ "hp": 86,
+ "break": 45,
+ "atk": 105,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 81
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "044",
+ "name": "glynsera",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "ice"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 512,
+ "stats": {
+ "hp": 95,
+ "break": 53,
+ "atk": 118,
+ "m_def": 76,
+ "p_def": 72,
+ "regen": 98
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "ice",
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 512,
+ "stats": {
+ "hp": 95,
+ "break": 53,
+ "atk": 118,
+ "m_def": 76,
+ "p_def": 72,
+ "regen": 98
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "ice"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "holy",
+ "level": 4
+ },
+ {
+ "type": "ice",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 512,
+ "stats": {
+ "hp": 95,
+ "break": 53,
+ "atk": 118,
+ "m_def": 76,
+ "p_def": 72,
+ "regen": 98
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "045",
+ "name": "bolty",
+ "elements": [
+ "electric"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 450,
+ "stats": {
+ "hp": 85,
+ "break": 87,
+ "atk": 85,
+ "m_def": 60,
+ "p_def": 60,
+ "regen": 73
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 450,
+ "stats": {
+ "hp": 85,
+ "break": 87,
+ "atk": 85,
+ "m_def": 60,
+ "p_def": 60,
+ "regen": 73
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "climb",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "enhanced_jump",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "046",
+ "name": "blazen",
+ "elements": [
+ "electric"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 530,
+ "stats": {
+ "hp": 100,
+ "break": 104,
+ "atk": 100,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 86
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 530,
+ "stats": {
+ "hp": 100,
+ "break": 104,
+ "atk": 100,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 86
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "dark",
+ "electric"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 4
+ },
+ {
+ "type": "crafting",
+ "level": 4
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 530,
+ "stats": {
+ "hp": 100,
+ "break": 104,
+ "atk": 100,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 86
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "climb",
+ "level": 3
+ }
+ ],
+ "mobility": [
+ {
+ "name": "enhanced_jump",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "047",
+ "name": "squarrel",
+ "elements": [
+ "fire"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 455,
+ "stats": {
+ "hp": 53,
+ "break": 94,
+ "atk": 80,
+ "m_def": 72,
+ "p_def": 79,
+ "regen": 77
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "climb",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "048",
+ "name": "squashel",
+ "elements": [
+ "fire"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "crafting",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 548,
+ "stats": {
+ "hp": 110,
+ "break": 108,
+ "atk": 82,
+ "m_def": 80,
+ "p_def": 80,
+ "regen": 88
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "climb",
+ "level": 2
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "049",
+ "name": "susuta",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 411,
+ "stats": {
+ "hp": 81,
+ "break": 72,
+ "atk": 61,
+ "m_def": 80,
+ "p_def": 53,
+ "regen": 64
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 411,
+ "stats": {
+ "hp": 81,
+ "break": 72,
+ "atk": 61,
+ "m_def": 80,
+ "p_def": 53,
+ "regen": 64
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "050",
+ "name": "popota",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 471,
+ "stats": {
+ "hp": 92,
+ "break": 80,
+ "atk": 96,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 67
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 471,
+ "stats": {
+ "hp": 92,
+ "break": 80,
+ "atk": 96,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 67
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "051",
+ "name": "piopiota",
+ "elements": [
+ "water"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 522,
+ "stats": {
+ "hp": 102,
+ "break": 50,
+ "atk": 108,
+ "m_def": 75,
+ "p_def": 99,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "dark",
+ "water"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 522,
+ "stats": {
+ "hp": 102,
+ "break": 50,
+ "atk": 108,
+ "m_def": 75,
+ "p_def": 99,
+ "regen": 88
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 3
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "052",
+ "name": "panpanta",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 545,
+ "stats": {
+ "hp": 104,
+ "break": 100,
+ "atk": 85,
+ "m_def": 106,
+ "p_def": 70,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "nighttime_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 545,
+ "stats": {
+ "hp": 104,
+ "break": 100,
+ "atk": 85,
+ "m_def": 106,
+ "p_def": 70,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "water"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 4
+ },
+ {
+ "type": "water",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 545,
+ "stats": {
+ "hp": 104,
+ "break": 100,
+ "atk": 85,
+ "m_def": 106,
+ "p_def": 70,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "swim",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "053",
+ "name": "shelly",
+ "elements": [
+ "water"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 1
+ },
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 382,
+ "stats": {
+ "hp": 75,
+ "break": 39,
+ "atk": 90,
+ "m_def": 56,
+ "p_def": 56,
+ "regen": 66
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "054",
+ "name": "sheldon",
+ "elements": [
+ "water"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "leisure",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 458,
+ "stats": {
+ "hp": 90,
+ "break": 44,
+ "atk": 109,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 79
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "055",
+ "name": "sherro",
+ "elements": [
+ "water"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 100,
+ "break": 50,
+ "atk": 121,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "thunderstorm_form",
+ "elements": [
+ "electric",
+ "water"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 2
+ },
+ {
+ "type": "water",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 100,
+ "break": 50,
+ "atk": 121,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 88
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "water"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 4
+ },
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "leisure",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 509,
+ "stats": {
+ "hp": 100,
+ "break": 50,
+ "atk": 121,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 88
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "056",
+ "name": "baleetle",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 437,
+ "stats": {
+ "hp": 76,
+ "break": 42,
+ "atk": 98,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 437,
+ "stats": {
+ "hp": 76,
+ "break": 42,
+ "atk": 98,
+ "m_def": 68,
+ "p_def": 68,
+ "regen": 85
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "057",
+ "name": "waleetle",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 516,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 114,
+ "m_def": 80,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "rock",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 516,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 114,
+ "m_def": 80,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "dark",
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 4
+ },
+ {
+ "type": "carry",
+ "level": 4
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 516,
+ "stats": {
+ "hp": 90,
+ "break": 52,
+ "atk": 114,
+ "m_def": 80,
+ "p_def": 80,
+ "regen": 100
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "058",
+ "name": "bouldus",
+ "elements": [
+ "rock"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 516,
+ "stats": {
+ "hp": 91,
+ "break": 52,
+ "atk": 114,
+ "m_def": 54,
+ "p_def": 101,
+ "regen": 104
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "rock"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "rock",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 516,
+ "stats": {
+ "hp": 91,
+ "break": 52,
+ "atk": 114,
+ "m_def": 54,
+ "p_def": 101,
+ "regen": 104
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "059",
+ "name": "fentuft",
+ "elements": [
+ "electric"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 429,
+ "stats": {
+ "hp": 85,
+ "break": 41,
+ "atk": 106,
+ "m_def": 60,
+ "p_def": 60,
+ "regen": 77
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": [
+ {
+ "name": "dash",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "060",
+ "name": "fenmane",
+ "elements": [
+ "electric"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 100,
+ "break": 50,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "electric"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 4
+ },
+ {
+ "type": "crafting",
+ "level": 4
+ },
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 100,
+ "break": 50,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 70,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 3
+ }
+ ],
+ "mobility": [
+ {
+ "name": "dash",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "061",
+ "name": "helmut",
+ "elements": [
+ "dark"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 339,
+ "stats": {
+ "hp": 60,
+ "break": 60,
+ "atk": 52,
+ "m_def": 48,
+ "p_def": 68,
+ "regen": 51
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 339,
+ "stats": {
+ "hp": 60,
+ "break": 60,
+ "atk": 52,
+ "m_def": 48,
+ "p_def": 68,
+ "regen": 51
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 1
+ },
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "dark",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 339,
+ "stats": {
+ "hp": 60,
+ "break": 60,
+ "atk": 52,
+ "m_def": 48,
+ "p_def": 68,
+ "regen": 51
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "vision",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "062",
+ "name": "pawney",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 55,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 84,
+ "regen": 81
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 55,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 84,
+ "regen": 81
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 55,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 84,
+ "regen": 81
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 4
+ },
+ {
+ "type": "ice",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 90,
+ "break": 55,
+ "atk": 125,
+ "m_def": 70,
+ "p_def": 84,
+ "regen": 81
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "grappling_hook",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "063",
+ "name": "rookey",
+ "elements": [
+ "dark"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 100,
+ "break": 105,
+ "atk": 90,
+ "m_def": 70,
+ "p_def": 100,
+ "regen": 75
+ }
+ }
+ },
+ {
+ "form": "mountain_woods_form",
+ "elements": [
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 100,
+ "break": 105,
+ "atk": 90,
+ "m_def": 70,
+ "p_def": 100,
+ "regen": 75
+ }
+ }
+ },
+ {
+ "form": "snowfield_form",
+ "elements": [
+ "ice",
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "ice",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 100,
+ "break": 105,
+ "atk": 90,
+ "m_def": 70,
+ "p_def": 100,
+ "regen": 75
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "vision",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "064",
+ "name": "jawling",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 407,
+ "stats": {
+ "hp": 78,
+ "break": 76,
+ "atk": 65,
+ "m_def": 51,
+ "p_def": 74,
+ "regen": 63
+ }
+ }
+ },
+ {
+ "form": "mountain_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "wind",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 407,
+ "stats": {
+ "hp": 78,
+ "break": 76,
+ "atk": 65,
+ "m_def": 51,
+ "p_def": 74,
+ "regen": 63
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "vision",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "065",
+ "name": "helmwhelp",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 2
+ },
+ {
+ "type": "wind",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 489,
+ "stats": {
+ "hp": 94,
+ "break": 92,
+ "atk": 78,
+ "m_def": 61,
+ "p_def": 88,
+ "regen": 76
+ }
+ }
+ },
+ {
+ "form": "mountain_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 2
+ },
+ {
+ "type": "wind",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 489,
+ "stats": {
+ "hp": 94,
+ "break": 92,
+ "atk": 78,
+ "m_def": 61,
+ "p_def": 88,
+ "regen": 76
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "vision",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "066",
+ "name": "helgon",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 543,
+ "stats": {
+ "hp": 104,
+ "break": 102,
+ "atk": 87,
+ "m_def": 68,
+ "p_def": 98,
+ "regen": 84
+ }
+ }
+ },
+ {
+ "form": "mountain_form",
+ "elements": [
+ "wind"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 543,
+ "stats": {
+ "hp": 104,
+ "break": 102,
+ "atk": 87,
+ "m_def": 68,
+ "p_def": 98,
+ "regen": 84
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "vision",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "067",
+ "name": "infergon",
+ "elements": [
+ "fire"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 125,
+ "m_def": 85,
+ "p_def": 70,
+ "regen": 80
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "fire",
+ "wind"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 4
+ },
+ {
+ "type": "fire",
+ "level": 4
+ },
+ {
+ "type": "wind",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 505,
+ "stats": {
+ "hp": 95,
+ "break": 50,
+ "atk": 125,
+ "m_def": 85,
+ "p_def": 70,
+ "regen": 80
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "vision",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "068",
+ "name": "cubbo",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 435,
+ "stats": {
+ "hp": 92,
+ "break": 42,
+ "atk": 100,
+ "m_def": 61,
+ "p_def": 70,
+ "regen": 70
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "obstacle_destruction",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "069",
+ "name": "grizbo",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 506,
+ "stats": {
+ "hp": 104,
+ "break": 50,
+ "atk": 124,
+ "m_def": 70,
+ "p_def": 83,
+ "regen": 75
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "dark",
+ "rock"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 4
+ },
+ {
+ "type": "rock",
+ "level": 4
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 506,
+ "stats": {
+ "hp": 104,
+ "break": 50,
+ "atk": 124,
+ "m_def": 70,
+ "p_def": 83,
+ "regen": 75
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "obstacle_destruction",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "070",
+ "name": "pebbling",
+ "elements": [
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 412,
+ "stats": {
+ "hp": 83,
+ "break": 79,
+ "atk": 68,
+ "m_def": 74,
+ "p_def": 52,
+ "regen": 56
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "071",
+ "name": "lavazar",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 1
+ },
+ {
+ "type": "fire",
+ "level": 2
+ },
+ {
+ "type": "carry",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 485,
+ "stats": {
+ "hp": 99,
+ "break": 93,
+ "atk": 82,
+ "m_def": 80,
+ "p_def": 63,
+ "regen": 68
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "072",
+ "name": "magmarex",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire",
+ "rock"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "rock",
+ "level": 2
+ },
+ {
+ "type": "fire",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 539,
+ "stats": {
+ "hp": 110,
+ "break": 104,
+ "atk": 91,
+ "m_def": 89,
+ "p_def": 70,
+ "regen": 75
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "fire",
+ "dark"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 4
+ },
+ {
+ "type": "fire",
+ "level": 4
+ },
+ {
+ "type": "dark",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 539,
+ "stats": {
+ "hp": 110,
+ "break": 104,
+ "atk": 91,
+ "m_def": 89,
+ "p_def": 70,
+ "regen": 75
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "obstacle_destruction",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "073",
+ "name": "geodeback",
+ "elements": [
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 2
+ },
+ {
+ "type": "rock",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 486,
+ "stats": {
+ "hp": 99,
+ "break": 102,
+ "atk": 81,
+ "m_def": 54,
+ "p_def": 80,
+ "regen": 70
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "074",
+ "name": "minespine",
+ "elements": [
+ "rock"
+ ],
+ "role": "break",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "break",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 110,
+ "break": 105,
+ "atk": 90,
+ "m_def": 60,
+ "p_def": 97,
+ "regen": 78
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "dig",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "075",
+ "name": "cozite",
+ "elements": [
+ "rock"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 1
+ },
+ {
+ "type": "rock",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 448,
+ "stats": {
+ "hp": 77,
+ "break": 50,
+ "atk": 87,
+ "m_def": 67,
+ "p_def": 67,
+ "regen": 100
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "076",
+ "name": "bailite",
+ "elements": [
+ "rock"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "rock"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "carry",
+ "level": 3
+ },
+ {
+ "type": "rock",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 514,
+ "stats": {
+ "hp": 90,
+ "break": 50,
+ "atk": 116,
+ "m_def": 83,
+ "p_def": 100,
+ "regen": 75
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "077",
+ "name": "bulbly",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 1
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 404,
+ "stats": {
+ "hp": 83,
+ "break": 37,
+ "atk": 68,
+ "m_def": 63,
+ "p_def": 63,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "lighting",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "078",
+ "name": "veilfloat",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 2
+ },
+ {
+ "type": "crafting",
+ "level": 2
+ }
+ ],
+ "attributes": {
+ "total": 473,
+ "stats": {
+ "hp": 99,
+ "break": 45,
+ "atk": 93,
+ "m_def": 74,
+ "p_def": 72,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "lighting",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "079",
+ "name": "luminelle",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 515,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 115,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "deluge_form",
+ "elements": [
+ "electric",
+ "water"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "water",
+ "level": 2
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 515,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 115,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 90
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "electric"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 4
+ },
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 515,
+ "stats": {
+ "hp": 110,
+ "break": 50,
+ "atk": 115,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "lighting",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "080",
+ "name": "fahloo",
+ "elements": [
+ "water"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "regen",
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 459,
+ "stats": {
+ "hp": 102,
+ "break": 44,
+ "atk": 76,
+ "m_def": 76,
+ "p_def": 68,
+ "regen": 93
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 1
+ },
+ {
+ "name": "swim",
+ "level": 1
+ }
+ ],
+ "mobility": [
+ {
+ "name": "flight",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "081",
+ "name": "erlath",
+ "elements": [
+ "water"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "water"
+ ],
+ "role": "regen",
+ "homeland_abilities": [
+ {
+ "type": "water",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 120,
+ "break": 50,
+ "atk": 90,
+ "m_def": 90,
+ "p_def": 80,
+ "regen": 110
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 3
+ },
+ {
+ "name": "swim",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "flight",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "082",
+ "name": "besauce",
+ "elements": [
+ "electric"
+ ],
+ "role": "regen",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "regen",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 1
+ }
+ ],
+ "attributes": {
+ "total": 538,
+ "stats": {
+ "hp": 88,
+ "break": 90,
+ "atk": 92,
+ "m_def": 75,
+ "p_def": 75,
+ "regen": 118
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "10001",
+ "name": "irisalis",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "prismana_form",
+ "elements": [
+ "grass"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "grass",
+ "level": 4
+ },
+ {
+ "type": "leisure",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 540,
+ "stats": {
+ "hp": 90,
+ "break": 56,
+ "atk": 130,
+ "m_def": 78,
+ "p_def": 78,
+ "regen": 108
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 3
+ }
+ ],
+ "mobility": []
+ }
+ },
+ {
+ "id": "10002",
+ "name": "dazmand",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 522,
+ "stats": {
+ "hp": 100,
+ "break": 55,
+ "atk": 108,
+ "m_def": 90,
+ "p_def": 81,
+ "regen": 88
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "10003",
+ "name": "fulmintis",
+ "elements": [
+ "electric"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "electric"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 520,
+ "stats": {
+ "hp": 99,
+ "break": 50,
+ "atk": 130,
+ "m_def": 70,
+ "p_def": 66,
+ "regen": 105
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "holy",
+ "electric"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "electric",
+ "level": 4
+ },
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "crafting",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 520,
+ "stats": {
+ "hp": 99,
+ "break": 50,
+ "atk": 130,
+ "m_def": 70,
+ "p_def": 66,
+ "regen": 105
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [
+ {
+ "name": "fly",
+ "level": 2
+ }
+ ],
+ "mobility": [
+ {
+ "name": "stealth",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "11001",
+ "name": "little_fire_spirit",
+ "elements": [
+ "fire"
+ ],
+ "role": "support",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "fire"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 107,
+ "break": 50,
+ "atk": 113,
+ "m_def": 88,
+ "p_def": 60,
+ "regen": 99
+ }
+ }
+ },
+ {
+ "form": "prismana_form",
+ "elements": [
+ "fire"
+ ],
+ "role": "support",
+ "homeland_abilities": [
+ {
+ "type": "fire",
+ "level": 4
+ }
+ ],
+ "attributes": {
+ "total": 517,
+ "stats": {
+ "hp": 107,
+ "break": 50,
+ "atk": 113,
+ "m_def": 88,
+ "p_def": 60,
+ "regen": 99
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": []
+ }
+ },
+ {
+ "id": "99996",
+ "name": "lunara",
+ "elements": [
+ "holy"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "holy"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 514,
+ "stats": {
+ "hp": 100,
+ "break": 64,
+ "atk": 116,
+ "m_def": 72,
+ "p_def": 72,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ },
+ {
+ "id": "99998",
+ "name": "helion",
+ "elements": [
+ "holy"
+ ],
+ "role": "dps",
+ "variations": [
+ {
+ "form": "basic_form",
+ "elements": [
+ "holy"
+ ],
+ "role": "dps",
+ "homeland_abilities": [
+ {
+ "type": "holy",
+ "level": 3
+ },
+ {
+ "type": "carry",
+ "level": 3
+ }
+ ],
+ "attributes": {
+ "total": 514,
+ "stats": {
+ "hp": 100,
+ "break": 64,
+ "atk": 116,
+ "m_def": 72,
+ "p_def": 72,
+ "regen": 90
+ }
+ }
+ }
+ ],
+ "exploration": {
+ "pathfinding": [],
+ "mobility": [
+ {
+ "name": "sprint",
+ "level": null
+ }
+ ]
+ }
+ }
+]
diff --git a/website/public/data/aniimo/i18n/en.json b/website/public/data/aniimo/i18n/en.json
new file mode 100644
index 0000000..c820672
--- /dev/null
+++ b/website/public/data/aniimo/i18n/en.json
@@ -0,0 +1,140 @@
+{
+ "bailite": "Bailite",
+ "baleetle": "Baleetle",
+ "basic_form": "Basic Form",
+ "bay_form": "Bay Form",
+ "beach_form": "Beach Form",
+ "besauce": "Besauce",
+ "blazen": "Blazen",
+ "bolty": "Bolty",
+ "bonesky": "Bonesky",
+ "bouldus": "Bouldus",
+ "break": "BREAK",
+ "budclaw": "Budclaw",
+ "budsquire": "Budsquire",
+ "bulbly": "Bulbly",
+ "carry": "Carry",
+ "celestis": "Celestis",
+ "chirpi": "Chirpi",
+ "climb": "Climb",
+ "cloudy_mist_form": "Cloudy Mist Form",
+ "cornet": "Cornet",
+ "cozite": "Cozite",
+ "crafting": "Crafting",
+ "cubbo": "Cubbo",
+ "dark": "dark",
+ "dash": "Dash",
+ "dazmand": "Dazmand",
+ "deluge_form": "Deluge Form",
+ "dewy": "Dewy",
+ "dig": "Dig",
+ "dps": "DPS",
+ "dreaple": "Dreaple",
+ "eklue": "Eklue",
+ "eko": "Eko",
+ "electric": "electric",
+ "emberpup": "Emberpup",
+ "enhanced_jump": "Enhanced Jump",
+ "erlath": "Erlath",
+ "fahloo": "Fahloo",
+ "fenmane": "Fenmane",
+ "fenrier": "Fenrier",
+ "fentuft": "Fentuft",
+ "fire": "fire",
+ "flamerion": "Flamerion",
+ "flameruff": "Flameruff",
+ "flight": "Flight",
+ "flutternym": "Flutternym",
+ "fly": "Fly",
+ "forest_form": "Forest Form",
+ "fragrancier": "Fragrancier",
+ "fulmintis": "Fulmintis",
+ "geoclaw": "Geoclaw",
+ "geodeback": "Geodeback",
+ "glacy": "Glacy",
+ "glynsera": "Glynsera",
+ "gracewing": "Gracewing",
+ "grappling_hook": "Grappling Hook",
+ "grass": "grass",
+ "grassland_form": "Grassland Form",
+ "grizbo": "Grizbo",
+ "heal": "Heal",
+ "helgon": "Helgon",
+ "helion": "Helion",
+ "helmut": "Helmut",
+ "helmwhelp": "Helmwhelp",
+ "highlands": "Highlands",
+ "holy": "holy",
+ "hummin": "Hummin",
+ "ice": "ice",
+ "ignitis": "Ignitis",
+ "infergon": "Infergon",
+ "inferlupa": "Inferlupa",
+ "iris": "Iris",
+ "irisal": "Irisal",
+ "irisalis": "Irisalis",
+ "jawling": "Jawling",
+ "lavazar": "Lavazar",
+ "leafy": "Leafy",
+ "leisure": "Leisure",
+ "lighting": "Lighting",
+ "little_fire_spirit": "Little Fire Spirit",
+ "luminelle": "Luminelle",
+ "lunara": "Lunara",
+ "magmarex": "Magmarex",
+ "melloblum": "Melloblum",
+ "minespine": "Minespine",
+ "mountain_form": "Mountain Form",
+ "mountain_woods_form": "Mountain Woods Form",
+ "mudflat_form": "Mudflat Form",
+ "nighttime_form": "Nighttime Form",
+ "nimbi": "Nimbi",
+ "obstacle_destruction": "Obstacle Destruction",
+ "panpanta": "Panpanta",
+ "pawney": "Pawney",
+ "pebbling": "Pebbling",
+ "perfumery": "Perfumery",
+ "piopiota": "Piopiota",
+ "plateau_form": "Plateau Form",
+ "pomawk": "Pomawk",
+ "pomegg": "Pomegg",
+ "popota": "Popota",
+ "pranky": "Pranky",
+ "prismana_form": "Prismana Form",
+ "regen": "REGEN",
+ "rock": "rock",
+ "rookey": "Rookey",
+ "rosetower_woods_form": "Rosetower Woods Form",
+ "scorchhowl": "Scorchhowl",
+ "sea_of_flowers_form": "Sea of Flowers Form",
+ "sheldon": "Sheldon",
+ "shelly": "Shelly",
+ "sherro": "Sherro",
+ "shrubclaw": "Shrubclaw",
+ "skippy": "Skippy",
+ "snowfield_form": "Snowfield Form",
+ "somniwing": "Somniwing",
+ "sparki": "Sparki",
+ "sprint": "Sprint",
+ "squarrel": "Squarrel",
+ "squashel": "Squashel",
+ "stealth": "Stealth",
+ "stellarys": "Stellarys",
+ "support": "Support",
+ "susuta": "Susuta",
+ "swim": "Swim",
+ "terrain_interaction": "Terrain Interaction",
+ "thornblade": "Thornblade",
+ "thunderstorm_form": "Thunderstorm Form",
+ "tromber": "Tromber",
+ "tubster": "Tubster",
+ "tuckin": "Tuckin",
+ "turbo": "Turbo",
+ "veilfloat": "Veilfloat",
+ "vision": "Vision",
+ "waleetle": "Waleetle",
+ "water": "water",
+ "wind": "wind",
+ "wisptis": "Wisptis",
+ "witchin": "Witchin"
+}
\ No newline at end of file
diff --git a/website/public/data/aniimo/i18n/fr.json b/website/public/data/aniimo/i18n/fr.json
new file mode 100644
index 0000000..f33fc82
--- /dev/null
+++ b/website/public/data/aniimo/i18n/fr.json
@@ -0,0 +1,140 @@
+{
+ "bailite": "Bastiroc",
+ "baleetle": "Balabée",
+ "basic_form": "Forme de base",
+ "bay_form": "Forme des baies",
+ "beach_form": "Forme des plages",
+ "besauce": "Pulsato",
+ "blazen": "Voltignasse",
+ "bolty": "Voltige",
+ "bonesky": "Osky",
+ "bouldus": "Boulabée",
+ "break": "RUPT.",
+ "budclaw": "Crabourgeon",
+ "budsquire": "Bourgeonnet",
+ "bulbly": "Bulbi",
+ "carry": "Porter",
+ "celestis": "Célestia",
+ "chirpi": "Cuicui",
+ "climb": "Grimper",
+ "cloudy_mist_form": "Forme des brumes nuageuses",
+ "cornet": "Clairbec",
+ "cozite": "Cabaroc",
+ "crafting": "Artisanat",
+ "cubbo": "Ourso",
+ "dark": "Ténèbres",
+ "dash": "Dash",
+ "dazmand": "Danzard",
+ "deluge_form": "Forme des déluges",
+ "dewy": "Plumiel",
+ "dig": "Creuser",
+ "dps": "DPS",
+ "dreaple": "Lainirique",
+ "eklue": "Ekolombo",
+ "eko": "Eko",
+ "electric": "Électrique",
+ "emberpup": "Loufeuteau",
+ "enhanced_jump": "Saut amélioré",
+ "erlath": "Fabulle",
+ "fahloo": "Falou",
+ "fenmane": "Fulgucrin",
+ "fenrier": "Glaivrier",
+ "fentuft": "Touffu",
+ "fire": "feu",
+ "flamerion": "Flamirage",
+ "flameruff": "Jappardent",
+ "flight": "Vol",
+ "flutternym": "Fluoretti",
+ "fly": "Voler",
+ "forest_form": "Forme des forêts",
+ "fragrancier": "Fragrancier",
+ "fulmintis": "Fulmi",
+ "geoclaw": "Cristocrabe",
+ "geodeback": "Géodosaure",
+ "glacy": "Givrotus",
+ "glynsera": "Molosabre",
+ "gracewing": "Voletti",
+ "grappling_hook": "Grappin",
+ "grass": "Herbe",
+ "grassland_form": "Forme des prairies",
+ "grizbo": "Grizzo",
+ "heal": "SOIN",
+ "helgon": "Cuiragon",
+ "helion": "Hélion",
+ "helmut": "Casquimou",
+ "helmwhelp": "Casquimord",
+ "highlands": "Hautes-plaines",
+ "holy": "Sacré",
+ "hummin": "Fredon",
+ "ice": "glace",
+ "ignitis": "Igniti",
+ "infergon": "Infergon",
+ "inferlupa": "Inferlupin",
+ "iris": "Iris",
+ "irisal": "Valsiris",
+ "irisalis": "Lumiris",
+ "jawling": "Machou",
+ "lavazar": "Lavasaure",
+ "leafy": "Voilotus",
+ "leisure": "Loisir",
+ "lighting": "Éclairage",
+ "little_fire_spirit": "Petit Esprit de Feu",
+ "luminelle": "Luminelle",
+ "lunara": "Lunara",
+ "magmarex": "Magmarex",
+ "melloblum": "Méliflore",
+ "minespine": "Cristorex",
+ "mountain_form": "Forme des montagnes",
+ "mountain_woods_form": "Forme des montagnes boisées",
+ "mudflat_form": "Forme des vasières",
+ "nighttime_form": "Nuit",
+ "nimbi": "Cumulaine",
+ "obstacle_destruction": "Destruction d'obstacles",
+ "panpanta": "Terriloutre",
+ "pawney": "Casquipreux",
+ "pebbling": "Silexodon",
+ "perfumery": "Parfumerie",
+ "piopiota": "Ondeloutre",
+ "plateau_form": "Forme des plateaux",
+ "pomawk": "Pomawk",
+ "pomegg": "Pomœuf",
+ "popota": "Bulloutre",
+ "pranky": "Filoutus",
+ "prismana_form": "Forme prismana",
+ "regen": "RÉGÉN.",
+ "rock": "Roche",
+ "rookey": "Casquillon",
+ "rosetower_woods_form": "Forme de tourbois",
+ "scorchhowl": "Hurlebrasier",
+ "sea_of_flowers_form": "Mer florale",
+ "sheldon": "Astor",
+ "shelly": "Astrid",
+ "sherro": "Astrophel",
+ "shrubclaw": "Crabiflore",
+ "skippy": "Tilotus",
+ "snowfield_form": "Forme des neiges",
+ "somniwing": "Vitti",
+ "sparki": "Farfafeu",
+ "sprint": "Sprint",
+ "squarrel": "Écurixe",
+ "squashel": "Écurouste",
+ "stealth": "Furtivité",
+ "stellarys": "Stellaria",
+ "support": "SOUTIEN",
+ "susuta": "Louloutre",
+ "swim": "Nager",
+ "terrain_interaction": "Interaction terrain",
+ "thornblade": "Rapiéronce",
+ "thunderstorm_form": "Forme des orages",
+ "tromber": "Trombec",
+ "tubster": "Tubabec",
+ "tuckin": "Nutrion",
+ "turbo": "Turbulaine",
+ "veilfloat": "Voilueur",
+ "vision": "Vision",
+ "waleetle": "Coléobée",
+ "water": "eau",
+ "wind": "Vent",
+ "wisptis": "Flammi",
+ "witchin": "Sorcelonde"
+}
diff --git a/website/public/data/games.json b/website/public/data/games.json
index 43c3255..6ed1ee8 100644
--- a/website/public/data/games.json
+++ b/website/public/data/games.json
@@ -55,6 +55,27 @@
]
}
]
+ },
+ {
+ "id": "aniimo",
+ "title": "Aniimo",
+ "eyebrow": "Guide de jeu",
+ "images": {
+ "cardCover": "/static/img/games/aniimo/card-cover.png",
+ "heroBg": "/static/img/games/aniimo/hero-bg.png",
+ "aniimosCover": "/static/img/aniimo/emberpup__basic_form.png"
+ },
+ "summary": "Retrouvez les Aniimos et leurs variantes selon leurs éléments, leur rôle et leurs compétences de logis.",
+ "sections": [
+ {
+ "title": "Aniimos",
+ "items": [
+ "Rechercher un Aniimo",
+ "Comparer les variantes",
+ "Filtrer les capacités et leurs niveaux"
+ ]
+ }
+ ]
}
]
}
diff --git a/website/public/data/site.json b/website/public/data/site.json
index 10af6f4..65b341a 100644
--- a/website/public/data/site.json
+++ b/website/public/data/site.json
@@ -2718,5 +2718,51 @@
},
"emptyTitle": "Aucune toolbox",
"emptyText": "Créez une première toolbox pour stocker vos outils dans ce navigateur."
+ },
+ "guideChecklist": {
+ "label": "Checklist associée",
+ "untitled": "Checklist sans titre",
+ "none": "Aucune checklist",
+ "linkToolbox": "Associer une toolbox pour suivre une checklist",
+ "noCompatible": "Aucune checklist de cette toolbox ne correspond aux éléments du guide.",
+ "noMatches": "Cette checklist ne contient plus de correspondance avec le guide.",
+ "matches": "correspondances",
+ "unlink": "Dissocier",
+ "hideCompleted": "Masquer les cochés",
+ "completed": "Coché"
+ },
+ "aniimo": {
+ "title": "Aniimos",
+ "description": "Filtrez les variantes par nom, éléments, rôle et compétences de logis, puis retournez les cartes pour consulter leurs capacités.",
+ "homeDescription": "Recherche par nom, rôle, éléments et compétences de logis.",
+ "copyChecklist": "Copier les Aniimos visibles sans les variantes pour checklist",
+ "copyVariantsChecklist": "Copier toutes les variantes visibles pour checklist",
+ "statsTitle": "Statistiques",
+ "statsTotal": "Total",
+ "stats": { "hp": "PV", "atk": "Attaque", "p_def": "Déf. phys.", "m_def": "Déf. mag.", "break": "Rupture", "regen": "Régén." },
+ "filters": "Filtres",
+ "name": "Nom",
+ "searchPlaceholder": "Nom français ou anglais…",
+ "elements": "Éléments",
+ "roles": "Rôles",
+ "role": "Rôle",
+ "abilities": "Compétences de logis",
+ "minimum": "Niveau minimum",
+ "abilityLevelsHint": "0 : sans filtre · 1–4 : niveau minimum",
+ "abilityDisabled": "Sans filtre",
+ "level": "Niv.",
+ "match": "Correspondance",
+ "and": "ET",
+ "or": "OU",
+ "reset": "Réinitialiser les filtres",
+ "groupVariants": "Regrouper les variantes",
+ "variants": "Variantes",
+ "results": "Résultats Aniimo",
+ "emptyTitle": "Aucun résultat",
+ "emptyText": "Ajustez la recherche ou réinitialisez les filtres actifs.",
+ "loading": "Chargement des Aniimos…",
+ "error": "Impossible de charger Aniimo",
+ "showFront": "Afficher le portrait",
+ "showBack": "Afficher les capacités"
}
}
diff --git a/website/public/static/icons/multiples.svg b/website/public/static/icons/multiples.svg
new file mode 100644
index 0000000..919090e
--- /dev/null
+++ b/website/public/static/icons/multiples.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/website/public/static/img/aniimo/bailite__basic_form.png b/website/public/static/img/aniimo/bailite__basic_form.png
new file mode 100644
index 0000000..2e85854
Binary files /dev/null and b/website/public/static/img/aniimo/bailite__basic_form.png differ
diff --git a/website/public/static/img/aniimo/baleetle__basic_form.png b/website/public/static/img/aniimo/baleetle__basic_form.png
new file mode 100644
index 0000000..cb8d833
Binary files /dev/null and b/website/public/static/img/aniimo/baleetle__basic_form.png differ
diff --git a/website/public/static/img/aniimo/baleetle__snowfield_form.png b/website/public/static/img/aniimo/baleetle__snowfield_form.png
new file mode 100644
index 0000000..ccbdc8e
Binary files /dev/null and b/website/public/static/img/aniimo/baleetle__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/besauce__basic_form.png b/website/public/static/img/aniimo/besauce__basic_form.png
new file mode 100644
index 0000000..5234a82
Binary files /dev/null and b/website/public/static/img/aniimo/besauce__basic_form.png differ
diff --git a/website/public/static/img/aniimo/blazen__basic_form.png b/website/public/static/img/aniimo/blazen__basic_form.png
new file mode 100644
index 0000000..8f22b9a
Binary files /dev/null and b/website/public/static/img/aniimo/blazen__basic_form.png differ
diff --git a/website/public/static/img/aniimo/blazen__mountain_woods_form.png b/website/public/static/img/aniimo/blazen__mountain_woods_form.png
new file mode 100644
index 0000000..b0627ea
Binary files /dev/null and b/website/public/static/img/aniimo/blazen__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/blazen__prismana_form.png b/website/public/static/img/aniimo/blazen__prismana_form.png
new file mode 100644
index 0000000..db416c6
Binary files /dev/null and b/website/public/static/img/aniimo/blazen__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/bolty__basic_form.png b/website/public/static/img/aniimo/bolty__basic_form.png
new file mode 100644
index 0000000..af616bb
Binary files /dev/null and b/website/public/static/img/aniimo/bolty__basic_form.png differ
diff --git a/website/public/static/img/aniimo/bolty__mountain_woods_form.png b/website/public/static/img/aniimo/bolty__mountain_woods_form.png
new file mode 100644
index 0000000..573407f
Binary files /dev/null and b/website/public/static/img/aniimo/bolty__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/bonesky__basic_form.png b/website/public/static/img/aniimo/bonesky__basic_form.png
new file mode 100644
index 0000000..836cda5
Binary files /dev/null and b/website/public/static/img/aniimo/bonesky__basic_form.png differ
diff --git a/website/public/static/img/aniimo/bonesky__nighttime_form.png b/website/public/static/img/aniimo/bonesky__nighttime_form.png
new file mode 100644
index 0000000..10ccfcb
Binary files /dev/null and b/website/public/static/img/aniimo/bonesky__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/bouldus__basic_form.png b/website/public/static/img/aniimo/bouldus__basic_form.png
new file mode 100644
index 0000000..8c9da4b
Binary files /dev/null and b/website/public/static/img/aniimo/bouldus__basic_form.png differ
diff --git a/website/public/static/img/aniimo/bouldus__snowfield_form.png b/website/public/static/img/aniimo/bouldus__snowfield_form.png
new file mode 100644
index 0000000..69ee5c5
Binary files /dev/null and b/website/public/static/img/aniimo/bouldus__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/break.png b/website/public/static/img/aniimo/break.png
new file mode 100644
index 0000000..0a319da
Binary files /dev/null and b/website/public/static/img/aniimo/break.png differ
diff --git a/website/public/static/img/aniimo/budclaw__basic_form.png b/website/public/static/img/aniimo/budclaw__basic_form.png
new file mode 100644
index 0000000..cd9ce08
Binary files /dev/null and b/website/public/static/img/aniimo/budclaw__basic_form.png differ
diff --git a/website/public/static/img/aniimo/budclaw__bay_form.png b/website/public/static/img/aniimo/budclaw__bay_form.png
new file mode 100644
index 0000000..11e5006
Binary files /dev/null and b/website/public/static/img/aniimo/budclaw__bay_form.png differ
diff --git a/website/public/static/img/aniimo/budclaw__beach_form.png b/website/public/static/img/aniimo/budclaw__beach_form.png
new file mode 100644
index 0000000..577d513
Binary files /dev/null and b/website/public/static/img/aniimo/budclaw__beach_form.png differ
diff --git a/website/public/static/img/aniimo/budclaw__mudflat_form.png b/website/public/static/img/aniimo/budclaw__mudflat_form.png
new file mode 100644
index 0000000..ff6770e
Binary files /dev/null and b/website/public/static/img/aniimo/budclaw__mudflat_form.png differ
diff --git a/website/public/static/img/aniimo/budsquire__basic_form.png b/website/public/static/img/aniimo/budsquire__basic_form.png
new file mode 100644
index 0000000..fef919f
Binary files /dev/null and b/website/public/static/img/aniimo/budsquire__basic_form.png differ
diff --git a/website/public/static/img/aniimo/budsquire__rosetower_woods_form.png b/website/public/static/img/aniimo/budsquire__rosetower_woods_form.png
new file mode 100644
index 0000000..77e0398
Binary files /dev/null and b/website/public/static/img/aniimo/budsquire__rosetower_woods_form.png differ
diff --git a/website/public/static/img/aniimo/bulbly__basic_form.png b/website/public/static/img/aniimo/bulbly__basic_form.png
new file mode 100644
index 0000000..2ae9845
Binary files /dev/null and b/website/public/static/img/aniimo/bulbly__basic_form.png differ
diff --git a/website/public/static/img/aniimo/carry.png b/website/public/static/img/aniimo/carry.png
new file mode 100644
index 0000000..73e5c35
Binary files /dev/null and b/website/public/static/img/aniimo/carry.png differ
diff --git a/website/public/static/img/aniimo/celestis__basic_form.png b/website/public/static/img/aniimo/celestis__basic_form.png
new file mode 100644
index 0000000..23f254d
Binary files /dev/null and b/website/public/static/img/aniimo/celestis__basic_form.png differ
diff --git a/website/public/static/img/aniimo/chirpi__basic_form.png b/website/public/static/img/aniimo/chirpi__basic_form.png
new file mode 100644
index 0000000..685879d
Binary files /dev/null and b/website/public/static/img/aniimo/chirpi__basic_form.png differ
diff --git a/website/public/static/img/aniimo/chirpi__beach_form.png b/website/public/static/img/aniimo/chirpi__beach_form.png
new file mode 100644
index 0000000..04a3cd9
Binary files /dev/null and b/website/public/static/img/aniimo/chirpi__beach_form.png differ
diff --git a/website/public/static/img/aniimo/chirpi__highlands.png b/website/public/static/img/aniimo/chirpi__highlands.png
new file mode 100644
index 0000000..251fc8e
Binary files /dev/null and b/website/public/static/img/aniimo/chirpi__highlands.png differ
diff --git a/website/public/static/img/aniimo/cornet__basic_form.png b/website/public/static/img/aniimo/cornet__basic_form.png
new file mode 100644
index 0000000..4ef926e
Binary files /dev/null and b/website/public/static/img/aniimo/cornet__basic_form.png differ
diff --git a/website/public/static/img/aniimo/cornet__beach_form.png b/website/public/static/img/aniimo/cornet__beach_form.png
new file mode 100644
index 0000000..b5a0d6b
Binary files /dev/null and b/website/public/static/img/aniimo/cornet__beach_form.png differ
diff --git a/website/public/static/img/aniimo/cornet__highlands.png b/website/public/static/img/aniimo/cornet__highlands.png
new file mode 100644
index 0000000..b14a7df
Binary files /dev/null and b/website/public/static/img/aniimo/cornet__highlands.png differ
diff --git a/website/public/static/img/aniimo/cornet__prismana_form.png b/website/public/static/img/aniimo/cornet__prismana_form.png
new file mode 100644
index 0000000..bee1905
Binary files /dev/null and b/website/public/static/img/aniimo/cornet__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/cozite__basic_form.png b/website/public/static/img/aniimo/cozite__basic_form.png
new file mode 100644
index 0000000..5f6381d
Binary files /dev/null and b/website/public/static/img/aniimo/cozite__basic_form.png differ
diff --git a/website/public/static/img/aniimo/crafting.png b/website/public/static/img/aniimo/crafting.png
new file mode 100644
index 0000000..74a85d9
Binary files /dev/null and b/website/public/static/img/aniimo/crafting.png differ
diff --git a/website/public/static/img/aniimo/cubbo__basic_form.png b/website/public/static/img/aniimo/cubbo__basic_form.png
new file mode 100644
index 0000000..a1709b5
Binary files /dev/null and b/website/public/static/img/aniimo/cubbo__basic_form.png differ
diff --git a/website/public/static/img/aniimo/dark.png b/website/public/static/img/aniimo/dark.png
new file mode 100644
index 0000000..8f7664a
Binary files /dev/null and b/website/public/static/img/aniimo/dark.png differ
diff --git a/website/public/static/img/aniimo/dazmand__basic_form.png b/website/public/static/img/aniimo/dazmand__basic_form.png
new file mode 100644
index 0000000..888d1d0
Binary files /dev/null and b/website/public/static/img/aniimo/dazmand__basic_form.png differ
diff --git a/website/public/static/img/aniimo/dewy__basic_form.png b/website/public/static/img/aniimo/dewy__basic_form.png
new file mode 100644
index 0000000..1ac04f6
Binary files /dev/null and b/website/public/static/img/aniimo/dewy__basic_form.png differ
diff --git a/website/public/static/img/aniimo/dps.png b/website/public/static/img/aniimo/dps.png
new file mode 100644
index 0000000..b815f44
Binary files /dev/null and b/website/public/static/img/aniimo/dps.png differ
diff --git a/website/public/static/img/aniimo/dreaple__basic_form.png b/website/public/static/img/aniimo/dreaple__basic_form.png
new file mode 100644
index 0000000..55fe0c9
Binary files /dev/null and b/website/public/static/img/aniimo/dreaple__basic_form.png differ
diff --git a/website/public/static/img/aniimo/eklue__basic_form.png b/website/public/static/img/aniimo/eklue__basic_form.png
new file mode 100644
index 0000000..77b14c8
Binary files /dev/null and b/website/public/static/img/aniimo/eklue__basic_form.png differ
diff --git a/website/public/static/img/aniimo/eko__basic_form.png b/website/public/static/img/aniimo/eko__basic_form.png
new file mode 100644
index 0000000..a0ad2b9
Binary files /dev/null and b/website/public/static/img/aniimo/eko__basic_form.png differ
diff --git a/website/public/static/img/aniimo/electric.png b/website/public/static/img/aniimo/electric.png
new file mode 100644
index 0000000..3f88b86
Binary files /dev/null and b/website/public/static/img/aniimo/electric.png differ
diff --git a/website/public/static/img/aniimo/emberpup__basic_form.png b/website/public/static/img/aniimo/emberpup__basic_form.png
new file mode 100644
index 0000000..bb8c668
Binary files /dev/null and b/website/public/static/img/aniimo/emberpup__basic_form.png differ
diff --git a/website/public/static/img/aniimo/emberpup__highlands.png b/website/public/static/img/aniimo/emberpup__highlands.png
new file mode 100644
index 0000000..5654b61
Binary files /dev/null and b/website/public/static/img/aniimo/emberpup__highlands.png differ
diff --git a/website/public/static/img/aniimo/emberpup__mountain_woods_form.png b/website/public/static/img/aniimo/emberpup__mountain_woods_form.png
new file mode 100644
index 0000000..76aacd0
Binary files /dev/null and b/website/public/static/img/aniimo/emberpup__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/erlath__basic_form.png b/website/public/static/img/aniimo/erlath__basic_form.png
new file mode 100644
index 0000000..59a6106
Binary files /dev/null and b/website/public/static/img/aniimo/erlath__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fahloo__basic_form.png b/website/public/static/img/aniimo/fahloo__basic_form.png
new file mode 100644
index 0000000..764da22
Binary files /dev/null and b/website/public/static/img/aniimo/fahloo__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fenmane__basic_form.png b/website/public/static/img/aniimo/fenmane__basic_form.png
new file mode 100644
index 0000000..5458bc3
Binary files /dev/null and b/website/public/static/img/aniimo/fenmane__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fenmane__prismana_form.png b/website/public/static/img/aniimo/fenmane__prismana_form.png
new file mode 100644
index 0000000..6205934
Binary files /dev/null and b/website/public/static/img/aniimo/fenmane__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/fenrier__basic_form.png b/website/public/static/img/aniimo/fenrier__basic_form.png
new file mode 100644
index 0000000..62512d7
Binary files /dev/null and b/website/public/static/img/aniimo/fenrier__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fenrier__nighttime_form.png b/website/public/static/img/aniimo/fenrier__nighttime_form.png
new file mode 100644
index 0000000..3d0b388
Binary files /dev/null and b/website/public/static/img/aniimo/fenrier__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/fentuft__basic_form.png b/website/public/static/img/aniimo/fentuft__basic_form.png
new file mode 100644
index 0000000..19660e3
Binary files /dev/null and b/website/public/static/img/aniimo/fentuft__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fire.png b/website/public/static/img/aniimo/fire.png
new file mode 100644
index 0000000..147d970
Binary files /dev/null and b/website/public/static/img/aniimo/fire.png differ
diff --git a/website/public/static/img/aniimo/flamerion__basic_form.png b/website/public/static/img/aniimo/flamerion__basic_form.png
new file mode 100644
index 0000000..d324e12
Binary files /dev/null and b/website/public/static/img/aniimo/flamerion__basic_form.png differ
diff --git a/website/public/static/img/aniimo/flamerion__forest_form.png b/website/public/static/img/aniimo/flamerion__forest_form.png
new file mode 100644
index 0000000..e967075
Binary files /dev/null and b/website/public/static/img/aniimo/flamerion__forest_form.png differ
diff --git a/website/public/static/img/aniimo/flamerion__highlands.png b/website/public/static/img/aniimo/flamerion__highlands.png
new file mode 100644
index 0000000..5679fdf
Binary files /dev/null and b/website/public/static/img/aniimo/flamerion__highlands.png differ
diff --git a/website/public/static/img/aniimo/flamerion__sea_of_flowers_form.png b/website/public/static/img/aniimo/flamerion__sea_of_flowers_form.png
new file mode 100644
index 0000000..e32b6b0
Binary files /dev/null and b/website/public/static/img/aniimo/flamerion__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/flameruff__basic_form.png b/website/public/static/img/aniimo/flameruff__basic_form.png
new file mode 100644
index 0000000..a6a00c5
Binary files /dev/null and b/website/public/static/img/aniimo/flameruff__basic_form.png differ
diff --git a/website/public/static/img/aniimo/flameruff__highlands.png b/website/public/static/img/aniimo/flameruff__highlands.png
new file mode 100644
index 0000000..3260a7a
Binary files /dev/null and b/website/public/static/img/aniimo/flameruff__highlands.png differ
diff --git a/website/public/static/img/aniimo/flameruff__mountain_woods_form.png b/website/public/static/img/aniimo/flameruff__mountain_woods_form.png
new file mode 100644
index 0000000..0d9f98b
Binary files /dev/null and b/website/public/static/img/aniimo/flameruff__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/flutternym__basic_form.png b/website/public/static/img/aniimo/flutternym__basic_form.png
new file mode 100644
index 0000000..e40add1
Binary files /dev/null and b/website/public/static/img/aniimo/flutternym__basic_form.png differ
diff --git a/website/public/static/img/aniimo/flutternym__mountain_woods_form.png b/website/public/static/img/aniimo/flutternym__mountain_woods_form.png
new file mode 100644
index 0000000..2fc137f
Binary files /dev/null and b/website/public/static/img/aniimo/flutternym__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/flutternym__nighttime_form.png b/website/public/static/img/aniimo/flutternym__nighttime_form.png
new file mode 100644
index 0000000..5c9b4bd
Binary files /dev/null and b/website/public/static/img/aniimo/flutternym__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/flutternym__sea_of_flowers_form.png b/website/public/static/img/aniimo/flutternym__sea_of_flowers_form.png
new file mode 100644
index 0000000..ed4fb76
Binary files /dev/null and b/website/public/static/img/aniimo/flutternym__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/fragrancier__basic_form.png b/website/public/static/img/aniimo/fragrancier__basic_form.png
new file mode 100644
index 0000000..ab80596
Binary files /dev/null and b/website/public/static/img/aniimo/fragrancier__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fulmintis__basic_form.png b/website/public/static/img/aniimo/fulmintis__basic_form.png
new file mode 100644
index 0000000..ce6e4f9
Binary files /dev/null and b/website/public/static/img/aniimo/fulmintis__basic_form.png differ
diff --git a/website/public/static/img/aniimo/fulmintis__prismana_form.png b/website/public/static/img/aniimo/fulmintis__prismana_form.png
new file mode 100644
index 0000000..3823119
Binary files /dev/null and b/website/public/static/img/aniimo/fulmintis__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/geoclaw__basic_form.png b/website/public/static/img/aniimo/geoclaw__basic_form.png
new file mode 100644
index 0000000..cfe144f
Binary files /dev/null and b/website/public/static/img/aniimo/geoclaw__basic_form.png differ
diff --git a/website/public/static/img/aniimo/geodeback__basic_form.png b/website/public/static/img/aniimo/geodeback__basic_form.png
new file mode 100644
index 0000000..fd6928b
Binary files /dev/null and b/website/public/static/img/aniimo/geodeback__basic_form.png differ
diff --git a/website/public/static/img/aniimo/glacy__basic_form.png b/website/public/static/img/aniimo/glacy__basic_form.png
new file mode 100644
index 0000000..a579b92
Binary files /dev/null and b/website/public/static/img/aniimo/glacy__basic_form.png differ
diff --git a/website/public/static/img/aniimo/glacy__prismana_form.png b/website/public/static/img/aniimo/glacy__prismana_form.png
new file mode 100644
index 0000000..96ea6ed
Binary files /dev/null and b/website/public/static/img/aniimo/glacy__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/glacy__sea_of_flowers_form.png b/website/public/static/img/aniimo/glacy__sea_of_flowers_form.png
new file mode 100644
index 0000000..97b4fe9
Binary files /dev/null and b/website/public/static/img/aniimo/glacy__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/glacy__snowfield_form.png b/website/public/static/img/aniimo/glacy__snowfield_form.png
new file mode 100644
index 0000000..d95ffd1
Binary files /dev/null and b/website/public/static/img/aniimo/glacy__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/glynsera__basic_form.png b/website/public/static/img/aniimo/glynsera__basic_form.png
new file mode 100644
index 0000000..57890e8
Binary files /dev/null and b/website/public/static/img/aniimo/glynsera__basic_form.png differ
diff --git a/website/public/static/img/aniimo/glynsera__nighttime_form.png b/website/public/static/img/aniimo/glynsera__nighttime_form.png
new file mode 100644
index 0000000..03e0856
Binary files /dev/null and b/website/public/static/img/aniimo/glynsera__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/glynsera__prismana_form.png b/website/public/static/img/aniimo/glynsera__prismana_form.png
new file mode 100644
index 0000000..d9c82b9
Binary files /dev/null and b/website/public/static/img/aniimo/glynsera__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/gracewing__basic_form.png b/website/public/static/img/aniimo/gracewing__basic_form.png
new file mode 100644
index 0000000..25a4cf8
Binary files /dev/null and b/website/public/static/img/aniimo/gracewing__basic_form.png differ
diff --git a/website/public/static/img/aniimo/gracewing__mountain_woods_form.png b/website/public/static/img/aniimo/gracewing__mountain_woods_form.png
new file mode 100644
index 0000000..7551b0c
Binary files /dev/null and b/website/public/static/img/aniimo/gracewing__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/gracewing__nighttime_form.png b/website/public/static/img/aniimo/gracewing__nighttime_form.png
new file mode 100644
index 0000000..c63ad51
Binary files /dev/null and b/website/public/static/img/aniimo/gracewing__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/gracewing__sea_of_flowers_form.png b/website/public/static/img/aniimo/gracewing__sea_of_flowers_form.png
new file mode 100644
index 0000000..0a6f89f
Binary files /dev/null and b/website/public/static/img/aniimo/gracewing__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/grass.png b/website/public/static/img/aniimo/grass.png
new file mode 100644
index 0000000..010cebb
Binary files /dev/null and b/website/public/static/img/aniimo/grass.png differ
diff --git a/website/public/static/img/aniimo/grizbo__basic_form.png b/website/public/static/img/aniimo/grizbo__basic_form.png
new file mode 100644
index 0000000..74ef38f
Binary files /dev/null and b/website/public/static/img/aniimo/grizbo__basic_form.png differ
diff --git a/website/public/static/img/aniimo/grizbo__prismana_form.png b/website/public/static/img/aniimo/grizbo__prismana_form.png
new file mode 100644
index 0000000..c13fa6b
Binary files /dev/null and b/website/public/static/img/aniimo/grizbo__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/heal.png b/website/public/static/img/aniimo/heal.png
new file mode 100644
index 0000000..a867981
Binary files /dev/null and b/website/public/static/img/aniimo/heal.png differ
diff --git a/website/public/static/img/aniimo/helgon__basic_form.png b/website/public/static/img/aniimo/helgon__basic_form.png
new file mode 100644
index 0000000..45022ee
Binary files /dev/null and b/website/public/static/img/aniimo/helgon__basic_form.png differ
diff --git a/website/public/static/img/aniimo/helgon__mountain_form.png b/website/public/static/img/aniimo/helgon__mountain_form.png
new file mode 100644
index 0000000..cb2d213
Binary files /dev/null and b/website/public/static/img/aniimo/helgon__mountain_form.png differ
diff --git a/website/public/static/img/aniimo/helion__basic_form.png b/website/public/static/img/aniimo/helion__basic_form.png
new file mode 100644
index 0000000..2650e53
Binary files /dev/null and b/website/public/static/img/aniimo/helion__basic_form.png differ
diff --git a/website/public/static/img/aniimo/helmut__basic_form.png b/website/public/static/img/aniimo/helmut__basic_form.png
new file mode 100644
index 0000000..a4e5e54
Binary files /dev/null and b/website/public/static/img/aniimo/helmut__basic_form.png differ
diff --git a/website/public/static/img/aniimo/helmut__mountain_woods_form.png b/website/public/static/img/aniimo/helmut__mountain_woods_form.png
new file mode 100644
index 0000000..922f58e
Binary files /dev/null and b/website/public/static/img/aniimo/helmut__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/helmut__snowfield_form.png b/website/public/static/img/aniimo/helmut__snowfield_form.png
new file mode 100644
index 0000000..3fea8ea
Binary files /dev/null and b/website/public/static/img/aniimo/helmut__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/helmwhelp__basic_form.png b/website/public/static/img/aniimo/helmwhelp__basic_form.png
new file mode 100644
index 0000000..0f0307f
Binary files /dev/null and b/website/public/static/img/aniimo/helmwhelp__basic_form.png differ
diff --git a/website/public/static/img/aniimo/helmwhelp__mountain_form.png b/website/public/static/img/aniimo/helmwhelp__mountain_form.png
new file mode 100644
index 0000000..54cfe20
Binary files /dev/null and b/website/public/static/img/aniimo/helmwhelp__mountain_form.png differ
diff --git a/website/public/static/img/aniimo/holy.png b/website/public/static/img/aniimo/holy.png
new file mode 100644
index 0000000..9b3b4e1
Binary files /dev/null and b/website/public/static/img/aniimo/holy.png differ
diff --git a/website/public/static/img/aniimo/hummin__basic_form.png b/website/public/static/img/aniimo/hummin__basic_form.png
new file mode 100644
index 0000000..0cecdd7
Binary files /dev/null and b/website/public/static/img/aniimo/hummin__basic_form.png differ
diff --git a/website/public/static/img/aniimo/hummin__mountain_form.png b/website/public/static/img/aniimo/hummin__mountain_form.png
new file mode 100644
index 0000000..3238cbd
Binary files /dev/null and b/website/public/static/img/aniimo/hummin__mountain_form.png differ
diff --git a/website/public/static/img/aniimo/ice.png b/website/public/static/img/aniimo/ice.png
new file mode 100644
index 0000000..4e0640c
Binary files /dev/null and b/website/public/static/img/aniimo/ice.png differ
diff --git a/website/public/static/img/aniimo/ignitis__basic_form.png b/website/public/static/img/aniimo/ignitis__basic_form.png
new file mode 100644
index 0000000..7e93d71
Binary files /dev/null and b/website/public/static/img/aniimo/ignitis__basic_form.png differ
diff --git a/website/public/static/img/aniimo/ignitis__forest_form.png b/website/public/static/img/aniimo/ignitis__forest_form.png
new file mode 100644
index 0000000..22dcc8e
Binary files /dev/null and b/website/public/static/img/aniimo/ignitis__forest_form.png differ
diff --git a/website/public/static/img/aniimo/ignitis__highlands.png b/website/public/static/img/aniimo/ignitis__highlands.png
new file mode 100644
index 0000000..5e6371d
Binary files /dev/null and b/website/public/static/img/aniimo/ignitis__highlands.png differ
diff --git a/website/public/static/img/aniimo/ignitis__prismana_form.png b/website/public/static/img/aniimo/ignitis__prismana_form.png
new file mode 100644
index 0000000..4e03bee
Binary files /dev/null and b/website/public/static/img/aniimo/ignitis__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/infergon__basic_form.png b/website/public/static/img/aniimo/infergon__basic_form.png
new file mode 100644
index 0000000..641e79e
Binary files /dev/null and b/website/public/static/img/aniimo/infergon__basic_form.png differ
diff --git a/website/public/static/img/aniimo/infergon__prismana_form.png b/website/public/static/img/aniimo/infergon__prismana_form.png
new file mode 100644
index 0000000..16abf5b
Binary files /dev/null and b/website/public/static/img/aniimo/infergon__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/inferlupa__basic_form.png b/website/public/static/img/aniimo/inferlupa__basic_form.png
new file mode 100644
index 0000000..084b8ec
Binary files /dev/null and b/website/public/static/img/aniimo/inferlupa__basic_form.png differ
diff --git a/website/public/static/img/aniimo/inferlupa__prismana_form.png b/website/public/static/img/aniimo/inferlupa__prismana_form.png
new file mode 100644
index 0000000..2472ebc
Binary files /dev/null and b/website/public/static/img/aniimo/inferlupa__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/iris__basic_form.png b/website/public/static/img/aniimo/iris__basic_form.png
new file mode 100644
index 0000000..5f1636b
Binary files /dev/null and b/website/public/static/img/aniimo/iris__basic_form.png differ
diff --git a/website/public/static/img/aniimo/iris__forest_form.png b/website/public/static/img/aniimo/iris__forest_form.png
new file mode 100644
index 0000000..38c446c
Binary files /dev/null and b/website/public/static/img/aniimo/iris__forest_form.png differ
diff --git a/website/public/static/img/aniimo/iris__grassland_form.png b/website/public/static/img/aniimo/iris__grassland_form.png
new file mode 100644
index 0000000..773a61b
Binary files /dev/null and b/website/public/static/img/aniimo/iris__grassland_form.png differ
diff --git a/website/public/static/img/aniimo/iris__highlands.png b/website/public/static/img/aniimo/iris__highlands.png
new file mode 100644
index 0000000..0495b91
Binary files /dev/null and b/website/public/static/img/aniimo/iris__highlands.png differ
diff --git a/website/public/static/img/aniimo/iris__mountain_woods_form.png b/website/public/static/img/aniimo/iris__mountain_woods_form.png
new file mode 100644
index 0000000..d1d3508
Binary files /dev/null and b/website/public/static/img/aniimo/iris__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/iris__plateau_form.png b/website/public/static/img/aniimo/iris__plateau_form.png
new file mode 100644
index 0000000..30c38e9
Binary files /dev/null and b/website/public/static/img/aniimo/iris__plateau_form.png differ
diff --git a/website/public/static/img/aniimo/iris__prismana_form.png b/website/public/static/img/aniimo/iris__prismana_form.png
new file mode 100644
index 0000000..fbe033d
Binary files /dev/null and b/website/public/static/img/aniimo/iris__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/irisal__basic_form.png b/website/public/static/img/aniimo/irisal__basic_form.png
new file mode 100644
index 0000000..20b8d3b
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__basic_form.png differ
diff --git a/website/public/static/img/aniimo/irisal__forest_form.png b/website/public/static/img/aniimo/irisal__forest_form.png
new file mode 100644
index 0000000..cfcffb6
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__forest_form.png differ
diff --git a/website/public/static/img/aniimo/irisal__grassland_form.png b/website/public/static/img/aniimo/irisal__grassland_form.png
new file mode 100644
index 0000000..3bf6a9a
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__grassland_form.png differ
diff --git a/website/public/static/img/aniimo/irisal__highlands.png b/website/public/static/img/aniimo/irisal__highlands.png
new file mode 100644
index 0000000..d338476
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__highlands.png differ
diff --git a/website/public/static/img/aniimo/irisal__mountain_woods_form.png b/website/public/static/img/aniimo/irisal__mountain_woods_form.png
new file mode 100644
index 0000000..a4c0571
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/irisal__plateau_form.png b/website/public/static/img/aniimo/irisal__plateau_form.png
new file mode 100644
index 0000000..39df12b
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__plateau_form.png differ
diff --git a/website/public/static/img/aniimo/irisal__prismana_form.png b/website/public/static/img/aniimo/irisal__prismana_form.png
new file mode 100644
index 0000000..8fd1c43
Binary files /dev/null and b/website/public/static/img/aniimo/irisal__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/irisalis__prismana_form.png b/website/public/static/img/aniimo/irisalis__prismana_form.png
new file mode 100644
index 0000000..b0d282d
Binary files /dev/null and b/website/public/static/img/aniimo/irisalis__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/jawling__basic_form.png b/website/public/static/img/aniimo/jawling__basic_form.png
new file mode 100644
index 0000000..1629fdd
Binary files /dev/null and b/website/public/static/img/aniimo/jawling__basic_form.png differ
diff --git a/website/public/static/img/aniimo/jawling__mountain_form.png b/website/public/static/img/aniimo/jawling__mountain_form.png
new file mode 100644
index 0000000..b06be27
Binary files /dev/null and b/website/public/static/img/aniimo/jawling__mountain_form.png differ
diff --git a/website/public/static/img/aniimo/lavazar__basic_form.png b/website/public/static/img/aniimo/lavazar__basic_form.png
new file mode 100644
index 0000000..3052010
Binary files /dev/null and b/website/public/static/img/aniimo/lavazar__basic_form.png differ
diff --git a/website/public/static/img/aniimo/leafy__basic_form.png b/website/public/static/img/aniimo/leafy__basic_form.png
new file mode 100644
index 0000000..d01e42d
Binary files /dev/null and b/website/public/static/img/aniimo/leafy__basic_form.png differ
diff --git a/website/public/static/img/aniimo/leisure.png b/website/public/static/img/aniimo/leisure.png
new file mode 100644
index 0000000..c01be5f
Binary files /dev/null and b/website/public/static/img/aniimo/leisure.png differ
diff --git a/website/public/static/img/aniimo/little_fire_spirit__basic_form.png b/website/public/static/img/aniimo/little_fire_spirit__basic_form.png
new file mode 100644
index 0000000..046776d
Binary files /dev/null and b/website/public/static/img/aniimo/little_fire_spirit__basic_form.png differ
diff --git a/website/public/static/img/aniimo/little_fire_spirit__prismana_form.png b/website/public/static/img/aniimo/little_fire_spirit__prismana_form.png
new file mode 100644
index 0000000..d3997ad
Binary files /dev/null and b/website/public/static/img/aniimo/little_fire_spirit__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/luminelle__basic_form.png b/website/public/static/img/aniimo/luminelle__basic_form.png
new file mode 100644
index 0000000..7356c03
Binary files /dev/null and b/website/public/static/img/aniimo/luminelle__basic_form.png differ
diff --git a/website/public/static/img/aniimo/luminelle__deluge_form.png b/website/public/static/img/aniimo/luminelle__deluge_form.png
new file mode 100644
index 0000000..6db73b7
Binary files /dev/null and b/website/public/static/img/aniimo/luminelle__deluge_form.png differ
diff --git a/website/public/static/img/aniimo/luminelle__prismana_form.png b/website/public/static/img/aniimo/luminelle__prismana_form.png
new file mode 100644
index 0000000..29770b5
Binary files /dev/null and b/website/public/static/img/aniimo/luminelle__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/lunara__basic_form.png b/website/public/static/img/aniimo/lunara__basic_form.png
new file mode 100644
index 0000000..74dd60c
Binary files /dev/null and b/website/public/static/img/aniimo/lunara__basic_form.png differ
diff --git a/website/public/static/img/aniimo/magmarex__basic_form.png b/website/public/static/img/aniimo/magmarex__basic_form.png
new file mode 100644
index 0000000..3ee1aac
Binary files /dev/null and b/website/public/static/img/aniimo/magmarex__basic_form.png differ
diff --git a/website/public/static/img/aniimo/magmarex__prismana_form.png b/website/public/static/img/aniimo/magmarex__prismana_form.png
new file mode 100644
index 0000000..d881b78
Binary files /dev/null and b/website/public/static/img/aniimo/magmarex__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/melloblum__basic_form.png b/website/public/static/img/aniimo/melloblum__basic_form.png
new file mode 100644
index 0000000..1b48672
Binary files /dev/null and b/website/public/static/img/aniimo/melloblum__basic_form.png differ
diff --git a/website/public/static/img/aniimo/melloblum__prismana_form.png b/website/public/static/img/aniimo/melloblum__prismana_form.png
new file mode 100644
index 0000000..a6c46c8
Binary files /dev/null and b/website/public/static/img/aniimo/melloblum__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/minespine__basic_form.png b/website/public/static/img/aniimo/minespine__basic_form.png
new file mode 100644
index 0000000..15eecd2
Binary files /dev/null and b/website/public/static/img/aniimo/minespine__basic_form.png differ
diff --git a/website/public/static/img/aniimo/nimbi__basic_form.png b/website/public/static/img/aniimo/nimbi__basic_form.png
new file mode 100644
index 0000000..6abf4bf
Binary files /dev/null and b/website/public/static/img/aniimo/nimbi__basic_form.png differ
diff --git a/website/public/static/img/aniimo/nimbi__cloudy_mist_form.png b/website/public/static/img/aniimo/nimbi__cloudy_mist_form.png
new file mode 100644
index 0000000..f22302d
Binary files /dev/null and b/website/public/static/img/aniimo/nimbi__cloudy_mist_form.png differ
diff --git a/website/public/static/img/aniimo/nimbi__deluge_form.png b/website/public/static/img/aniimo/nimbi__deluge_form.png
new file mode 100644
index 0000000..ea3b720
Binary files /dev/null and b/website/public/static/img/aniimo/nimbi__deluge_form.png differ
diff --git a/website/public/static/img/aniimo/nimbi__plateau_form.png b/website/public/static/img/aniimo/nimbi__plateau_form.png
new file mode 100644
index 0000000..9118528
Binary files /dev/null and b/website/public/static/img/aniimo/nimbi__plateau_form.png differ
diff --git a/website/public/static/img/aniimo/panpanta__basic_form.png b/website/public/static/img/aniimo/panpanta__basic_form.png
new file mode 100644
index 0000000..2c1d6d0
Binary files /dev/null and b/website/public/static/img/aniimo/panpanta__basic_form.png differ
diff --git a/website/public/static/img/aniimo/panpanta__nighttime_form.png b/website/public/static/img/aniimo/panpanta__nighttime_form.png
new file mode 100644
index 0000000..afad546
Binary files /dev/null and b/website/public/static/img/aniimo/panpanta__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/panpanta__prismana_form.png b/website/public/static/img/aniimo/panpanta__prismana_form.png
new file mode 100644
index 0000000..c09f639
Binary files /dev/null and b/website/public/static/img/aniimo/panpanta__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/pawney__basic_form.png b/website/public/static/img/aniimo/pawney__basic_form.png
new file mode 100644
index 0000000..e720bab
Binary files /dev/null and b/website/public/static/img/aniimo/pawney__basic_form.png differ
diff --git a/website/public/static/img/aniimo/pawney__mountain_woods_form.png b/website/public/static/img/aniimo/pawney__mountain_woods_form.png
new file mode 100644
index 0000000..e5fa975
Binary files /dev/null and b/website/public/static/img/aniimo/pawney__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/pawney__prismana_form.png b/website/public/static/img/aniimo/pawney__prismana_form.png
new file mode 100644
index 0000000..f42e6ee
Binary files /dev/null and b/website/public/static/img/aniimo/pawney__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/pawney__snowfield_form.png b/website/public/static/img/aniimo/pawney__snowfield_form.png
new file mode 100644
index 0000000..a21ccaf
Binary files /dev/null and b/website/public/static/img/aniimo/pawney__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/pebbling__basic_form.png b/website/public/static/img/aniimo/pebbling__basic_form.png
new file mode 100644
index 0000000..28f2484
Binary files /dev/null and b/website/public/static/img/aniimo/pebbling__basic_form.png differ
diff --git a/website/public/static/img/aniimo/perfumery.png b/website/public/static/img/aniimo/perfumery.png
new file mode 100644
index 0000000..6cff4a6
Binary files /dev/null and b/website/public/static/img/aniimo/perfumery.png differ
diff --git a/website/public/static/img/aniimo/piopiota__basic_form.png b/website/public/static/img/aniimo/piopiota__basic_form.png
new file mode 100644
index 0000000..cf1bd8d
Binary files /dev/null and b/website/public/static/img/aniimo/piopiota__basic_form.png differ
diff --git a/website/public/static/img/aniimo/piopiota__nighttime_form.png b/website/public/static/img/aniimo/piopiota__nighttime_form.png
new file mode 100644
index 0000000..fafb7fa
Binary files /dev/null and b/website/public/static/img/aniimo/piopiota__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/pomawk__basic_form.png b/website/public/static/img/aniimo/pomawk__basic_form.png
new file mode 100644
index 0000000..084af53
Binary files /dev/null and b/website/public/static/img/aniimo/pomawk__basic_form.png differ
diff --git a/website/public/static/img/aniimo/pomawk__highlands.png b/website/public/static/img/aniimo/pomawk__highlands.png
new file mode 100644
index 0000000..4e9988c
Binary files /dev/null and b/website/public/static/img/aniimo/pomawk__highlands.png differ
diff --git a/website/public/static/img/aniimo/pomawk__sea_of_flowers_form.png b/website/public/static/img/aniimo/pomawk__sea_of_flowers_form.png
new file mode 100644
index 0000000..b11838e
Binary files /dev/null and b/website/public/static/img/aniimo/pomawk__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/pomawk__snowfield_form.png b/website/public/static/img/aniimo/pomawk__snowfield_form.png
new file mode 100644
index 0000000..61551f3
Binary files /dev/null and b/website/public/static/img/aniimo/pomawk__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/pomegg__basic_form.png b/website/public/static/img/aniimo/pomegg__basic_form.png
new file mode 100644
index 0000000..d9427b1
Binary files /dev/null and b/website/public/static/img/aniimo/pomegg__basic_form.png differ
diff --git a/website/public/static/img/aniimo/pomegg__highlands.png b/website/public/static/img/aniimo/pomegg__highlands.png
new file mode 100644
index 0000000..d6ed5a2
Binary files /dev/null and b/website/public/static/img/aniimo/pomegg__highlands.png differ
diff --git a/website/public/static/img/aniimo/pomegg__sea_of_flowers_form.png b/website/public/static/img/aniimo/pomegg__sea_of_flowers_form.png
new file mode 100644
index 0000000..29f0f5a
Binary files /dev/null and b/website/public/static/img/aniimo/pomegg__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/pomegg__snowfield_form.png b/website/public/static/img/aniimo/pomegg__snowfield_form.png
new file mode 100644
index 0000000..0b376b4
Binary files /dev/null and b/website/public/static/img/aniimo/pomegg__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/popota__basic_form.png b/website/public/static/img/aniimo/popota__basic_form.png
new file mode 100644
index 0000000..5d8457a
Binary files /dev/null and b/website/public/static/img/aniimo/popota__basic_form.png differ
diff --git a/website/public/static/img/aniimo/popota__nighttime_form.png b/website/public/static/img/aniimo/popota__nighttime_form.png
new file mode 100644
index 0000000..242b87d
Binary files /dev/null and b/website/public/static/img/aniimo/popota__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/pranky__basic_form.png b/website/public/static/img/aniimo/pranky__basic_form.png
new file mode 100644
index 0000000..f65784e
Binary files /dev/null and b/website/public/static/img/aniimo/pranky__basic_form.png differ
diff --git a/website/public/static/img/aniimo/pranky__sea_of_flowers_form.png b/website/public/static/img/aniimo/pranky__sea_of_flowers_form.png
new file mode 100644
index 0000000..966fe5c
Binary files /dev/null and b/website/public/static/img/aniimo/pranky__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/pranky__snowfield_form.png b/website/public/static/img/aniimo/pranky__snowfield_form.png
new file mode 100644
index 0000000..9d0b712
Binary files /dev/null and b/website/public/static/img/aniimo/pranky__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/regen.png b/website/public/static/img/aniimo/regen.png
new file mode 100644
index 0000000..e7b5eb7
Binary files /dev/null and b/website/public/static/img/aniimo/regen.png differ
diff --git a/website/public/static/img/aniimo/rock.png b/website/public/static/img/aniimo/rock.png
new file mode 100644
index 0000000..85ba998
Binary files /dev/null and b/website/public/static/img/aniimo/rock.png differ
diff --git a/website/public/static/img/aniimo/rookey__basic_form.png b/website/public/static/img/aniimo/rookey__basic_form.png
new file mode 100644
index 0000000..afa99a5
Binary files /dev/null and b/website/public/static/img/aniimo/rookey__basic_form.png differ
diff --git a/website/public/static/img/aniimo/rookey__mountain_woods_form.png b/website/public/static/img/aniimo/rookey__mountain_woods_form.png
new file mode 100644
index 0000000..d3e5122
Binary files /dev/null and b/website/public/static/img/aniimo/rookey__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/rookey__snowfield_form.png b/website/public/static/img/aniimo/rookey__snowfield_form.png
new file mode 100644
index 0000000..2e99540
Binary files /dev/null and b/website/public/static/img/aniimo/rookey__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/scorchhowl__basic_form.png b/website/public/static/img/aniimo/scorchhowl__basic_form.png
new file mode 100644
index 0000000..0ac13fc
Binary files /dev/null and b/website/public/static/img/aniimo/scorchhowl__basic_form.png differ
diff --git a/website/public/static/img/aniimo/scorchhowl__highlands.png b/website/public/static/img/aniimo/scorchhowl__highlands.png
new file mode 100644
index 0000000..fd4f016
Binary files /dev/null and b/website/public/static/img/aniimo/scorchhowl__highlands.png differ
diff --git a/website/public/static/img/aniimo/scorchhowl__mountain_woods_form.png b/website/public/static/img/aniimo/scorchhowl__mountain_woods_form.png
new file mode 100644
index 0000000..16cf1e4
Binary files /dev/null and b/website/public/static/img/aniimo/scorchhowl__mountain_woods_form.png differ
diff --git a/website/public/static/img/aniimo/scorchhowl__prismana_form.png b/website/public/static/img/aniimo/scorchhowl__prismana_form.png
new file mode 100644
index 0000000..fbc15d8
Binary files /dev/null and b/website/public/static/img/aniimo/scorchhowl__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/scorchhowl__thunderstorm_form.png b/website/public/static/img/aniimo/scorchhowl__thunderstorm_form.png
new file mode 100644
index 0000000..ecb3780
Binary files /dev/null and b/website/public/static/img/aniimo/scorchhowl__thunderstorm_form.png differ
diff --git a/website/public/static/img/aniimo/sheldon__basic_form.png b/website/public/static/img/aniimo/sheldon__basic_form.png
new file mode 100644
index 0000000..e6d5e32
Binary files /dev/null and b/website/public/static/img/aniimo/sheldon__basic_form.png differ
diff --git a/website/public/static/img/aniimo/shelly__basic_form.png b/website/public/static/img/aniimo/shelly__basic_form.png
new file mode 100644
index 0000000..5dd4dd1
Binary files /dev/null and b/website/public/static/img/aniimo/shelly__basic_form.png differ
diff --git a/website/public/static/img/aniimo/sherro__basic_form.png b/website/public/static/img/aniimo/sherro__basic_form.png
new file mode 100644
index 0000000..7685b52
Binary files /dev/null and b/website/public/static/img/aniimo/sherro__basic_form.png differ
diff --git a/website/public/static/img/aniimo/sherro__prismana_form.png b/website/public/static/img/aniimo/sherro__prismana_form.png
new file mode 100644
index 0000000..2ea6e7e
Binary files /dev/null and b/website/public/static/img/aniimo/sherro__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/sherro__thunderstorm_form.png b/website/public/static/img/aniimo/sherro__thunderstorm_form.png
new file mode 100644
index 0000000..389eccb
Binary files /dev/null and b/website/public/static/img/aniimo/sherro__thunderstorm_form.png differ
diff --git a/website/public/static/img/aniimo/shrubclaw__basic_form.png b/website/public/static/img/aniimo/shrubclaw__basic_form.png
new file mode 100644
index 0000000..6324899
Binary files /dev/null and b/website/public/static/img/aniimo/shrubclaw__basic_form.png differ
diff --git a/website/public/static/img/aniimo/shrubclaw__bay_form.png b/website/public/static/img/aniimo/shrubclaw__bay_form.png
new file mode 100644
index 0000000..6fb1ebf
Binary files /dev/null and b/website/public/static/img/aniimo/shrubclaw__bay_form.png differ
diff --git a/website/public/static/img/aniimo/shrubclaw__beach_form.png b/website/public/static/img/aniimo/shrubclaw__beach_form.png
new file mode 100644
index 0000000..6769479
Binary files /dev/null and b/website/public/static/img/aniimo/shrubclaw__beach_form.png differ
diff --git a/website/public/static/img/aniimo/shrubclaw__mudflat_form.png b/website/public/static/img/aniimo/shrubclaw__mudflat_form.png
new file mode 100644
index 0000000..36e7f84
Binary files /dev/null and b/website/public/static/img/aniimo/shrubclaw__mudflat_form.png differ
diff --git a/website/public/static/img/aniimo/skippy__basic_form.png b/website/public/static/img/aniimo/skippy__basic_form.png
new file mode 100644
index 0000000..954a446
Binary files /dev/null and b/website/public/static/img/aniimo/skippy__basic_form.png differ
diff --git a/website/public/static/img/aniimo/skippy__sea_of_flowers_form.png b/website/public/static/img/aniimo/skippy__sea_of_flowers_form.png
new file mode 100644
index 0000000..053dd9f
Binary files /dev/null and b/website/public/static/img/aniimo/skippy__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/skippy__snowfield_form.png b/website/public/static/img/aniimo/skippy__snowfield_form.png
new file mode 100644
index 0000000..0a942ef
Binary files /dev/null and b/website/public/static/img/aniimo/skippy__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/somniwing__prismana_form.png b/website/public/static/img/aniimo/somniwing__prismana_form.png
new file mode 100644
index 0000000..03dffc9
Binary files /dev/null and b/website/public/static/img/aniimo/somniwing__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/sparki__basic_form.png b/website/public/static/img/aniimo/sparki__basic_form.png
new file mode 100644
index 0000000..debbc53
Binary files /dev/null and b/website/public/static/img/aniimo/sparki__basic_form.png differ
diff --git a/website/public/static/img/aniimo/sparki__forest_form.png b/website/public/static/img/aniimo/sparki__forest_form.png
new file mode 100644
index 0000000..9dc9ff7
Binary files /dev/null and b/website/public/static/img/aniimo/sparki__forest_form.png differ
diff --git a/website/public/static/img/aniimo/sparki__highlands.png b/website/public/static/img/aniimo/sparki__highlands.png
new file mode 100644
index 0000000..3f67bed
Binary files /dev/null and b/website/public/static/img/aniimo/sparki__highlands.png differ
diff --git a/website/public/static/img/aniimo/sparki__sea_of_flowers_form.png b/website/public/static/img/aniimo/sparki__sea_of_flowers_form.png
new file mode 100644
index 0000000..c69b42d
Binary files /dev/null and b/website/public/static/img/aniimo/sparki__sea_of_flowers_form.png differ
diff --git a/website/public/static/img/aniimo/squarrel__basic_form.png b/website/public/static/img/aniimo/squarrel__basic_form.png
new file mode 100644
index 0000000..2ba888c
Binary files /dev/null and b/website/public/static/img/aniimo/squarrel__basic_form.png differ
diff --git a/website/public/static/img/aniimo/squashel__basic_form.png b/website/public/static/img/aniimo/squashel__basic_form.png
new file mode 100644
index 0000000..e87ad38
Binary files /dev/null and b/website/public/static/img/aniimo/squashel__basic_form.png differ
diff --git a/website/public/static/img/aniimo/stellarys__basic_form.png b/website/public/static/img/aniimo/stellarys__basic_form.png
new file mode 100644
index 0000000..64be602
Binary files /dev/null and b/website/public/static/img/aniimo/stellarys__basic_form.png differ
diff --git a/website/public/static/img/aniimo/stellarys__deluge_form.png b/website/public/static/img/aniimo/stellarys__deluge_form.png
new file mode 100644
index 0000000..c78bd41
Binary files /dev/null and b/website/public/static/img/aniimo/stellarys__deluge_form.png differ
diff --git a/website/public/static/img/aniimo/stellarys__prismana_form.png b/website/public/static/img/aniimo/stellarys__prismana_form.png
new file mode 100644
index 0000000..02888a2
Binary files /dev/null and b/website/public/static/img/aniimo/stellarys__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/support.png b/website/public/static/img/aniimo/support.png
new file mode 100644
index 0000000..d89a5ce
Binary files /dev/null and b/website/public/static/img/aniimo/support.png differ
diff --git a/website/public/static/img/aniimo/susuta__basic_form.png b/website/public/static/img/aniimo/susuta__basic_form.png
new file mode 100644
index 0000000..4f82425
Binary files /dev/null and b/website/public/static/img/aniimo/susuta__basic_form.png differ
diff --git a/website/public/static/img/aniimo/susuta__nighttime_form.png b/website/public/static/img/aniimo/susuta__nighttime_form.png
new file mode 100644
index 0000000..cd1a8ff
Binary files /dev/null and b/website/public/static/img/aniimo/susuta__nighttime_form.png differ
diff --git a/website/public/static/img/aniimo/thornblade__basic_form.png b/website/public/static/img/aniimo/thornblade__basic_form.png
new file mode 100644
index 0000000..8888a2a
Binary files /dev/null and b/website/public/static/img/aniimo/thornblade__basic_form.png differ
diff --git a/website/public/static/img/aniimo/thornblade__prismana_form.png b/website/public/static/img/aniimo/thornblade__prismana_form.png
new file mode 100644
index 0000000..dc5eb8e
Binary files /dev/null and b/website/public/static/img/aniimo/thornblade__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/thornblade__rosetower_woods_form.png b/website/public/static/img/aniimo/thornblade__rosetower_woods_form.png
new file mode 100644
index 0000000..da73648
Binary files /dev/null and b/website/public/static/img/aniimo/thornblade__rosetower_woods_form.png differ
diff --git a/website/public/static/img/aniimo/thornblade__thunderstorm_form.png b/website/public/static/img/aniimo/thornblade__thunderstorm_form.png
new file mode 100644
index 0000000..446c7a5
Binary files /dev/null and b/website/public/static/img/aniimo/thornblade__thunderstorm_form.png differ
diff --git a/website/public/static/img/aniimo/tromber__basic_form.png b/website/public/static/img/aniimo/tromber__basic_form.png
new file mode 100644
index 0000000..c800660
Binary files /dev/null and b/website/public/static/img/aniimo/tromber__basic_form.png differ
diff --git a/website/public/static/img/aniimo/tromber__beach_form.png b/website/public/static/img/aniimo/tromber__beach_form.png
new file mode 100644
index 0000000..0ad73de
Binary files /dev/null and b/website/public/static/img/aniimo/tromber__beach_form.png differ
diff --git a/website/public/static/img/aniimo/tromber__highlands.png b/website/public/static/img/aniimo/tromber__highlands.png
new file mode 100644
index 0000000..5b68c84
Binary files /dev/null and b/website/public/static/img/aniimo/tromber__highlands.png differ
diff --git a/website/public/static/img/aniimo/tubster__basic_form.png b/website/public/static/img/aniimo/tubster__basic_form.png
new file mode 100644
index 0000000..8ae3412
Binary files /dev/null and b/website/public/static/img/aniimo/tubster__basic_form.png differ
diff --git a/website/public/static/img/aniimo/tubster__beach_form.png b/website/public/static/img/aniimo/tubster__beach_form.png
new file mode 100644
index 0000000..5dc6760
Binary files /dev/null and b/website/public/static/img/aniimo/tubster__beach_form.png differ
diff --git a/website/public/static/img/aniimo/tubster__highlands.png b/website/public/static/img/aniimo/tubster__highlands.png
new file mode 100644
index 0000000..25a2b33
Binary files /dev/null and b/website/public/static/img/aniimo/tubster__highlands.png differ
diff --git a/website/public/static/img/aniimo/tuckin__basic_form.png b/website/public/static/img/aniimo/tuckin__basic_form.png
new file mode 100644
index 0000000..d355d61
Binary files /dev/null and b/website/public/static/img/aniimo/tuckin__basic_form.png differ
diff --git a/website/public/static/img/aniimo/tuckin__mountain_form.png b/website/public/static/img/aniimo/tuckin__mountain_form.png
new file mode 100644
index 0000000..553eceb
Binary files /dev/null and b/website/public/static/img/aniimo/tuckin__mountain_form.png differ
diff --git a/website/public/static/img/aniimo/turbo__basic_form.png b/website/public/static/img/aniimo/turbo__basic_form.png
new file mode 100644
index 0000000..8fc41c1
Binary files /dev/null and b/website/public/static/img/aniimo/turbo__basic_form.png differ
diff --git a/website/public/static/img/aniimo/turbo__cloudy_mist_form.png b/website/public/static/img/aniimo/turbo__cloudy_mist_form.png
new file mode 100644
index 0000000..fb54b25
Binary files /dev/null and b/website/public/static/img/aniimo/turbo__cloudy_mist_form.png differ
diff --git a/website/public/static/img/aniimo/turbo__deluge_form.png b/website/public/static/img/aniimo/turbo__deluge_form.png
new file mode 100644
index 0000000..3602be9
Binary files /dev/null and b/website/public/static/img/aniimo/turbo__deluge_form.png differ
diff --git a/website/public/static/img/aniimo/turbo__plateau_form.png b/website/public/static/img/aniimo/turbo__plateau_form.png
new file mode 100644
index 0000000..f050463
Binary files /dev/null and b/website/public/static/img/aniimo/turbo__plateau_form.png differ
diff --git a/website/public/static/img/aniimo/turbo__prismana_form.png b/website/public/static/img/aniimo/turbo__prismana_form.png
new file mode 100644
index 0000000..e2f9460
Binary files /dev/null and b/website/public/static/img/aniimo/turbo__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/veilfloat__basic_form.png b/website/public/static/img/aniimo/veilfloat__basic_form.png
new file mode 100644
index 0000000..a612ae2
Binary files /dev/null and b/website/public/static/img/aniimo/veilfloat__basic_form.png differ
diff --git a/website/public/static/img/aniimo/waleetle__basic_form.png b/website/public/static/img/aniimo/waleetle__basic_form.png
new file mode 100644
index 0000000..18e895f
Binary files /dev/null and b/website/public/static/img/aniimo/waleetle__basic_form.png differ
diff --git a/website/public/static/img/aniimo/waleetle__prismana_form.png b/website/public/static/img/aniimo/waleetle__prismana_form.png
new file mode 100644
index 0000000..08d71ab
Binary files /dev/null and b/website/public/static/img/aniimo/waleetle__prismana_form.png differ
diff --git a/website/public/static/img/aniimo/waleetle__snowfield_form.png b/website/public/static/img/aniimo/waleetle__snowfield_form.png
new file mode 100644
index 0000000..57b2f3b
Binary files /dev/null and b/website/public/static/img/aniimo/waleetle__snowfield_form.png differ
diff --git a/website/public/static/img/aniimo/water.png b/website/public/static/img/aniimo/water.png
new file mode 100644
index 0000000..3772400
Binary files /dev/null and b/website/public/static/img/aniimo/water.png differ
diff --git a/website/public/static/img/aniimo/wind.png b/website/public/static/img/aniimo/wind.png
new file mode 100644
index 0000000..31ae9e0
Binary files /dev/null and b/website/public/static/img/aniimo/wind.png differ
diff --git a/website/public/static/img/aniimo/wisptis__basic_form.png b/website/public/static/img/aniimo/wisptis__basic_form.png
new file mode 100644
index 0000000..bbd8f04
Binary files /dev/null and b/website/public/static/img/aniimo/wisptis__basic_form.png differ
diff --git a/website/public/static/img/aniimo/wisptis__forest_form.png b/website/public/static/img/aniimo/wisptis__forest_form.png
new file mode 100644
index 0000000..2f840b0
Binary files /dev/null and b/website/public/static/img/aniimo/wisptis__forest_form.png differ
diff --git a/website/public/static/img/aniimo/wisptis__highlands.png b/website/public/static/img/aniimo/wisptis__highlands.png
new file mode 100644
index 0000000..7d9ca83
Binary files /dev/null and b/website/public/static/img/aniimo/wisptis__highlands.png differ
diff --git a/website/public/static/img/aniimo/witchin__basic_form.png b/website/public/static/img/aniimo/witchin__basic_form.png
new file mode 100644
index 0000000..b017a68
Binary files /dev/null and b/website/public/static/img/aniimo/witchin__basic_form.png differ
diff --git a/website/public/static/img/aniimo/witchin__mountain_form.png b/website/public/static/img/aniimo/witchin__mountain_form.png
new file mode 100644
index 0000000..6793ed1
Binary files /dev/null and b/website/public/static/img/aniimo/witchin__mountain_form.png differ
diff --git a/website/public/static/img/aniimo/witchin__prismana_form.png b/website/public/static/img/aniimo/witchin__prismana_form.png
new file mode 100644
index 0000000..4a6f081
Binary files /dev/null and b/website/public/static/img/aniimo/witchin__prismana_form.png differ
diff --git a/website/public/static/img/games/aniimo/card-cover.png b/website/public/static/img/games/aniimo/card-cover.png
new file mode 100644
index 0000000..29f0c2f
Binary files /dev/null and b/website/public/static/img/games/aniimo/card-cover.png differ
diff --git a/website/public/static/img/games/aniimo/hero-bg.png b/website/public/static/img/games/aniimo/hero-bg.png
new file mode 100644
index 0000000..c1f0b42
Binary files /dev/null and b/website/public/static/img/games/aniimo/hero-bg.png differ
diff --git a/website/src/features/games/GameRoute.jsx b/website/src/features/games/GameRoute.jsx
index 6574fcd..6fa53c4 100644
--- a/website/src/features/games/GameRoute.jsx
+++ b/website/src/features/games/GameRoute.jsx
@@ -1,4 +1,5 @@
// Rôle : route vers les pages de jeux disponibles et leurs sous-pages.
+import { AniimoPage } from "./aniimo/AniimoPage.jsx";
import { GamesPage } from "./GamesPage.jsx";
import { Diablo4Page } from "./diablo4/Diablo4Page.jsx";
import { MhwildsPage } from "./mhwilds/MhwildsPage.jsx";
@@ -9,6 +10,7 @@ export function GameRoute(props) {
const heroStyle = game?.images?.heroBg ? { "--game-hero-image": `url("${game.images.heroBg}")` } : undefined;
if (!game) return
;
+ if (game.id === "aniimo") return
;
if (game.id === "diablo4") return
;
if (game.id === "mhwilds") return
;
diff --git a/website/src/features/games/GuideChecklistControls.jsx b/website/src/features/games/GuideChecklistControls.jsx
new file mode 100644
index 0000000..3ed4ec1
--- /dev/null
+++ b/website/src/features/games/GuideChecklistControls.jsx
@@ -0,0 +1,35 @@
+// Rôle : affiche le sélecteur de checklist et le masquage commun aux pages guides.
+import { Icon } from "../../components/Icon.jsx";
+import { useGuideChecklist } from "./useGuideChecklist.js";
+
+export function CurrentGuideChecklistControls({ gameId }) {
+ const checklist = useGuideChecklist();
+ return checklist.available && checklist.guideId.split("/")[0] === gameId ?
: null;
+}
+
+export function GuideChecklistControls({ checklist }) {
+ const { content, toolbox, selected, lists, hideCompleted } = checklist;
+ return
+ {!toolbox ?
: <>
+
+ {!lists.length &&
{content.noCompatible}
}
+ {selected && <>
+
+
+
+
+ {!selected.count &&
{content.noMatches}
}
+ >}
+ >}
+
;
+}
+
+export function GuideChecklistMark({ completed, label }) {
+ return completed ?
✓ : null;
+}
diff --git a/website/src/features/games/aniimo/AniimoCard.jsx b/website/src/features/games/aniimo/AniimoCard.jsx
new file mode 100644
index 0000000..ff2de3a
--- /dev/null
+++ b/website/src/features/games/aniimo/AniimoCard.jsx
@@ -0,0 +1,110 @@
+// Rôle : affiche une carte Aniimo retournable et les onglets des variantes correspondantes.
+import { useId, useLayoutEffect, useRef, useState } from "react";
+import { GuideChecklistMark } from "../GuideChecklistControls.jsx";
+import { Icon } from "../../../components/Icon.jsx";
+import { Tooltip } from "../../../components/Tooltip.jsx";
+import { ANIIMO_STATS, aniimoAsset } from "./utils.js";
+
+function Attributes({ values, t }) {
+ return
{values.map((value) =>
{t(value)})};
+}
+
+function shortForm(label) {
+ const words = label.split(/[\s’-]+/).filter((word) => !/^(forme|de|des|du|la|le|les|d)$/i.test(word));
+ // Trois lettres distinguent notamment les plateaux, prairies et prismana.
+ return (words.length === 1 ? words[0].slice(0, 3) : words.map((word) => word[0]).join("")).toLocaleUpperCase("fr");
+}
+
+function CardFaces({ variant, t, content, completed, completedLabel, statsMax }) {
+ const [flipped, setFlipped] = useState(false);
+ const frontButton = useRef(null);
+ const backButton = useRef(null);
+ const focusAfterFlip = useRef(false);
+ useLayoutEffect(() => {
+ if (!focusAfterFlip.current) return;
+ (flipped ? backButton : frontButton).current?.focus({ preventScroll: true });
+ focusAfterFlip.current = false;
+ }, [flipped]);
+ function flip() {
+ focusAfterFlip.current = true;
+ setFlipped((value) => !value);
+ }
+ const flipButton = (back, ref) =>
;
+ return (
+
+
+
+ {flipButton(false, frontButton)}
+
+
+
+
{t(variant.name)}
+
{t(variant.form)}
+
{variant.elements.map((element) =>
)}
+
+
+
+ {flipButton(true, backButton)}
+
+
+
{t(variant.name)}
+
{t(variant.form)}
+
+
+
+
{content.abilities}
+
{variant.homeland_abilities.map((ability) => -
+
+ {t(ability.type)}
+ {content.level} {ability.level}
+ )}
+
{content.statsTitle}
+
+ {ANIIMO_STATS.map((key) =>
+ {content.stats[key]}
+
+
+
+ {variant.attributes.stats[key]}
+
)}
+
{content.statsTotal}{variant.attributes.total}
+
+
+
+
+
+ );
+}
+
+export function AniimoCard({ variants, t, content, isCompleted, completedLabel, statsMax }) {
+ const id = useId();
+ const [selectedKey, setSelectedKey] = useState(variants[0].key);
+ const variant = variants.find((item) => item.key === selectedKey) || variants[0];
+ // Mémoriser le repli pour ne pas restaurer un ancien onglet lorsque le filtre est retiré.
+ if (selectedKey !== variant.key) setSelectedKey(variant.key);
+ const multiple = variants.length > 1;
+ function selectTab(index, target) {
+ setSelectedKey(variants[index].key);
+ target.parentElement.children[index].focus();
+ }
+ return (
+
+
+ {multiple &&
+ {variants.map((item, index) => setSelectedKey(item.key)} onKeyDown={(event) => {
+ const next = event.key === "ArrowRight" ? (index + 1) % variants.length : event.key === "ArrowLeft" ? (index - 1 + variants.length) % variants.length : event.key === "Home" ? 0 : event.key === "End" ? variants.length - 1 : null;
+ if (next === null) return;
+ event.preventDefault();
+ selectTab(next, event.currentTarget);
+ }}>{shortForm(t(item.form))})}
+
}
+
+
+
+
+
+ );
+}
diff --git a/website/src/features/games/aniimo/AniimoFilters.jsx b/website/src/features/games/aniimo/AniimoFilters.jsx
new file mode 100644
index 0000000..d68451f
--- /dev/null
+++ b/website/src/features/games/aniimo/AniimoFilters.jsx
@@ -0,0 +1,81 @@
+// Rôle : expose les catégories repliables et les seuils Aniimo dans un défilement commun.
+import { useState } from "react";
+import { Icon } from "../../../components/Icon.jsx";
+import { aniimoAsset, createAniimoFilters } from "./utils.js";
+
+function LogicChoice({ label, value, onChange, content }) {
+ return (
+
+
{content.match}
+
+ {["and", "or"].map((logic) => )}
+
+
+ );
+}
+
+export function AniimoFilters({ filters, onChange, options, t, content }) {
+ const [collapsed, setCollapsed] = useState({});
+ const abilityOptions = [...options.abilities].sort((a, b) => Number(!options.elements.includes(a)) - Number(!options.elements.includes(b)));
+ function toggle(key, option, checked) {
+ onChange({ ...filters, [key]: checked ? [...filters[key], option] : filters[key].filter((value) => value !== option) });
+ }
+
+ return (
+
+
+
{content.filters}
{content.title}
+
+
+
+
+ {["roles", "elements", "abilities"].map((key) => (
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/website/src/features/games/aniimo/AniimoPage.jsx b/website/src/features/games/aniimo/AniimoPage.jsx
new file mode 100644
index 0000000..3629cd3
--- /dev/null
+++ b/website/src/features/games/aniimo/AniimoPage.jsx
@@ -0,0 +1,53 @@
+// Rôle : assemble l'accueil Aniimo et la grille filtrable de ses variantes.
+import { useMemo } from "react";
+import { CopyChecklistItemsButton } from "../CopyChecklistItemsButton.jsx";
+import { useGuideChecklist } from "../useGuideChecklist.js";
+import { Icon } from "../../../components/Icon.jsx";
+import { GameBreadcrumb } from "../GameBreadcrumb.jsx";
+import { AniimoCard } from "./AniimoCard.jsx";
+import { AniimoFilters } from "./AniimoFilters.jsx";
+import { getAniimoStatsMax, aniimoLabel, filterAniimoVariants, groupAniimoVariants } from "./utils.js";
+
+function AniimoListing({ game, aniimo, filters, setFilters, content }) {
+ const active = filters.aniimo;
+ const statsMax = useMemo(() => getAniimoStatsMax(aniimo.variants), [aniimo.variants]);
+ const checklist = useGuideChecklist("aniimo/aniimos");
+ const filtered = useMemo(() => filterAniimoVariants(aniimo.variants, active), [aniimo.variants, active]);
+ const visible = filtered.filter((variant) => checklist.isVisible(variant.key));
+ const groups = useMemo(() => groupAniimoVariants(visible), [visible]);
+ const totalAniimos = useMemo(() => new Set(aniimo.variants.map((variant) => variant.aniimoId)).size, [aniimo.variants]);
+ const t = (key) => aniimoLabel(aniimo.translations, key);
+ const cards = active.grouped ? groups : visible.map((variant) => [variant]);
+ const update = (next) => setFilters((current) => ({ ...current, aniimo: next }));
+ return <>
+
+
+
+
+
{content.title}
+ {groups.length} / {totalAniimos} {content.title}{" · "}{visible.length} / {aniimo.variants.length} {content.variants.toLowerCase()}
+ t(variant.name)) : visible.map((variant) => `${t(variant.name)} — ${t(variant.form)}`)} title={active.grouped ? content.copyChecklist : content.copyVariantsChecklist} />
+
+
{content.description}
+
+
+
+
+
+
+ {cards.length ? {cards.map((variants) =>
)}
: {content.emptyTitle}
{content.emptyText}
}
+
+
+ >;
+}
+
+export function AniimoPage({ category, game, aniimo, filters, setFilters, siteContent }) {
+ const content = siteContent.aniimo;
+ if (category !== "aniimos") return <>
+
{game.eyebrow}
{game.title}
{game.summary}
+
+ >;
+ if (!aniimo.loaded) return
;
+ if (aniimo.error) return
{content.error}
{aniimo.error}
;
+ return
;
+}
diff --git a/website/src/features/games/aniimo/utils.js b/website/src/features/games/aniimo/utils.js
new file mode 100644
index 0000000..cc45d8c
--- /dev/null
+++ b/website/src/features/games/aniimo/utils.js
@@ -0,0 +1,67 @@
+// Rôle : prépare les variantes Aniimo et applique les critères avant leur regroupement.
+export function createAniimoFilters() {
+ return { name: "", elements: [], roles: [], abilities: {}, elementsLogic: "and", abilitiesLogic: "and", grouped: true };
+}
+
+export const ANIIMO_STATS = ["hp", "atk", "p_def", "m_def", "break", "regen"];
+// Échelle commune fondée sur le catalogue complet, indépendante des filtres.
+export function getAniimoStatsMax(variants) {
+ return Math.max(1, ...variants.flatMap((variant) => ANIIMO_STATS.map((key) => variant.attributes.stats[key])));
+}
+
+export function aniimoLabel(translations, key) {
+ const value = translations[key] || key.replace(/_/g, " ");
+ return value.charAt(0).toUpperCase() + value.slice(1);
+}
+
+export function aniimoAsset(key) {
+ return `/static/img/aniimo/${key}.png`;
+}
+
+function normalizeName(value) {
+ return value.trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/œ/g, "oe").replace(/æ/g, "ae");
+}
+
+export function prepareAniimoData(aniimos, translations, englishTranslations) {
+ const variants = aniimos.flatMap((aniimo) => aniimo.variations.map((variation) => ({
+ ...variation,
+ aniimoId: aniimo.id,
+ name: aniimo.name,
+ key: `${aniimo.id}:${variation.form}`,
+ image: aniimoAsset(`${aniimo.name}__${variation.form}`),
+ searchNames: [aniimo.name, translations[aniimo.name], englishTranslations[aniimo.name]].filter(Boolean).map(normalizeName)
+ })));
+ const sorted = (values) => [...new Set(values)].sort((a, b) => aniimoLabel(translations, a).localeCompare(aniimoLabel(translations, b), "fr"));
+ return {
+ variants,
+ filterOptions: {
+ elements: sorted(variants.flatMap((variant) => variant.elements)),
+ roles: sorted(variants.map((variant) => variant.role)),
+ abilities: sorted(variants.flatMap((variant) => variant.homeland_abilities.map((ability) => ability.type)))
+ }
+ };
+}
+
+function matchesSelection(selected, logic, predicate) {
+ return !selected.length || (logic === "or" ? selected.some(predicate) : selected.every(predicate));
+}
+
+export function filterAniimoVariants(variants, filters) {
+ const search = normalizeName(filters.name);
+ const abilities = Object.entries(filters.abilities);
+ return variants.filter((variant) => (
+ (!search || variant.searchNames.some((name) => name.includes(search)))
+ && matchesSelection(filters.elements, filters.elementsLogic, (element) => variant.elements.includes(element))
+ && (!filters.roles.length || filters.roles.includes(variant.role))
+ && matchesSelection(abilities, filters.abilitiesLogic, ([type, minimum]) => variant.homeland_abilities.some((ability) => ability.type === type && ability.level >= minimum))
+ ));
+}
+
+export function groupAniimoVariants(variants) {
+ const groups = new Map();
+ for (const variant of variants) {
+ if (!groups.has(variant.aniimoId)) groups.set(variant.aniimoId, []);
+ groups.get(variant.aniimoId).push(variant);
+ }
+ return [...groups.values()];
+}
diff --git a/website/src/features/games/diablo4/Diablo4AffixCard.jsx b/website/src/features/games/diablo4/Diablo4AffixCard.jsx
index 5dd9f6c..55f7a33 100644
--- a/website/src/features/games/diablo4/Diablo4AffixCard.jsx
+++ b/website/src/features/games/diablo4/Diablo4AffixCard.jsx
@@ -1,9 +1,11 @@
// Rôle : affiche une carte d'affixe Diablo IV et ses catégories.
+import { GuideChecklistMark } from "../GuideChecklistControls.jsx";
import { getCategoryIconStyle, getCategoryLabel } from "./utils.js";
-export function Diablo4AffixCard({ affix, categoryMap }) {
+export function Diablo4AffixCard({ affix, categoryMap, completed, completedLabel }) {
return (
-
+
+
{affix.label}
diff --git a/website/src/features/games/diablo4/Diablo4Listing.jsx b/website/src/features/games/diablo4/Diablo4Listing.jsx
index b052835..59aad35 100644
--- a/website/src/features/games/diablo4/Diablo4Listing.jsx
+++ b/website/src/features/games/diablo4/Diablo4Listing.jsx
@@ -1,4 +1,5 @@
// Rôle : affiche et filtre la liste des affixes Diablo IV.
+import { useGuideChecklist } from "../useGuideChecklist.js";
import { useMemo } from "react";
import { CopyChecklistItemsButton } from "../CopyChecklistItemsButton.jsx";
import { GameBreadcrumb } from "../GameBreadcrumb.jsx";
@@ -9,11 +10,14 @@ import { getCategoryLabel, getFilteredDiablo4Affixes } from "./utils.js";
export function Diablo4Listing({ game, diablo4, filters, setFilters }) {
const activeFilters = filters.diablo4;
const options = [...(diablo4.filterOptions.affixes || [])].sort((a, b) => getCategoryLabel(a).localeCompare(getCategoryLabel(b), "fr"));
- const visible = useMemo(
+ const filtered = useMemo(
() => getFilteredDiablo4Affixes({ affixes: diablo4.affixes, filters: activeFilters }),
[diablo4.affixes, activeFilters]
);
+ const checklist = useGuideChecklist("diablo4/affixes");
+ const visible = filtered.filter((affix) => checklist.isVisible(affix.id));
+
return (
<>
@@ -34,7 +38,7 @@ export function Diablo4Listing({ game, diablo4, filters, setFilters }) {
{visible.length ? visible.map((affix) => (
-
+
)) : (
Aucun résultat
diff --git a/website/src/features/games/guideChecklist.js b/website/src/features/games/guideChecklist.js
new file mode 100644
index 0000000..cb13096
--- /dev/null
+++ b/website/src/features/games/guideChecklist.js
@@ -0,0 +1,58 @@
+// Rôle : reconnaît les lignes de checklist et valide les associations temporaires des guides.
+export const GUIDE_CHECKLIST_SESSION_KEY = "sokkog:guide-checklists";
+const GUIDES = new Set(["aniimo/aniimos", "mhwilds/monsters", "mhwilds/endemic", "diablo4/affixes"]);
+
+export function normalizeGuideLabel(value) {
+ return String(value || "").normalize("NFD").replace(/[\u0300-\u036f]/g, "")
+ .toLocaleLowerCase("fr").replace(/œ/g, "oe").replace(/æ/g, "ae").trim().replace(/\s+/g, " ");
+}
+
+export function matchGuideChecklist(entries, data) {
+ const rows = Array.isArray(data?.sections) ? data.sections.flatMap((section) => section.items || []) : data?.items || [];
+ const byLabel = new Map();
+ for (const row of rows) {
+ const label = normalizeGuideLabel(row.label);
+ if (!label) continue;
+ if (!byLabel.has(label)) byLabel.set(label, []);
+ byLabel.get(label).push(row);
+ }
+ const findRows = (labels = []) => [...new Set(labels.map(normalizeGuideLabel))].flatMap((label) => byLabel.get(label) || []);
+ const matched = new Set();
+ const completed = new Set();
+ for (const entry of entries) {
+ const specific = findRows(entry.labels);
+ const matches = specific.length ? specific : findRows(entry.fallbackLabels);
+ if (!matches.length) continue;
+ matched.add(entry.key);
+ if (matches.every((row) => Number(row.qtyCurrent || 0) >= Math.max(1, Number(row.qtyTarget || 1)))) completed.add(entry.key);
+ }
+ return { count: matched.size, completed };
+}
+
+export function validGuideAssociations(value, toolboxes, links) {
+ return Object.fromEntries(Object.entries(value || {}).filter(([guideId, association]) => {
+ if (!GUIDES.has(guideId) || !association || typeof association !== "object") return false;
+ const toolbox = toolboxes.find((item) => item.id === association.toolboxId && item.id === links[guideId.split("/")[0]]);
+ return toolbox?.modules.some((module) => module.id === association.moduleId && module.type === "checklist");
+ }).map(([key, association]) => [key, {
+ toolboxId: association.toolboxId, moduleId: association.moduleId, hideCompleted: association.hideCompleted === true
+ }]));
+}
+
+export function aniimoChecklistEntries(variants, translations, englishTranslations) {
+ return variants.map((variant) => {
+ const names = [translations[variant.name], englishTranslations[variant.name]].filter(Boolean);
+ const forms = [translations[variant.form], englishTranslations[variant.form]].filter(Boolean);
+ return { key: variant.key, labels: names.flatMap((name) => forms.map((form) => `${name} — ${form}`)), fallbackLabels: names };
+ });
+}
+
+export function getGuideChecklistEntries(guideId, { aniimo, mhwilds, diablo4 }) {
+ if (!GUIDES.has(guideId)) return null;
+ if (guideId === "aniimo/aniimos") return aniimo.loaded ? aniimoChecklistEntries(aniimo.variants, aniimo.translations, aniimo.englishTranslations) : null;
+ if (guideId === "diablo4/affixes") return diablo4.loaded ? diablo4.affixes.map((affix) => ({ key: affix.id, labels: [affix.label] })) : null;
+ if (!mhwilds.loaded) return null;
+ return (guideId === "mhwilds/monsters" ? mhwilds.monsters : mhwilds.endemic).map((item) => ({
+ key: item.name, labels: [mhwilds.translations[item.name] || item.name.replace(/_/g, " ")]
+ }));
+}
diff --git a/website/src/features/games/loaders.js b/website/src/features/games/loaders.js
index 534f55d..96531fa 100644
--- a/website/src/features/games/loaders.js
+++ b/website/src/features/games/loaders.js
@@ -1,4 +1,6 @@
// Rôle : charge et normalise les données publiques des jeux.
+import { prepareAniimoData } from "./aniimo/utils.js";
+
export const INITIAL_MHWILDS_STATE = {
loaded: false,
loading: false,
@@ -125,3 +127,23 @@ export async function loadDiablo4Data(signal) {
filterOptionKeys: { affixes: filterKey }
};
}
+
+export const INITIAL_ANIIMO_STATE = {
+ loaded: false,
+ loading: false,
+ error: "",
+ translations: {},
+ variants: [],
+ filterOptions: { elements: [], roles: [], abilities: [] }
+};
+
+export async function loadAniimoData(signal) {
+ const responses = await Promise.all([
+ fetch("/data/aniimo/aniimos.json", { signal }),
+ fetch("/data/aniimo/i18n/fr.json", { signal }),
+ fetch("/data/aniimo/i18n/en.json", { signal })
+ ]);
+ if (responses.some((response) => !response.ok)) throw new Error("Les données Aniimo sont indisponibles.");
+ const [aniimos, translations, englishTranslations] = await Promise.all(responses.map((response) => response.json()));
+ return { loaded: true, loading: false, error: "", translations, englishTranslations, ...prepareAniimoData(aniimos, translations, englishTranslations) };
+}
diff --git a/website/src/features/games/mhwilds/MhwildsListing.jsx b/website/src/features/games/mhwilds/MhwildsListing.jsx
index 8a1d307..191e12a 100644
--- a/website/src/features/games/mhwilds/MhwildsListing.jsx
+++ b/website/src/features/games/mhwilds/MhwildsListing.jsx
@@ -1,4 +1,5 @@
// Rôle : affiche les listes filtrables de monstres et de faune Monster Hunter Wilds.
+import { useGuideChecklist } from "../useGuideChecklist.js";
import { useMemo } from "react";
import { GameBreadcrumb } from "../GameBreadcrumb.jsx";
import { EndemicCard } from "./cards/EndemicCard.jsx";
@@ -14,7 +15,9 @@ export function MhwildsListing({ category, game, mhwilds, filters, setFilters, t
const options = (mhwilds.filterOptions?.[category]?.length ? mhwilds.filterOptions[category] : getUniqueConditionValues(items, filterKey, t))
.filter((value) => value !== "none")
.sort((a, b) => t(a).localeCompare(t(b), "fr"));
- const visible = useMemo(() => getFilteredMhwildsItems({ category, filterKey, mhwilds, filters, t }), [category, filterKey, mhwilds, filters, t]);
+ const filtered = useMemo(() => getFilteredMhwildsItems({ category, filterKey, mhwilds, filters, t }), [category, filterKey, mhwilds, filters, t]);
+ const checklist = useGuideChecklist(`mhwilds/${category}`);
+ const visible = filtered.filter((item) => checklist.isVisible(item.name));
const label = isMonsters ? t("monsters", { capitalize: true }) : t("endemic_life", { capitalize: true });
return (
@@ -42,8 +45,8 @@ export function MhwildsListing({ category, game, mhwilds, filters, setFilters, t
{visible.length ? visible.map((item) => (
isMonsters
- ?
- :
+ ?
+ :
)) : (
Aucun résultat
diff --git a/website/src/features/games/mhwilds/cards/EndemicCard.jsx b/website/src/features/games/mhwilds/cards/EndemicCard.jsx
index 5cca3e3..2e757e4 100644
--- a/website/src/features/games/mhwilds/cards/EndemicCard.jsx
+++ b/website/src/features/games/mhwilds/cards/EndemicCard.jsx
@@ -1,8 +1,9 @@
// Rôle : affiche une carte de faune endémique Monster Hunter Wilds.
+import { GuideChecklistMark } from "../../GuideChecklistControls.jsx";
import { IconRow } from "./IconRow.jsx";
import { assetPath, getConditionValues, normalizeText } from "../utils.js";
-export function EndemicCard({ item, t }) {
+export function EndemicCard({ item, t, completed, completedLabel }) {
const locations = getConditionValues(item.locations);
const description = t(item.description);
const normalizedDescription = normalizeText(description);
@@ -11,7 +12,8 @@ export function EndemicCard({ item, t }) {
&& normalizedDescription !== normalizeText("to be completed");
return (
-
+
+
diff --git a/website/src/features/games/mhwilds/cards/MonsterCard.jsx b/website/src/features/games/mhwilds/cards/MonsterCard.jsx
index 48be028..5fe69d9 100644
--- a/website/src/features/games/mhwilds/cards/MonsterCard.jsx
+++ b/website/src/features/games/mhwilds/cards/MonsterCard.jsx
@@ -1,10 +1,11 @@
// Rôle : affiche une carte monstre retournable avec faiblesses et hitzones.
+import { GuideChecklistMark } from "../../GuideChecklistControls.jsx";
import { useState } from "react";
import { DamageTable } from "./DamageTable.jsx";
import { IconRow } from "./IconRow.jsx";
import { assetPath, getConditionValues } from "../utils.js";
-export function MonsterCard({ monster, t }) {
+export function MonsterCard({ monster, t, completed, completedLabel }) {
const [flipped, setFlipped] = useState(false);
const weaknesses = getConditionValues(monster.weaknesses);
const ailments = getConditionValues(monster.ailments).filter((value) => value !== "none");
@@ -12,6 +13,7 @@ export function MonsterCard({ monster, t }) {
return (
setFlipped(!flipped)}
onKeyDown={(event) => {
if (event.key !== "Enter" && event.key !== " ") return;
@@ -21,10 +23,11 @@ export function MonsterCard({ monster, t }) {
tabIndex="0"
role="button"
aria-pressed={flipped}
- aria-label={`Afficher les dégâts de ${t(monster.name, { capitalize: true })}`}
+ aria-label={`Afficher les dégâts de ${t(monster.name, { capitalize: true })}${completed ? ` — ${completedLabel}` : ""}`}
>
+
@@ -39,6 +42,7 @@ export function MonsterCard({ monster, t }) {
+
diff --git a/website/src/features/games/useGuideChecklist.js b/website/src/features/games/useGuideChecklist.js
new file mode 100644
index 0000000..ec725d4
--- /dev/null
+++ b/website/src/features/games/useGuideChecklist.js
@@ -0,0 +1,30 @@
+// Rôle : partage la progression réactive et les commandes de liaison entre les guides.
+import { createContext, useContext, useMemo } from "react";
+import { getGuideChecklistEntries, matchGuideChecklist } from "./guideChecklist.js";
+
+export const GuideChecklistContext = createContext(null);
+
+export function useGuideChecklist(requestedGuideId) {
+ const { toolboxes, links, moduleData, guideChecklistSession, actions, siteContent, route, aniimo, mhwilds, diablo4 } = useContext(GuideChecklistContext);
+ const guideId = requestedGuideId || route.replace(/^\/games\//, "");
+ const entries = useMemo(() => getGuideChecklistEntries(guideId, { aniimo, mhwilds, diablo4 }), [guideId, aniimo, mhwilds, diablo4]);
+ const gameId = guideId.split("/")[0];
+ const content = siteContent.guideChecklist;
+ const toolbox = toolboxes.find((item) => item.id === links[gameId]);
+ const association = guideChecklistSession.associations[guideId];
+ const lists = useMemo(() => (toolbox?.modules || []).filter((module) => module.type === "checklist").map((module, index) => ({
+ id: module.id, title: module.title || `${content.untitled} ${index + 1}`,
+ ...matchGuideChecklist(entries || [], moduleData[`${toolbox.id}:${module.id}`])
+ })), [entries, toolbox, moduleData, content.untitled]);
+ const selected = lists.find((list) => list.id === association?.moduleId);
+ const hideCompleted = Boolean(selected && association.hideCompleted);
+ const isCompleted = (key) => Boolean(selected?.completed.has(key));
+ return {
+ content, toolbox, selected, hideCompleted, isCompleted, guideId, available: entries !== null,
+ lists: lists.filter((list) => list.count || list.id === selected?.id),
+ isVisible: (key) => !hideCompleted || !isCompleted(key),
+ associate: (moduleId) => guideChecklistSession.setAssociation(guideId, moduleId ? { toolboxId: toolbox.id, moduleId, hideCompleted } : null),
+ toggleHidden: () => guideChecklistSession.setAssociation(guideId, { ...association, hideCompleted: !hideCompleted }),
+ linkToolbox: () => actions.setLinkModalGameId(gameId)
+ };
+}
diff --git a/website/src/features/toolboxes/ToolboxPages.jsx b/website/src/features/toolboxes/ToolboxPages.jsx
index 8ac1cbd..556a139 100644
--- a/website/src/features/toolboxes/ToolboxPages.jsx
+++ b/website/src/features/toolboxes/ToolboxPages.jsx
@@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "react";
import { GuidedTooltipTour } from "../../components/GuidedTooltipTour.jsx";
import { Icon } from "../../components/Icon.jsx";
import { ImportButton } from "../../components/ImportButton.jsx";
+import { CurrentGuideChecklistControls } from "../games/GuideChecklistControls.jsx";
import { StorageQuota } from "../../components/StorageQuota.jsx";
import { ToastPositionSwitch } from "../../components/ToastPositionSwitch.jsx";
import { useGroupedReorder } from "../../hooks/useGroupedReorder.js";
@@ -399,6 +400,7 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
+
{toolbox ? : (
Aucune toolbox associée à cette page jeu.
)}
diff --git a/website/src/hooks/useAppData.js b/website/src/hooks/useAppData.js
index 66f21ec..1a399e2 100644
--- a/website/src/hooks/useAppData.js
+++ b/website/src/hooks/useAppData.js
@@ -1,6 +1,6 @@
// Rôle : charge le contenu éditorial et les données publiques nécessaires à l'application.
import { useEffect, useState } from "react";
-import { INITIAL_DIABLO4_STATE, INITIAL_MHWILDS_STATE, loadDiablo4Data, loadMhwildsData } from "../features/games/loaders.js";
+import { INITIAL_ANIIMO_STATE, loadAniimoData, INITIAL_DIABLO4_STATE, INITIAL_MHWILDS_STATE, loadDiablo4Data, loadMhwildsData } from "../features/games/loaders.js";
function useGameData(active, initialState, load) {
const [requested, setRequested] = useState(active);
@@ -30,6 +30,7 @@ export function useAppData(route) {
const [games, setGames] = useState([]);
const [gamesError, setGamesError] = useState("");
const mhwilds = useGameData(route.startsWith("/games/mhwilds"), INITIAL_MHWILDS_STATE, loadMhwildsData);
+ const aniimo = useGameData(route.startsWith("/games/aniimo"), INITIAL_ANIIMO_STATE, loadAniimoData);
const diablo4 = useGameData(route.startsWith("/games/diablo4"), INITIAL_DIABLO4_STATE, loadDiablo4Data);
useEffect(() => {
@@ -64,6 +65,7 @@ export function useAppData(route) {
games,
gamesError,
mhwilds,
- diablo4
+ diablo4,
+ aniimo
};
}
diff --git a/website/src/hooks/useGameFilters.js b/website/src/hooks/useGameFilters.js
index fb527cf..ee464c9 100644
--- a/website/src/hooks/useGameFilters.js
+++ b/website/src/hooks/useGameFilters.js
@@ -1,8 +1,10 @@
// Rôle : gère l'état des filtres actifs pour les pages de jeu.
import { useState } from "react";
+import { createAniimoFilters } from "../features/games/aniimo/utils.js";
export function useGameFilters() {
return useState({
+ aniimo: createAniimoFilters(),
monsters: { name: "", weaknesses: [], logic: "and" },
endemic: { name: "", locations: [], logic: "and" },
diablo4: { name: "", categories: [], logic: "and" }
diff --git a/website/src/hooks/useGuideChecklistSession.js b/website/src/hooks/useGuideChecklistSession.js
new file mode 100644
index 0000000..270cecf
--- /dev/null
+++ b/website/src/hooks/useGuideChecklistSession.js
@@ -0,0 +1,34 @@
+// Rôle : conserve les liaisons guide/checklist pendant la session et retire les références supprimées.
+import { useEffect, useMemo, useState } from "react";
+import { GUIDE_CHECKLIST_SESSION_KEY, validGuideAssociations } from "../features/games/guideChecklist.js";
+
+function readSession() {
+ try {
+ const value = JSON.parse(sessionStorage.getItem(GUIDE_CHECKLIST_SESSION_KEY) || "{}");
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
+ } catch { return {}; }
+}
+
+export function useGuideChecklistSession({ toolboxes, links, ready }) {
+ const [associations, setAssociations] = useState(readSession);
+ const valid = useMemo(() => ready ? validGuideAssociations(associations, toolboxes, links) : associations, [associations, toolboxes, links, ready]);
+ if (ready && Object.keys(valid).length !== Object.keys(associations).length) setAssociations(valid);
+
+ useEffect(() => {
+ if (!ready) return;
+ try {
+ if (Object.keys(valid).length) sessionStorage.setItem(GUIDE_CHECKLIST_SESSION_KEY, JSON.stringify(valid));
+ else sessionStorage.removeItem(GUIDE_CHECKLIST_SESSION_KEY);
+ } catch { /* La session reste disponible en mémoire si le navigateur refuse le stockage. */ }
+ }, [ready, valid]);
+
+ function setAssociation(guideId, association) {
+ setAssociations((current) => {
+ const next = { ...current };
+ if (association) next[guideId] = association;
+ else delete next[guideId];
+ return next;
+ });
+ }
+ return { associations: valid, setAssociation };
+}
diff --git a/website/src/main.jsx b/website/src/main.jsx
index efa33eb..03fa908 100644
--- a/website/src/main.jsx
+++ b/website/src/main.jsx
@@ -10,6 +10,8 @@ import { Icon } from "./components/Icon.jsx";
import { useIndexedToolboxes } from "./features/toolboxes/storage/useIndexedToolboxes.js";
import { useToolboxActions } from "./features/toolboxes/useToolboxActions.js";
import { useAppData } from "./hooks/useAppData.js";
+import { GuideChecklistContext } from "./features/games/useGuideChecklist.js";
+import { useGuideChecklistSession } from "./hooks/useGuideChecklistSession.js";
import { useGameFilters } from "./hooks/useGameFilters.js";
import { useToolboxAlerts } from "./hooks/useToolboxAlerts.js";
import { RouteContent } from "./router/RouteContent.jsx";
@@ -28,7 +30,7 @@ function getNotificationDuration(message, options = {}) {
function App() {
const route = useHashRoute();
- const { siteContent, siteContentError, games, gamesError, mhwilds, diablo4 } = useAppData(route);
+ const { siteContent, siteContentError, games, gamesError, mhwilds, diablo4, aniimo } = useAppData(route);
const [filters, setFilters] = useGameFilters();
const [confirmModal, setConfirmModal] = useState(null);
const [createModal, setCreateModal] = useState(null);
@@ -40,6 +42,7 @@ function App() {
const [storageError, setStorageError] = useState("");
const handleStorageError = useCallback((message) => setStorageError(message), []);
const store = useIndexedToolboxes(handleStorageError);
+ const guideChecklistSession = useGuideChecklistSession(store);
const notify = useCallback((message, options = {}) => {
setNotifications((current) => [
@@ -122,6 +125,7 @@ function App() {
}
return (
+
+
);
}
diff --git a/website/src/pages/DesignSystemPage.jsx b/website/src/pages/DesignSystemPage.jsx
index 8bfe79b..d987845 100644
--- a/website/src/pages/DesignSystemPage.jsx
+++ b/website/src/pages/DesignSystemPage.jsx
@@ -67,6 +67,15 @@ const RADIO_OPTIONS = {
};
const COMPONENTS = [
+ {
+ id: "nds-range",
+ name: "Range",
+ className: "nds-range",
+ sizes: ["sm", "md"],
+ variants: VARIANT_VALUES.none,
+ attributes: { "data-size": ["sm", "md"], "data-state": FIELD_STATE_VALUES },
+ render: ({ attributes, state }) =>
+ },
{
id: "nds-input",
name: "Input",
@@ -400,10 +409,10 @@ const PATTERN_COMPONENTS = [
id: "nds-progress",
name: "Progress",
className: "nds-progress",
- sizes: SIZE_VALUES.none,
+ sizes: ["md", "sm"],
variants: ["primary", "success", "warning", "danger"],
- attributes: { "data-variant": ["primary", "success", "warning", "danger"] },
- render: ({ attributes, variant }) =>
+ attributes: { "data-size": ["md", "sm"], "data-variant": ["primary", "success", "warning", "danger"] },
+ render: ({ attributes, variant }) =>
},
{
id: "nds-filter-panel",
@@ -661,6 +670,14 @@ function InteractiveRadioButtonGroup({ attributes, options, iconOnly = false })
);
}
+function InteractiveRange({ attributes, disabled }) {
+ const [value, setValue] = useState(1);
+ return ;
+}
+
function InteractiveStepper({ attributes, input = false }) {
const [value, setValue] = useState(2);
const increment = (step) => setValue((current) => Math.min(20, Math.max(0, current + step)));
diff --git a/website/src/styles/_aniimo.scss b/website/src/styles/_aniimo.scss
new file mode 100644
index 0000000..ea6228a
--- /dev/null
+++ b/website/src/styles/_aniimo.scss
@@ -0,0 +1,198 @@
+// Rôle : adapte les filtres et cartes Aniimo aux portraits verticaux et aux variantes.
+@use "design-system/surfaces";
+
+.aniimo-home-card img { padding: 0 15%; object-fit: cover; object-position: 50% 38%; }
+.aniimo-layout .game-results { min-width: 0; }
+.aniimo-filters { min-width: 0; }
+.aniimo-filter-sections { gap: 16px; }
+.aniimo-filter-section-surface + .aniimo-filter-section-surface { padding-top: 12px; border-top: 1px solid var(--color-border); }
+.aniimo-filter-section {
+ min-width: 0;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ legend { width: 100%; padding: 0; margin-bottom: 6px; }
+}
+.aniimo-filter-toggle.nds-button {
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
+ padding-inline: 4px;
+ .ui-icon { width: 14px; height: 14px; }
+}
+.aniimo-selected-count { margin-left: auto; color: var(--color-accent-gold); }
+.aniimo-filter-options {
+ display: grid;
+ gap: 8px;
+}
+.aniimo-chip-options { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
+.aniimo-chip-options .filter-chip { min-width: 0; }
+.aniimo-ability-options { gap: 2px; }
+.aniimo-ability-filter {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(64px, 1fr) 2ch;
+ align-items: center;
+ gap: 6px;
+ font-size: var(--font-size-xs);
+ label { display: flex; align-items: center; gap: 4px; min-width: 0; white-space: nowrap; }
+ img { flex-shrink: 0; object-fit: contain; }
+ &[data-active] .aniimo-minimum-value { color: var(--color-accent-gold); font-weight: 700; }
+}
+.aniimo-minimum-value { text-align: right; font-variant-numeric: tabular-nums; }
+.aniimo-levels-hint { margin: 4px 0; color: var(--color-text-muted); font-size: var(--font-size-xs); }
+.aniimo-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr)); }
+.aniimo-result { min-width: 0; max-width: 280px; width: 100%; }
+.aniimo-tabs-slot { height: 36px; min-width: 0; padding: 2px; }
+.aniimo-tabs.nds-radio-button-group[data-size] {
+ display: grid;
+ grid-auto-columns: minmax(0, 1fr);
+ width: 100%;
+ padding: 2px;
+ button { min-width: 0; padding-inline: 0; white-space: nowrap; font-size: 11px; }
+ button[data-form="prismana_form"] {
+ position: relative;
+ background-image: linear-gradient(110deg,
+ color-mix(in srgb, var(--color-accent-pink) 16%, transparent),
+ color-mix(in srgb, var(--color-accent-gold) 16%, transparent),
+ color-mix(in srgb, var(--color-accent-cyan) 16%, transparent),
+ color-mix(in srgb, var(--color-primary) 16%, transparent));
+ &::after {
+ content: "";
+ position: absolute;
+ right: 4px;
+ bottom: 1px;
+ left: 4px;
+ height: 2px;
+ border-radius: var(--radius-pill);
+ background: linear-gradient(90deg, var(--color-accent-pink), var(--color-accent-gold), var(--color-accent-cyan), var(--color-primary));
+ opacity: 0.7;
+ pointer-events: none;
+ }
+ }
+}
+.aniimo-card { position: relative; perspective: 1200px; }
+.aniimo-card-inner {
+ @include surfaces.flip-card-inner;
+ position: relative;
+}
+.aniimo-card:hover .aniimo-card-inner,
+.aniimo-card:focus-within .aniimo-card-inner,
+.aniimo-card.is-flipped .aniimo-card-inner { will-change: transform; }
+.aniimo-card.is-flipped .aniimo-card-inner { transform: rotateY(180deg); }
+.aniimo-face.nds-card {
+ @include surfaces.card-surface(rgba(16, 20, 38, 0.92));
+ min-width: 0;
+ contain: paint;
+ overflow: hidden;
+ backface-visibility: hidden;
+}
+.aniimo-card:hover .aniimo-face { @include surfaces.flip-card-face-hover; }
+.aniimo-front { position: relative; display: flex; flex-direction: column; transform: rotateY(0deg); }
+.aniimo-front .aniimo-card-body {
+ display: flex;
+ flex-direction: column;
+ flex: 0 0 96px;
+ height: 96px;
+ padding: 8px 12px;
+ h2 { line-height: 20px; }
+ .aniimo-form {
+ flex: 1;
+ margin: 2px 0 4px;
+ line-height: 14px;
+ white-space: normal;
+ overflow: visible;
+ text-overflow: clip;
+ }
+}
+.aniimo-back {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ transform: rotateY(180deg);
+}
+.aniimo-back-heading { flex: 0 0 auto; }
+.aniimo-back-details.nds-scrollbox[data-variant="ghost"] {
+ flex: 1 1 0;
+ min-height: 0;
+ max-height: none;
+ padding: 0 12px 12px;
+ overscroll-behavior: contain;
+ scrollbar-gutter: stable;
+ h3:first-child { margin-top: 0; }
+ > h3 { margin-top: var(--space-6); }
+}
+.aniimo-portrait {
+ @include surfaces.card-cover-divider;
+ aspect-ratio: 3 / 4;
+ max-height: 250px;
+ width: 100%;
+ flex: 0 0 auto;
+ overflow: hidden;
+ img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; }
+}
+// Les deux portraits de Petit Esprit de Feu ont davantage de vide sous le personnage.
+.aniimo-result[data-aniimo="11001"] .aniimo-portrait img { object-position: center 20%; }
+.aniimo-card-body {
+ padding: 10px 12px;
+ h2 { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--font-size-base); }
+ h3 { margin: 12px 0 6px; font-size: var(--font-size-sm); }
+}
+.aniimo-back h2 { padding-right: 30px; }
+.aniimo-form { margin: 3px 0 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--color-text-muted); font-size: var(--font-size-xs); }
+.aniimo-element-icons { display: flex; align-items: center; justify-content: center; gap: 8px; height: 24px; }
+.aniimo-element-icons img { display: block; object-fit: contain; }
+.aniimo-attributes { display: flex; flex-wrap: wrap; gap: 6px; }
+.aniimo-attributes .nds-badge { display: inline-flex; align-items: center; gap: 4px; }
+.aniimo-attributes img, .aniimo-abilities img { object-fit: contain; flex-shrink: 0; }
+.aniimo-abilities {
+ display: grid;
+ gap: 8px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ li { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; align-items: center; gap: 6px; font-size: var(--font-size-sm); }
+}
+.aniimo-face .aniimo-flip {
+ @include surfaces.flip-card-indicator;
+ min-width: 0;
+ min-height: 0;
+ padding: 0;
+ .ui-icon { width: 18px; height: 18px; }
+}
+.aniimo-card:hover .aniimo-flip { @include surfaces.flip-card-indicator-hover; }
+@media (prefers-reduced-motion: reduce) {
+ .aniimo-card-inner { transition: none; }
+}
+
+.aniimo-results-count [data-copy-target="true"] { color: var(--color-accent-gold); font-weight: 700; }
+.aniimo-stats { display: grid; gap: 8px; }
+.aniimo-stat { display: grid; grid-template-columns: 66px minmax(0, 1fr) 3ch; align-items: center; gap: 6px; font-size: var(--font-size-xs); }
+.aniimo-stat-value { text-align: right; font-variant-numeric: tabular-nums; }
+.aniimo-stat-total { display: flex; justify-content: space-between; gap: 8px; font-size: var(--font-size-sm); }
+
+.aniimo-back-summary {
+ position: relative;
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--space-5);
+ &::before {
+ content: "";
+ position: absolute;
+ inset-block: 0;
+ left: 50%;
+ width: 1px;
+ background: color-mix(in srgb, var(--color-border) 50%, transparent);
+ pointer-events: none;
+ }
+ > div { min-width: 0; }
+ .nds-badge {
+ min-width: 0;
+ max-width: 100%;
+ padding: 4px 6px;
+ flex-wrap: nowrap;
+ font-size: var(--font-size-xs);
+ > span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
+ }
+ img { width: 18px; height: 18px; }
+}
diff --git a/website/src/styles/_mhwilds.scss b/website/src/styles/_mhwilds.scss
index 24dbd95..1453e58 100644
--- a/website/src/styles/_mhwilds.scss
+++ b/website/src/styles/_mhwilds.scss
@@ -52,8 +52,7 @@
position: relative;
width: 100%;
height: 100%;
- transform-style: preserve-3d;
- transition: transform 420ms var(--ease-standard);
+ @include surfaces.flip-card-inner;
}
.monster-card:hover .mhwilds-card-inner,
@@ -86,27 +85,8 @@
}
.mhwilds-flip-indicator {
- position: absolute;
- top: 10px;
- right: 10px;
- z-index: 2;
- display: grid;
- width: 34px;
- height: 34px;
- place-items: center;
- border: 1px solid rgba(165, 180, 252, 0.12);
- border-radius: var(--radius-md);
- background: rgba(7, 10, 24, 0.36);
- color: var(--color-text-muted);
+ @include surfaces.flip-card-indicator;
pointer-events: none;
- box-shadow: none;
- opacity: 0.78;
- transition:
- border-color var(--duration-fast) var(--ease-standard),
- background-color var(--duration-fast) var(--ease-standard),
- color var(--duration-fast) var(--ease-standard),
- opacity var(--duration-fast) var(--ease-standard),
- box-shadow var(--duration-fast) var(--ease-standard);
}
.mhwilds-flip-indicator .ui-icon {
@@ -123,21 +103,11 @@
}
.monster-card:hover .mhwilds-flip-indicator {
- border-color: rgba(196, 181, 253, 0.22);
- background: rgba(16, 20, 38, 0.52);
- color: #c4b5fd;
- opacity: 1;
- box-shadow: 0 0 12px rgba(139, 92, 246, 0.1);
+ @include surfaces.flip-card-indicator-hover;
}
.monster-card:hover .mhwilds-card-face {
- @include surfaces.card-surface-hover(
- rgba(16, 20, 38, 0.95),
- rgba(139, 92, 246, 0.76),
- rgba(196, 181, 253, 0.58),
- 0 0 14px rgba(139, 92, 246, 0.14),
- 0 0 24px rgba(196, 181, 253, 0.08)
- );
+ @include surfaces.flip-card-face-hover;
}
.mhwilds-card-art {
diff --git a/website/src/styles/design-system/_attributes.scss b/website/src/styles/design-system/_attributes.scss
index 8da9d25..6f0ff30 100644
--- a/website/src/styles/design-system/_attributes.scss
+++ b/website/src/styles/design-system/_attributes.scss
@@ -1,5 +1,6 @@
// Rôle : assemble l'API publique NDS par classes de famille et attributs data-*.
@use "attributes/fields";
+@use "attributes/ranges";
@use "attributes/buttons";
@use "attributes/steppers";
@use "attributes/switches";
diff --git a/website/src/styles/design-system/_controls.scss b/website/src/styles/design-system/_controls.scss
index 6027aa5..cfe4abb 100644
--- a/website/src/styles/design-system/_controls.scss
+++ b/website/src/styles/design-system/_controls.scss
@@ -1,5 +1,6 @@
// Rôle : expose les mixins publics des contrôles interactifs.
@forward "controls/fields";
+@forward "controls/ranges";
@forward "controls/buttons";
@forward "controls/steppers";
@forward "controls/switches";
diff --git a/website/src/styles/design-system/_surfaces.scss b/website/src/styles/design-system/_surfaces.scss
index 0961b0f..d01b21d 100644
--- a/website/src/styles/design-system/_surfaces.scss
+++ b/website/src/styles/design-system/_surfaces.scss
@@ -29,3 +29,49 @@
@mixin card-cover-divider {
border-bottom: 1px solid rgba(246, 196, 83, 0.42);
}
+
+@mixin flip-card-inner {
+ transform-style: preserve-3d;
+ transition: transform 420ms var(--ease-standard);
+}
+
+@mixin flip-card-indicator {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ z-index: 2;
+ display: grid;
+ width: 34px;
+ height: 34px;
+ place-items: center;
+ border: 1px solid rgba(165, 180, 252, 0.12);
+ border-radius: var(--radius-md);
+ background: rgba(7, 10, 24, 0.36);
+ color: var(--color-text-muted);
+ box-shadow: none;
+ opacity: 0.78;
+ transition:
+ border-color var(--duration-fast) var(--ease-standard),
+ background-color var(--duration-fast) var(--ease-standard),
+ color var(--duration-fast) var(--ease-standard),
+ opacity var(--duration-fast) var(--ease-standard),
+ box-shadow var(--duration-fast) var(--ease-standard);
+}
+
+@mixin flip-card-indicator-hover {
+ border-color: rgba(196, 181, 253, 0.22);
+ background: rgba(16, 20, 38, 0.52);
+ color: #c4b5fd;
+ opacity: 1;
+ box-shadow: 0 0 12px rgba(139, 92, 246, 0.1);
+}
+
+@mixin flip-card-face-hover {
+ @include card-surface-hover(
+ rgba(16, 20, 38, 0.95),
+ rgba(139, 92, 246, 0.76),
+ rgba(196, 181, 253, 0.58),
+ 0 0 14px rgba(139, 92, 246, 0.14),
+ 0 0 24px rgba(196, 181, 253, 0.08)
+ );
+}
diff --git a/website/src/styles/design-system/attributes/_patterns.scss b/website/src/styles/design-system/attributes/_patterns.scss
index 7ceffc4..5937f51 100644
--- a/website/src/styles/design-system/attributes/_patterns.scss
+++ b/website/src/styles/design-system/attributes/_patterns.scss
@@ -99,9 +99,11 @@
}
.nds-chip[data-size="sm"] {
- min-height: 34px;
- padding: 5px 10px;
+ min-height: 30px;
+ padding: 4px 8px;
+ gap: 4px;
font-size: var(--font-size-xs);
+ > img, > .ui-icon { width: 16px; height: 16px; }
}
.nds-empty-state {
@@ -128,6 +130,10 @@
@include controls.progress-pattern;
}
+.nds-progress[data-size="sm"] {
+ @include controls.progress-pattern(sm);
+}
+
.nds-progress > span,
.nds-progress-bar {
@include controls.progress-bar-pattern;
diff --git a/website/src/styles/design-system/attributes/_ranges.scss b/website/src/styles/design-system/attributes/_ranges.scss
new file mode 100644
index 0000000..669ca6c
--- /dev/null
+++ b/website/src/styles/design-system/attributes/_ranges.scss
@@ -0,0 +1,5 @@
+// Rôle : expose la primitive publique nds-range en tailles md et sm.
+@use "../controls";
+
+.nds-range { @include controls.range-control; }
+.nds-range[data-size="sm"] { @include controls.range-control(sm); }
diff --git a/website/src/styles/design-system/attributes/_states.scss b/website/src/styles/design-system/attributes/_states.scss
index 7d5f198..3582c38 100644
--- a/website/src/styles/design-system/attributes/_states.scss
+++ b/website/src/styles/design-system/attributes/_states.scss
@@ -31,6 +31,7 @@
white-space: nowrap;
}
+.nds-range:focus-visible,
.nds-input:focus-visible,
.nds-time:focus-visible,
.nds-select:focus-visible,
@@ -49,6 +50,7 @@
outline-offset: 3px;
}
+.nds-range:disabled,
.nds-button:disabled,
.nds-icon-button:disabled,
.nds-quantity-stepper button:disabled,
diff --git a/website/src/styles/design-system/controls/_patterns.scss b/website/src/styles/design-system/controls/_patterns.scss
index 567f6e1..69662c8 100644
--- a/website/src/styles/design-system/controls/_patterns.scss
+++ b/website/src/styles/design-system/controls/_patterns.scss
@@ -205,10 +205,11 @@
}
}
-@mixin progress-pattern {
+@mixin progress-pattern($size: md) {
display: block;
width: 100%;
- height: 8px;
+ height: var(--space-2);
+ @if $size == sm { height: calc(var(--space-2) / 2); }
overflow: hidden;
border: 1px solid rgba(165, 180, 252, 0.1);
border-radius: var(--radius-pill);
diff --git a/website/src/styles/design-system/controls/_ranges.scss b/website/src/styles/design-system/controls/_ranges.scss
new file mode 100644
index 0000000..c0f0aa0
--- /dev/null
+++ b/website/src/styles/design-system/controls/_ranges.scss
@@ -0,0 +1,48 @@
+// Rôle : définit les curseurs natifs NDS et leurs tailles sans modifier leur comportement clavier.
+@mixin range-track {
+ height: 6px;
+ border: 1px solid var(--color-border-hover);
+ border-radius: var(--radius-pill);
+ background: var(--color-primary-soft);
+}
+
+@mixin range-thumb($size) {
+ box-sizing: border-box;
+ width: $size;
+ height: $size;
+ border: 2px solid var(--color-accent-gold);
+ border-radius: 50%;
+ background: var(--color-primary);
+}
+
+@mixin range-control($size: md) {
+ $thumb: 20px;
+ $height: 44px;
+ @if $size == sm {
+ $thumb: 18px;
+ $height: 36px;
+ }
+ appearance: none;
+ display: block;
+ width: 100%;
+ min-width: 0;
+ height: $height;
+ margin: 0;
+ padding: 0;
+ border: 0;
+ border-radius: var(--radius-sm);
+ background: transparent;
+ cursor: pointer;
+ accent-color: var(--color-primary);
+
+ &::-webkit-slider-runnable-track { @include range-track; }
+ &::-moz-range-track { @include range-track; }
+ &::-webkit-slider-thumb {
+ @include range-thumb($thumb);
+ appearance: none;
+ margin-top: calc((4px - #{$thumb}) / 2);
+ }
+ &::-moz-range-thumb { @include range-thumb($thumb); }
+ &:hover:not(:disabled)::-webkit-slider-thumb { background: var(--color-primary-hover); }
+ &:hover:not(:disabled)::-moz-range-thumb { background: var(--color-primary-hover); }
+}
diff --git a/website/src/styles/games/_checklist.scss b/website/src/styles/games/_checklist.scss
new file mode 100644
index 0000000..19b2fac
--- /dev/null
+++ b/website/src/styles/games/_checklist.scss
@@ -0,0 +1,32 @@
+// Rôle : présente la liaison checklist et l'état terminé communs aux cartes de guides.
+.guide-checklist-controls {
+ display: grid;
+ align-items: center;
+ text-align: center;
+ gap: var(--space-3);
+ margin-bottom: var(--space-4);
+ padding: var(--space-3);
+ min-width: 0;
+ .field { min-width: 0; margin: 0; }
+ select { width: 100%; min-width: 0; }
+ p { margin: 0; color: var(--color-text-muted); font-size: var(--font-size-sm); }
+ .nds-switch { max-width: 100%; }
+ .guide-checklist-actions { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: var(--space-3); }
+}
+.guide-checklist-mark {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 2;
+ pointer-events: none;
+}
+.endemic-card, .diablo4-affix-card { position: relative; }
+.diablo4-affix-card .guide-checklist-mark,
+.aniimo-back .guide-checklist-mark { top: auto; left: auto; right: 10px; bottom: 10px; }
+.mhwilds-card-back .guide-checklist-mark { left: auto; right: 50px; }
+.diablo4-affix-card .diablo4-category-list { padding-right: 24px; }
+[data-checklist-complete="true"] {
+ .aniimo-back-details.nds-scrollbox[data-variant] { padding-bottom: 36px; }
+ .aniimo-portrait img, .mhwilds-card-art img { filter: grayscale(1); opacity: 0.55; }
+ .diablo4-category-icon { filter: grayscale(1); opacity: 0.55; }
+}
diff --git a/website/src/styles/games/_filters.scss b/website/src/styles/games/_filters.scss
index 31d51c9..b591b04 100644
--- a/website/src/styles/games/_filters.scss
+++ b/website/src/styles/games/_filters.scss
@@ -83,3 +83,8 @@
text-overflow: ellipsis;
white-space: nowrap;
}
+
+.filter-chip[data-size="sm"] {
+ grid-template-columns: 16px 16px minmax(0, 1fr);
+ span { font-size: inherit; }
+}
diff --git a/website/src/styles/icons/_actions.scss b/website/src/styles/icons/_actions.scss
index e1c4b8b..1ffa140 100644
--- a/website/src/styles/icons/_actions.scss
+++ b/website/src/styles/icons/_actions.scss
@@ -238,3 +238,8 @@
mask-image: url("/static/icons/back.svg");
-webkit-mask-image: url("/static/icons/back.svg");
}
+
+.ui-icon-multiple {
+ mask-image: url("/static/icons/multiples.svg");
+ -webkit-mask-image: url("/static/icons/multiples.svg");
+}
diff --git a/website/src/styles/main.scss b/website/src/styles/main.scss
index 749eabc..bf8dd67 100644
--- a/website/src/styles/main.scss
+++ b/website/src/styles/main.scss
@@ -13,5 +13,7 @@
@use "overlays";
@use "mhwilds";
@use "diablo4";
+@use "aniimo";
+@use "games/checklist";
@use "design-system/attributes";
@use "responsive";