@ix-xs/metamob.api 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1521 @@
1
+ const nodeComfort = require("@ix-xs/node-comfort");
2
+ const base = "https://beta.metamob.fr/api/v1";
3
+ const cache = require("./.cache/$");
4
+
5
+ function convertIds(data) {
6
+ if (nodeComfort.isArray(data)) {
7
+ return data.map(convertIds);
8
+ }
9
+ if (nodeComfort.isObject(data)) {
10
+ const result = {};
11
+
12
+ for (const key in data) {
13
+ if (key === "id" && nodeComfort.isString(data[key])) {
14
+ result[key] = Number(data[key]);
15
+ } else {
16
+ result[key] = convertIds(data[key]);
17
+ }
18
+ }
19
+
20
+ return result;
21
+ }
22
+
23
+ return data;
24
+ }
25
+ function gameByName(game_name) {
26
+ return cache.gameVersions.find(
27
+ (g) => g.name.toLowerCase() === game_name.toLowerCase(),
28
+ );
29
+ }
30
+ function serverByName(server_name) {
31
+ return cache.servers.find(
32
+ (s) => s.name.toLowerCase() === server_name.toLowerCase(),
33
+ );
34
+ }
35
+ function monsterTypeByName(type_name) {
36
+ return cache.monsterTypes.find(
37
+ (t) =>
38
+ t.name.en.toLowerCase() === type_name.toLowerCase() ||
39
+ t.name.es.toLowerCase() === type_name.toLowerCase() ||
40
+ t.name.fr.toLowerCase() === type_name.toLowerCase(),
41
+ );
42
+ }
43
+ function monsterByName(monster_name) {
44
+ return cache.monsters.find(
45
+ (m) =>
46
+ m.name.en.toLowerCase() === monster_name.toLowerCase() ||
47
+ m.name.es.toLowerCase() === monster_name.toLowerCase() ||
48
+ m.name.fr.toLowerCase() === monster_name.toLowerCase(),
49
+ );
50
+ }
51
+
52
+ /**
53
+ * **@ix-xs/metamob.api**
54
+ *
55
+ * L'[API Metamob](https://beta.metamob.fr/help/api) vous permet d'accéder à vos données de manière programmatique. Vous pouvez l'utiliser pour créer des outils personnalisés, des bots Discord, ou intégrer Metamob à d'autres applications.
56
+ * ___
57
+ * ## 🔐 Créer une clé API
58
+ * 1. Connectez-vous à votre compte
59
+ * 2. Accédez aux Paramètres
60
+ * 3. Dans la section Clé API, cliquez sur Générer une clé
61
+ * 4. Copiez et conservez précieusement votre clé (elle ne sera plus affichée)
62
+ * ___
63
+ * ## ⚙ Limites d'utilisation
64
+ * Pour garantir la disponibilité du service, l'API est soumise à des limites :
65
+ * * 60 requêtes par minute par clé API
66
+ * * Les requêtes au-delà de cette limite recevront une erreur 429 Too Many Requests
67
+ * * L'en-tête Retry-After indique le temps d'attente avant de pouvoir refaire une requête
68
+ * ___
69
+ * ## 📦 Structure de réponse
70
+ * Toutes les requêtes renvoient un objet JSON normalisé :
71
+ * ```js
72
+ * {
73
+ * ok: boolean, // Indique si la requête a réussi
74
+ * status: number, // Code HTTP (ex: 200, 404, 429)
75
+ * statusText: string, // Libellé du statut HTTP
76
+ * retryAfter?: number, // Présent uniquement si status = 429
77
+ * error?: string, // Présent si une erreur se produit lors de la récupération des données
78
+ * data?: Object|Object[], // Contenu retourné (absent si ok = false)
79
+ * pagination?: { // Informations de pagination (si applicables)
80
+ * total: number, // Nombre total d’éléments correspondants
81
+ * limit: number, // Nombre d’éléments renvoyés dans cette page
82
+ * offset: number, // Index du premier élément dans cette page
83
+ * },
84
+ * }
85
+ * ```
86
+ * ___
87
+ * ## Données en cache (Disk)
88
+ * Ce package intègre un **cache de données statiques** sous forme de fichiers JSON embarqués dans le module. Ces données correspondent à des informations qui changent rarement côté Metamob (par exemple listes de serveurs, types d’objets, etc.).
89
+ * ### Avantages du cache :
90
+ * * **Réduit les requêtes API** : Les données quasi-statiques sont disponibles immédiatement sans appel API
91
+ * * **Améliore les performances** : Recherche par nom directe sans passer par l'API
92
+ * * **Conversion ID ↔ Nom** : Permet de convertir les identifiants numériques en noms lisibles
93
+ *
94
+ * ✏️ Exemple : Au lieu de faire `GET /monsters/123`, vous pouvez rechercher `getMonsters({ monster_name: "Arakne" })`
95
+ * Le cache convertit automatiquement le nom en ID pour l'appel API
96
+ *
97
+ * Cela facilite grandement la gestion et rend les requêtes plus intuitives
98
+ *
99
+ * Vous pouvez aussi inverser : récupérer le nom à partir d'un ID via le cache
100
+
101
+ ### Données disponibles en cache :
102
+ * * `gameVersions` - Versions du jeu disponibles (Dofus Unity, Retro, Touch)
103
+ * * `servers` - Liste complète des serveurs par communauté
104
+ * * `monsterTypes` - Types de monstres (monstre, archimonstre, boss...)
105
+ * * `monsters` - Catalogue complet des monstres avec noms multilingues
106
+
107
+ ### Mise à jour du cache :
108
+ * Les données sont stockées localement dans des fichiers JSON, chargés au démarrage du module
109
+ * Le cache n'est **pas mis à jour automatiquement** depuis l'API
110
+ * Les mises à jour du cache sont gérées via des **nouvelles versions du package**, publiées régulièrement pour refléter les changements côté Metamob
111
+ * ___
112
+ * Exemple d'utilisation :
113
+ * ```js
114
+ * const metamobAPI = require("@ix-xs/metamob.api");
115
+ * const client = new metamobAPI("MA_CLÉ_API");
116
+ *
117
+ * const response = await client.getServers();
118
+ *
119
+ * if (!response.ok) {
120
+ * return console.error(response.retryAfter ?? response.error ?? response.statusText);
121
+ * }
122
+ * else {
123
+ * console.log(response.data);
124
+ * }
125
+ * ```
126
+ * ___
127
+ * @module @ix-xs/metamob.api
128
+ * @author ix-xs
129
+ * @see {@link https://beta.metamob.fr/help/api Metamob API Documentation}
130
+ */
131
+
132
+ module.exports = class MetamobAPI {
133
+ #api_key;
134
+
135
+ /**
136
+ * @param {string} api_key - Votre clé API
137
+ */
138
+ constructor(api_key) {
139
+ if (!api_key || !nodeComfort.isString(api_key)) {
140
+ throw new Error(`\`api_key\` parameter must be a string`);
141
+ }
142
+
143
+ this.#api_key = api_key;
144
+ }
145
+
146
+ /**
147
+ * ### Versions du jeu
148
+
149
+ * @typedef {"Dofus (Unity)"|"Dofus Retro (1.29)"|"Dofus Touch"} GameName
150
+
151
+ * @typedef {object} GameVersion
152
+ * @property {number} id
153
+ * @property {GameName} name
154
+
155
+ * @param {object} [options]
156
+ * @param {GameName} [options.game_name]
157
+
158
+ * @returns {Promise<{
159
+ * ok: boolean,
160
+ * status: number,
161
+ * statusText: string,
162
+ * error?: string,
163
+ * retryAfter?: number,
164
+ * data?: Array<GameVersion>|GameVersion,
165
+ * }>}
166
+
167
+ * @example
168
+ * getGameVersions(); // Liste toutes les versions du jeu (Array)
169
+ * getGameVersions({ game_name: "Dofus (Unity)" }); // Détails d'une version spécifique (Object)
170
+ */
171
+ async getGameVersions(options) {
172
+ let path = `${base}/game-versions`;
173
+
174
+ if (options?.game_name) {
175
+ const game = gameByName(options.game_name);
176
+
177
+ if (!game) {
178
+ throw new Error(`game_name '${options.game_name}' doesn't exist`);
179
+ }
180
+
181
+ path += `/${game.id}`;
182
+ }
183
+
184
+ let result = {};
185
+
186
+ try {
187
+ const _ = await fetch(path, {
188
+ headers: {
189
+ Authorization: `Bearer ${this.#api_key}`,
190
+ },
191
+ });
192
+
193
+ result.ok = _.ok;
194
+ result.status = _.status;
195
+ result.statusText = _.statusText;
196
+
197
+ if (_.status === 429) {
198
+ result.retryAfter = _.headers.get("Retry-After");
199
+ }
200
+
201
+ let data = {};
202
+
203
+ if (result.ok) {
204
+ data = convertIds(await _.json());
205
+ }
206
+
207
+ result = {
208
+ ...result,
209
+ ...data,
210
+ };
211
+ } catch (error) {
212
+ result.ok = false;
213
+ result.status = 500;
214
+ result.statusText = "Internal Server Error";
215
+ result.error = error.message ?? error.toString();
216
+ }
217
+
218
+ return result;
219
+ }
220
+
221
+ /**
222
+ * ### Serveurs
223
+
224
+ * @typedef {"Brial"|"Rafal"|"Salar"|"Kourial"|"Dakal"|"Mikhal"|"Imagiro"|"Hell Mina"|"Tylezia"|"Orukam"|"Tal Kasha"|"Draconiros"|"Ombre"|"Fallanster"|"Boune"|"Allisteria"|"Blair"|"Kelerog"|"Talok"|"Tiliwan"} ServerName
225
+
226
+ * @typedef {"World"|"France"} ServerCommunity
227
+
228
+ * @typedef {object} Server
229
+ * @property {number} id
230
+ * @property {ServerName} name
231
+ * @property {ServerCommunity} community
232
+ * @property {GameVersion} game_version
233
+
234
+ * @param {object} [options]
235
+ * @param {ServerName} [options.server_name]
236
+
237
+ * @returns {Promise<{
238
+ * ok: boolean,
239
+ * status: number,
240
+ * statusText: string,
241
+ * error?: string,
242
+ * retryAfter?: number,
243
+ * data?: Array<Server>|Server,
244
+ * }>}
245
+
246
+ * @example
247
+ * getServers(); // Liste tous les serveurs (Array)
248
+ * getServers({ server_name: "Brial" }); // Détails d'un serveur spécifique (Object)
249
+ */
250
+ async getServers(options) {
251
+ let path = `${base}/servers`;
252
+
253
+ if (options?.server_name) {
254
+ const server = serverByName(options.server_name);
255
+
256
+ if (!server) {
257
+ throw new Error(`server_name '${options.server_name}' doesn't exists`);
258
+ }
259
+
260
+ path += `/${server.id}`;
261
+ }
262
+
263
+ let result = {};
264
+
265
+ try {
266
+ const _ = await fetch(path, {
267
+ headers: {
268
+ Authorization: `Bearer ${this.#api_key}`,
269
+ },
270
+ });
271
+
272
+ result.ok = _.ok;
273
+ result.status = _.status;
274
+ result.statusText = _.statusText;
275
+
276
+ if (_.status === 429) {
277
+ result.retryAfter = _.headers.get("Retry-After");
278
+ }
279
+
280
+ let data = {};
281
+
282
+ if (result.ok) {
283
+ data = convertIds(await _.json());
284
+ }
285
+
286
+ result = {
287
+ ...result,
288
+ ...data,
289
+ };
290
+ } catch (error) {
291
+ result.ok = false;
292
+ result.status = 500;
293
+ result.statusText = "Internal Server Error";
294
+ result.error = error.message ?? error.toString();
295
+ }
296
+
297
+ return result;
298
+ }
299
+
300
+ /**
301
+ * ### Types de monstres
302
+
303
+ * @typedef {"monstre"|"monster"|"monstruo"|"archimonstre"|"archmonster"|"archimonstruo"|"boss"} MonsterTypeName
304
+
305
+ * @typedef {object} MonsterType
306
+ * @property {number} id
307
+ * @property {Record<"fr"|"en"|"es", MonsterTypeName>} name
308
+
309
+ * @param {object} [options]
310
+ * @param {MonsterTypeName} [options.type_name]
311
+
312
+ * @returns {Promise<{
313
+ * ok: boolean,
314
+ * status: number,
315
+ * statusText: string,
316
+ * error?: string,
317
+ * retryAfter?: number,
318
+ * data?: Array<MonsterType>|MonsterType,
319
+ * }>}
320
+
321
+ * @example
322
+ * getMonsterTypes(); // Liste tous les types de monstres (Array)
323
+ * getMonsterTypes({ type_name: "monstre" }); // Détails d'un type spécifique (Object)
324
+ */
325
+ async getMonsterTypes(options) {
326
+ let path = `${base}/monster-types`;
327
+
328
+ if (options?.type_name) {
329
+ const monsterType = monsterTypeByName(options.type_name);
330
+
331
+ if (!monsterType) {
332
+ throw new Error(`type_name '${options.type_name}' doesn't exist`);
333
+ }
334
+
335
+ path += `/${monsterType.id}`;
336
+ }
337
+
338
+ let result = {
339
+ ok: false,
340
+ status: 500,
341
+ statusText: "Internal Server Error",
342
+ };
343
+
344
+ try {
345
+ const _ = await fetch(path, {
346
+ headers: {
347
+ Authorization: `Bearer ${this.#api_key}`,
348
+ },
349
+ });
350
+
351
+ result.ok = _.ok;
352
+ result.status = _.status;
353
+ result.statusText = _.statusText;
354
+
355
+ if (_.status === 429) {
356
+ result.retryAfter = _.headers.get("Retry-After");
357
+ }
358
+
359
+ let data = {};
360
+
361
+ if (result.ok) {
362
+ data = convertIds(await _.json());
363
+ }
364
+
365
+ result = {
366
+ ...result,
367
+ ...data,
368
+ };
369
+ } catch (error) {
370
+ result.ok = false;
371
+ result.status = 500;
372
+ result.statusText = "Internal Server Error";
373
+ result.error = error.message ?? error.toString();
374
+ }
375
+
376
+ return result;
377
+ }
378
+
379
+ /**
380
+ * @typedef {"Aboub"|"Aboub"|"Abub"|"Aboudbra le Porteur"|"Abounteous the Generous"|"Abubanero el Naranja"|"Abrakadnuzar"|"Treeknidylus"|"Abrákneo el Elegido"|"Abrakanette l'Encapsulé"|"Treekonk the Stunned"|"Abrakeponerse el Optimista"|"Abrakildas le Vénérable"|"Treektamak the Loud"|"Abrakadabra el Pata de Kabra"|"Abrakine le Sombre"|"Treekness the Dark"|"Abrajinieves el Enanito"|"Abraklette le Fondant"|"Treekalack the Sad"|"Ábrakin el Oscuro"|"Abrakleur Clair"|"Light Treeckler"|"Abrajidor claro"|"Abrakleur Sombre"|"Dark Treeckler"|"Abrajidor oscuro"|"Abrakne"|"Treechnee"|"Abrakno"|"Abrakne Sombre"|"Dark Treechnee"|"Abrakno oscuro"|"Abraknyde"|"Treechnid"|"Abráknido"|"Abraknyde Ancestral"|"Ancestral Treechnid"|"Abráknido Ancestral"|"Abraknyde Sombre"|"Dark Treechnid"|"Abráknido oscuro"|"Abraknyde Vénérable"|"Venerable Treechnid"|"Abráknido venerable"|"Abrakroc l'édenté"|"Treekniddioo the Needy"|"Abrakíledo el Patas Ligeras"|"Abrinos le Clair"|"Treekstalbal the Psychic"|"Abroesidor el Navegante"|"Aerohouctor le guerrier"|"Aeroktor the Warrior"|"Aerohuctor, el Guerrero"|"Aerotrugobur le Malveillant"|"Aerogoburius the Malicious"|"Aerotrugobur, el Malvado"|"Akaka le Souillé"|"Akakaka the Dirty"|"Kakai el Ensuciado"|"Akakwa"|"Akakwa"|"Kuakai"|"Alhoui le Répondeur"|"Aftathabeep the Answerphone"|"Alhienado el Enajenado"|"Alhyène"|"Alyeena"|"Alhiena"|"Ameur la Laide"|"Amlullabeye the Dreamer"|"Amlobdovar el Movidista"|"Amlub"|"Amlub"|"Amlub"|"Aquabralak le guerrier"|"Aquabralak the Warrior"|"Aquabralak, el Guerrero"|"Aqualikros l'impitoyable"|"Aqualikros the Merciless"|"Aqualikros, el Despiadado"|"Arabord la Cruche"|"Arachma the Greek"|"Araklas Mausus el Encofiado"|"Arachitik la Souffreteuse"|"Arachnangel the Hopeful"|"Arkandinska la Lírica"|"Arakazam la Psychique"|"Arakazam the Psychic"|"Arakazam la Psíquica"|"Arakmutée"|"Arachmutated"|"Arakmutada"|"Araknawa"|"Araknawa"|"Araknawa"|"Araknay la Galopante"|"Arachnekros the Aggressive"|"Araknekros el Salvaje"|"Arakne"|"Arachnee"|"Arakna"|"Arakne Agressive"|"Aggressive Arachnee"|"Arakna Agresiva"|"Arakne des Égouts"|"Sewer Arachnee"|"Arakna de alcantarilla"|"Arakne Majeure"|"Major Arachnee"|"Arakna mayor"|"Arakne Malade"|"Sick Arachnee"|"Arakna enferma"|"Arakozette l'Intrépide"|"Arachnawar the Killinmachin"|"Araknosia el Olvidadizo"|"Arakule la Revancharde"|"Arakula the Carpature"|"Arakniry la Destripada"|"Arapex"|"Daddy Longlex"|"Arápex"|"Arapliké la Calligraphe"|"Arachiro the Calligrapher"|"Arafernalia la Calígrafa"|"Bakaglace le Congelé"|"Bakazicle the Icicle"|"Bakazhielo el Congelado"|"Bakazako"|"Bakazako"|"Bakazako"|"Bambono le Divin"|"Bambono the Holy"|"Bambono el Divino"|"Bambouské le Camouflé"|"Bambottinit the Quiet"|"Bambudín el Azteca"|"Bambouto"|"Bambooto"|"Bambuto"|"Bambouto Sacré"|"Holy Bambooto"|"Bambuto Sagrado"|"Bandapar l'Exclu"|"Bandirty the Messy"|"Banrí Mantís el Pigmentado"|"Bandit du clan des Roublards"|"Rogue Clan Bandit"|"Bandido del clan de los tymadores"|"Bandit Manchot"|"One-Armed Bandit"|"Bandido manco"|"Bandson le Tonitruant"|"Bandinamit the Explosive"|"Bandiras el zorro del clan Los Malagueños"|"Barbroussa"|"Barbrossa"|"Barbrusa"|"Barchwork le Multicolore"|"Blorko the Colourful"|"Barchwork el Multicolor"|"Barebourd le Comte"|"Barbrosskam the Chief"|"Barbrétzel el Salado"|"Bebetto l'Intellectuel"|"Bakaka the Intellectual"|"Kuapánfilo el Intelectual"|"Berger Porkass"|"Lousy Pig Shepherd"|"Pastor puerkazo"|"Betto"|"Baka"|"Kuapatán"|"Bi le Partageur"|"Biblokajin the Bald"|"Biblidiana la Controvertida"|"Biblop Coco"|"Coco Biblop"|"Biblop coco"|"Biblop Griotte"|"Morello Cherry Biblop"|"Biblop guinda"|"Biblop Indigo"|"Indigo Biblop"|"Biblop índigo"|"Biblop Reinette"|"Pippin Biblop"|"Biblop reineta"|"Bigbadaboum l'Élémentaire"|"Bigbadabooum the Elementary"|"Grambadabum el Elemental"|"Bilvoezé le Bonimenteur"|"Biblopopo the Organiser"|"Bibolsón el Anilloso"|"Bistou le Quêteur"|"Billbiblop the Great"|"Biblues el Ritmo"|"Bistou le Rieur"|"Bibloponey the Entertainer"|"Biblópera el Fantasma"|"Bitouf Aérien"|"Air Pikoko"|"Tufo aéreo"|"Bitouf des Plaines"|"Plain Pikoko"|"Tufo de las llanuras"|"Bitouf Sombre"|"Dark Pikoko"|"Tufo oscuro"|"Bitoven le Musicien"|"Pikhoven the Deaf"|"Tofofo el Blandito"|"Bizarbwork"|"Weirbwork"|"Eztrambwork"|"Black Tiwabbit"|"Black Tiwabbit"|"Black pekewabbit"|"Black Wabbit"|"Black Wabbit"|"Black wabbit"|"Blof l'Apathique"|"Blopal the Precious"|"Bloppy Reinarker la Primera Mitad"|"Blop Coco"|"Coco Blop"|"Blop coco"|"Blop Coco Royal"|"Royal Coco Blop"|"Blop Coco Real"|"Blop Griotte"|"Morello Cherry Blop"|"Blop guinda"|"Blop Griotte Royal"|"Royal Morello Cherry Blop"|"Blop Guinda Real"|"Blop Indigo"|"Indigo Blop"|"Blop índigo"|"Blop Indigo Royal"|"Royal Indigo Blop"|"Blop Índigo Real"|"Blop Multicolore Royal"|"Royal Rainbow Blop"|"Blop Multicolor Real"|"Blop Reinette"|"Pippin Blop"|"Blop reineta"|"Blop Reinette Royal"|"Royal Pippin Blop"|"Blop Reineta Real"|"Bloporte le Veule"|"Blopium the Delirious"|"Blop Inocho el Narizotas"|"Blordur l'Infect"|"Blorchid the Gorgeous"|"Blop Dylan el Ventoso"|"Blorie l'Assourdissante"|"Blopulent the Pretentious"|"Blómperman el Explosivo"|"Bonpake le Chavireur"|"Ishigood Pak the Mover"|"Paketeru el Impresionante"|"Boo"|"Mushd"|"Boo"|"Boomba"|"Boomba"|"Boomba"|"Boombata le Garde"|"Boombora the Dangerous"|"Doomba el Inimitable"|"Boostif l'Affamé"|"Mushdrill the Piercer"|"Bo'Callaghan el Trebol"|"Boudalf le Blanc"|"Gobbach the Contrapuntaler"|"Jalatintin el Reportero"|"Boudur le Raide"|"Bakeraider the Tomb"|"Paelladero Oscuro el Arrozoso"|"Boufdégou le Refoulant"|"Gobballad the Romantic"|"Jaleté el Extraterrestre"|"Bouflet le Puéril"|"Gobbalky the Stubborn"|"Jalatillo el Infantil"|"Boufton Blanc"|"White Gobbly"|"Jalatín blanco"|"Boufton Noir"|"Black Gobbly"|"Jalatín negro"|"Bouftou"|"Gobball"|"Jalató"|"Boulanger Sombre"|"Dark Baker"|"Panadero oscuro"|"Boulgourvil le Lointain"|"Gobballyhoo the Noisy"|"Jalatintanic el Hundido"|"Bouliver le Géant"|"Mopidyk the Mire"|"Lodontólogo el Sonriente"|"Boumbardier"|"Boombardier"|"Bumbardero"|"Bourbassingue"|"Miremop"|"Lodostropajo"|"Bourdard"|"Beaztinga"|"Zumbobo"|"Bourde le Maladroit"|"Blunder the Clumsy"|"Patón el Metido"|"Bourdilleu le Social"|"Buzzby the Social"|"Zumburdieu el Social"|"Braconnier"|"Poacher"|"Cazador de contrabando"|"Bramin le Bicéphale"|"Pocher the Kingponger"|"Cazafrán el Colorante"|"Brouste l'Humiliant"|"Floratio the Investigator"|"Esquenosé el Indeciso"|"Brouture"|"Rotaflor"|"Esquejika"|"Bulbambou"|"Bulbamboo"|"Bulbambú"|"Bulbiflore"|"Bulbiflor"|"Bulbiflor"|"Bulbig"|"Bulbig"|"Bulbig"|"Bulbuisson"|"Bulbush"|"Bulbomatorral"|"Buldeflore le Pénétrant"|"Bulbisonic the Penetrating"|"Bulbiftericia la Amarillenta"|"Bulgig le Danseur"|"Bulbigroov the Dancer"|"Bulbii la Creadora"|"Bulleur le Dormeur"|"Bulbamoon the Trumpeter"|"Buldamort el Serpiente"|"Bulsavon le Gonflé"|"Bulbushisu the Makisan"|"Bulbubunet la Única"|"Bwork"|"Bwork"|"Bwork"|"Bwork Archer"|"Bwork Archer"|"Bwork arquero"|"Bwork Mage"|"Bwork Magus"|"Bwork mago"|"Bworkasse le Dégoutant"|"Bworak the Bohemian"|"Bwarkgner el Magnificentista"|"Bworker"|"Bworker"|"Bworker"|"Bworkette"|"Bworkette"|"Bworka"|"Bwormage le Respectueux"|"Bworkoder the Mazter"|"Bwhork Mageneration el Precursor"|"Caboume l'Artilleur"|"Ganon the Dwarf"|"Cañón Dorzuelo el Doloroso"|"Canondorf"|"Cannon Dorf"|"Cañón dorf"|"Cavalier Porkass"|"Lousy Pig Knight"|"Caballero puerkazo"|"Cavordemal le Sorcier"|"Pygknightlion the Lousy"|"Caballagami Pueryukazo el Aburrido"|"Chafalfer l'Optimiste"|"Chafaldrag the Charming"|"Chafo el del Ocho"|"Chafemal le Bagarreur"|"Chaferanho the Essential"|"Cháferlie el Ángeles"|"Chafer"|"Chafer"|"Chafer"|"Chafer Archer"|"Chafer Archer"|"Chafer arquero"|"Chafer d'Élite"|"Elite Chafer"|"Chafer de élite"|"Chafer Draugr"|"Draugur Chafer"|"Chafer draugr"|"Chafer Fantassin"|"Chafer Foot Soldier"|"Chafer infante"|"Chafer Invisible"|"Invisible Chafer"|"Chafer invisible"|"Chafer Lancier"|"Chafer Lancer"|"Chafer lancero"|"Chaffoin le Sournois"|"Chafred the Fish"|"Chafíner Divarrio el Casposo"|"Chafmarcel le Fêtard"|"Chaferotix the Sixtininth"|"Chaferditos los Tres"|"Chafrit le Barbare"|"Chaferuption the Volcanic"|"Chafernan D'alonzo el Nano"|"Chalan le Commerçant"|"Chafermented the Drinker"|"Chagüer Langers los Coloridos"|"Chamane d'Egoutant"|"Grossewer Shaman"|"Chamán de Alcantarilla"|"Chamchie le Difficile"|"Matmushmush the Flasher"|"Champi Casso el Cúbico"|"Chamdblé le Cultivé"|"Spimushuaia the Traveller"|"Champán el Espumoso"|"Chamflay le Ballonné"|"Speedmush the Racer"|"Champlin el Cómico"|"Chamilero le Malchanceux"|"Nidsally the Mushtang"|"Champacné el Granitos"|"Chamitant le Dillettante"|"Shamassel the Off"|"Chamadkasas, las Desesperadas"|"Chamoute le Duveteux"|"Edvushmunch the Screamer"|"Champli el Sonoro"|"Champ à Gnons"|"Mush Rhume"|"Seta peleona"|"Champ Champ"|"Mush Mush"|"Champi champ"|"Champa Bleu"|"Blue Spimush"|"Champo azul"|"Champa Marron"|"Brown Spimush"|"Champo marrón"|"Champa Rouge"|"Red Spimush"|"Champo rojo"|"Champa Vert"|"Green Spimush"|"Champo verde"|"Champaknyde"|"Mushnid"|"Champáknido"|"Champayr le Disjoncté"|"Spimushtache the Hairy"|"Champolís el Astronauta"|"Champayt l'Odorant"|"Spimushty the Smelly"|"Champlomo el Soldadito"|"Champbis"|"Mush Tup"|"Champbis"|"Champmane"|"Mush Mish"|"Setador"|"Champmé le Méchant"|"Mushketeer the Loyal"|"Setal Slugdor el Exterminador"|"Champodonte"|"Mushmunch"|"Champidonte"|"Champolyon le Polyglotte"|"Mushuliet the Catapulet"|"Setsa'n Desiti la Amistosa"|"Champoul l'Illuminé"|"Romush the Montecchi"|"Chalbis el King"|"Chef Crocodaille"|"Crocodyl Chief"|"Jefe cocodrail"|"Chef de Guerre Bouftou"|"Gobball War Chief"|"Jefe de guerra jalató"|"Chêne Mou"|"Soft Oak"|"Roble Blando"|"Chevaucheur de Karne"|"Karne Rider"|"Cabalgador de Karne"|"Chevaucheur Koalak"|"Koalak Rider"|"Koalak cabalgador"|"Chevaustine le Reconstruit"|"Karnyona the Rider"|"Cabalista el Conspirador"|"Chiendanlémin l'Illusionniste"|"Warazpacho the Cherrilla"|"Merkxguerrita el Ogro"|"Chiendent"|"Warguerite"|"Marguerrita"|"Chonstip la Passagère"|"Pigoblet the Useful"|"Cochumájer el Rápido"|"Citassaté le Service"|"Jackellington the Lantewn"|"Calawino el Oriental"|"Citwouille"|"Pumpkwin"|"Calawaza"|"Cochon de Farle"|"Farle's Pig"|"Cerdo de Farle"|"Cochon de Lait"|"Piglet"|"Cochinillo"|"Codem"|"Codem"|"Codem"|"Codenlgaz le Problème"|"Codemonic the Mean"|"Codembolia el Obstructor"|"Cooleuvre"|"Grass Snake"|"Coolebra"|"Cooligane le Névrosé"|"Grasnakizanami the Ruler"|"Cooligan el Agresivo"|"Coquille Explosive"|"Explosive Shell"|"Cáscara Explosiva"|"Corailleur"|"Coralator"|"Coralador"|"Corailleur Magistral"|"Great Coralator"|"Coralador Magistral"|"Corbac"|"Crobak"|"Cuerbok"|"Corboyard l'Enigmatique"|"Kojaklator the Lollipoper"|"Cortazador el Inconformista"|"Corpat le Vampire"|"Crowmanion the Primitive"|"Cuergotismo el Febril"|"Crabe"|"Crab"|"Cangrejo"|"Crachefoux"|"Spitfoux"|"Escupefux"|"Crachefouxtre le Surpris"|"Spitfouxgolly the Surprised"|"Escupefistro el Torpedo"|"Crakmitaine le Faucheur"|"Jiminicrackler the Conscious"|"Crujaitor el Eurovisivo"|"Cramikaz le Suicidaire"|"Cracklerod the Old"|"Crujidilo Dundee el Australiano"|"Craqueboule"|"Crackrock"|"Crujibola"|"Craqueboule Poli"|"Polished Crackrock"|"Crujibola pulío"|"Craquecrac l'Endurant"|"Crickcrack the Crossfit"|"Cracrac el Resistente"|"Craqueleur"|"Crackler"|"Crujidor"|"Craqueleur des Plaines"|"Plain Crackler"|"Crujidor de las llanuras"|"Craqueleur Légendaire"|"Legendary Crackler"|"Crujidor Legendario"|"Craqueleur Poli"|"Polished Crackler"|"Crujidor pulío"|"Craquelourd"|"Cracklerge"|"Crujidolmen"|"Craquetou le Fissuré"|"Crackrodilrock the Helltune"|"Crujlieta la Veronesa"|"Craquetuss le Piquant"|"Crackrockisree the Tiger"|"Crojmeo el Veronés"|"Craraboss le Féérique"|"Krabaoly the Patient"|"Cangri-doo la Hadada"|"Crathdogue le Cruel"|"Crackedral the Majestic"|"Crujíbaro el Tzantza"|"Croc Gland"|"Whitish Fang"|"Colmillo blando"|"Croc Gland Enragé"|"Furious Whitish Fang"|"Colmillo blando rabioso"|"Crocabulia"|"Crocabulia"|"Cocabulia"|"Crocodaille"|"Crocodyl"|"Cocodrail"|"Crognan le Barbare"|"Lupisnockio the Woodwolf"|"Colmillazaqui, el Inagotable"|"Crognan le Barbare"|"Lupisnockio the Woodwolf"|"Colmillazaqui, el Inagotable"|"Crok le Beau"|"Crokdylann the Rebel"|"Jefe Cocolumbo el Detective"|"Crolnareff l'Exilé"|"Croccyx the Bummer"|"Cocodranel la perfumada"|"Cromikay le Néophyte"|"Snowhitisha the Pure"|"Colmillamoto el Omnipotente"|"Crowneille"|"Crovus"|"Crowrajo"|"Cruskof le Rustre"|"Crabaramis the One"|"Crustérix el Viajante"|"Crusmeyer le Pervers"|"Crabathos the For"|"Crusthórpal Passian el Submarino"|"Crustensyl le Pragmatique"|"Craborthos the All"|"Crustodralí el Bigotudo"|"Crustorail Kouraçao"|"Kurasso Craboral"|"Crustoral kuraçao"|"Crustorail Malibout"|"Mahlibuh Craboral"|"Crustoral malibut"|"Crustorail Morito"|"Mojeeto Craboral"|"Crustoral mohito"|"Crustorail Passaoh"|"Passaoh Craboral"|"Crustoral passaoh"|"Crustterus l'Organique"|"Crabartanian the Allforone"|"Crustoriyama el Boludo"|"Dardalaine"|"Venomica"|"Arapúas"|"Dardamel la Kidnappeuse"|"Gargamarak the Kidnapper"|"Dárdamel la Secuestradora"|"Dark Vlad"|"Dark Vlad"|"Dark Vlad"|"Déminoboule"|"Deminoball"|"Deminobola"|"Disciple Zoth"|"Zoth Disciple"|"Discípulo zoth"|"Diskord le Belliqueux"|"Ezothbeitor the Neighbour"|"Diszápulo Delzoih el Profeta"|"Dok Alako"|"Dok Alako"|"Dok alako"|"Doktopuss le Maléfique"|"Dokterwho the Tardisporter"|"DoK Ok el Gasterópodo"|"Don Dorgan"|"Dorgan Ation"|"Don dórgano"|"Don Duss Ang"|"Blodz Uker"|"Don dessangre"|"Don Kizoth l'Obstiné"|"Don Quizothe the Stubborn"|"Don Kizoth el Obstinado"|"Dragacé"|"Dragnnoyed"|"Draguirritado"|"Dragalgan l'Effervescent"|"Dragostino the Tiny"|"Dragosstinho, el Futboleiro"|"Dragdikal le Décisif"|"Dregguantico the Trainer"|"Drakójak el Piruletas"|"Drageaufol la Joyeuse"|"Dragossiper the Nag"|"Ledrag el Ognat"|"Dragioli le Succulent"|"Dragoskovit the Barefoot"|"Dragrogui el Ebrio"|"Dragioli le Succulent"|"Dragoskovit the Barefoot"|"Dragrogui, el Ebrio"|"Dragkouine la Déguisée"|"Dreggump the Shrimp"|"Dragkuín el Disfrazado"|"Draglida la Disparue"|"Dragotitis the Painful"|"Dragozart Almandreus el Prodigio"|"Dragma le Bouillant"|"Dreggooniz the Adventurous"|"Dragma, el Griego"|"Dragminster le Magicien"|"Dragorse the Wild"|"Dragoss&Dungeoss el Original"|"Dragmoclaiss le Fataliste"|"Dreggatón the Latino"|"Dragmocles el Fatalista"|"Dragnarok"|"Dragnarok"|"Dragnarok"|"Dragnostik le Sceptique"|"Dreggommomm the Chewer"|"Drajoanito el Rojo"|"Dragnoute l'Irascible"|"Drakokidoki the Volunteer"|"Dragamenón el Destructroyer"|"Dragobert le Monarque"|"Dragory the Violent"|"Dreghouse el Cínico"|"Dragodinde amande sauvage"|"Wild Almond Dragoturkey"|"Dragopavo almendrado salvaje"|"Dragodinde dorée sauvage"|"Wild Golden Dragoturkey"|"Dragopavo dorado salvaje"|"Dragodinde rousse sauvage"|"Wild Ginger Dragoturkey"|"Dragopavo pelirrojo salvaje"|"Dragoeth le Penseur"|"Dreggoog the Downunder"|"Drajorgito, el Verde"|"Dragoeuf Ardoise"|"Slate Dreggon"|"Dragohuevo pizarroso"|"Dragoeuf Argile"|"Clay Dreggon"|"Dragohuevo arcilloso"|"DragOeuf Blanc"|"White Dreggon"|"Dragohuevo Blanco"|"DragOeuf Blanc Éveillé"|"Alert White Dreggon"|"Dragohuevo Blanco Despierto"|"DragOeuf Blanc Immature"|"Immature White Dreggon"|"Dragohuevo Blanco Inmaduro"|"Dragoeuf Calcaire"|"Limestone Dreggon"|"Dragohuevo calizo"|"Dragoeuf Charbon"|"Coal Dreggon"|"Dragohuevo carbonoso"|"DragOeuf de Saphir"|"Sapphire Dreggon"|"Dragohuevo Zafiro"|"DragOeuf de Saphir Éveillé"|"Alert Sapphire Dreggon"|"Dragohuevo zafiro despierto"|"DragOeuf de Saphir Immature"|"Immature Sapphire Dreggon"|"Dragohuevo Zafiro Inmaduro"|"DragOeuf Doré"|"Golden Dreggon"|"Dragohuevo Dorado"|"DragOeuf Doré Éveillé"|"Alert Golden Dreggon"|"Dragohuevo dorado despierto"|"DragOeuf Doré Immature"|"Immature Golden Dreggon"|"Dragohuevo Dorado Inmaduro"|"Dragoeuf Guerrier"|"Dreggon Warrior"|"Dragohuevo guerrero"|"DragOeuf Noir"|"Black Dreggon"|"Dragohuevo Negro"|"DragOeuf Noir Éveillé"|"Alert Black Dreggon"|"Dragohuevo negro despierto"|"DragOeuf Noir Immature"|"Immature Black Dreggon"|"Dragohuevo Negro Inmaduro"|"Dragoeuf Volant"|"Flying Dreggon"|"Dragohuevo volador"|"Dragon Cochon"|"Dragon Pig"|"Dragocerdo"|"Dragonienne l'Econome"|"Dragangora the Softy"|"Mafaldragosa la Hermana Pequeña"|"Dragoo le Cramoisi"|"Dreggooliz the Macho"|"Drageagainst, el Máquina"|"Dragoss Ardoise"|"Slate Dragoss"|"Dragoss pizarroso"|"Dragoss Argile"|"Clay Dragoss"|"Dragoss arcilloso"|"Dragoss Blanc"|"White Dragoss"|"Dragoss Blanco"|"Dragoss Blanc Eveillé"|"Alert White Dragoss"|"Dragoss blanco despierto"|"Dragoss Calcaire"|"Limestone Dragoss"|"Dragoss calizo"|"Dragoss Charbon"|"Charcoal Dragoss"|"Dragoss carbonoso"|"Dragoss de Saphir"|"Sapphire Dragoss"|"Dragoss Zafiro"|"Dragoss de Saphir Eveillé"|"Alert Sapphire Dragoss"|"Dragoss zafiro despierto"|"Dragoss Doré"|"Golden Dragoss"|"Dragoss Dorado"|"Dragoss Doré Éveillé"|"Alert Golden Dragoss"|"Dragoss dorado despierto"|"Dragoss Noir"|"Black Dragoss"|"Dragoss Negro"|"Dragoss Noir Éveillé"|"Alert Black Dragoss"|"Dragoss negro despierto"|"Dragsta le Détendu"|"Dragoolash the Stewed"|"Drajoimito, el Azul"|"Dragstayr le Fonceur"|"Dragamemnon the Deadtroyer"|"Dreggeatón el Latino"|"Dragstik le Frustre"|"Dreggonzola the Cheesy"|"Drugmiente, la Bella"|"Dragstore le Généraliste"|"Drakween the Cross Dresser"|"Dragstor, el de la Esquina"|"Dragtarus le Bellâtre"|"Draigovsky the SocalledSwan"|"Dragoss To el Caluroso"|"Dragtonien le Malvoyant"|"Dreggrieg the Pianist"|"Draltóniko, el Ojo de Águila"|"Dragtopaile l'Excavateur"|"Dragaustin the Power"|"Dragump, el Oscarizado"|"Dragtula l'Ancien"|"Dreggershween the Tinpanalley"|"Drakaoly, la Violinista"|"Draguaindrop"|"Dragandrop"|"Dragandrop"|"Dragueuse"|"Dragostess"|"Dragosa"|"Dragybuss le Sucré"|"Dragospel the Black"|"Dragoss Pel, el Negro"|"Drakoalak"|"Drakoalak"|"Drakoalak"|"Drakolage le Tentateur"|"Drakoamax the Mad"|"Drakolakao el Sabroso"|"Draquetteur le Voleur"|"Draghouse the Cynical"|"Dragore el Sangriento"|"Ecorfé la Vive"|"Barkricrac the Unsteady"|"Dientetris el Inolvidable"|"Étoile de la Mer d'Asse"|"Starfish Trooper"|"Estrella del mar Rano"|"Étoilette la Bouchée"|"Stary the Strooper"|"Estroilette la Atascada"|"Fanburn le Viril"|"Tanuktonik the Doofdoof"|"Fantasmaik Táisunkui San el Pegador"|"Fandanleuil le Précis"|"Polterghaisk the Stray Soul"|"Fanthraks el Acomplejado"|"Fandouich l'Hautain"|"Tanukhuina the Drawer"|"Fanturo Pandez-Revértulo el Espadachín"|"Fanfancisco le Cosmopolite"|"Pandumonium the Joker"|"Fantasmeluze la Gentil"|"Fangshu"|"Fangshu"|"Fangshu"|"Fangshui la Dysorthographiée"|"Fangshui the Misspelled"|"Fangshui la Parónima"|"Fanhatur le Simple"|"TanuKiki the Deliveryghost"|"Fant-eagux el Germano"|"Fanhopruno le Gourmet"|"Satonuki the Plastikpaddy"|"Fantasmonroe el Deseo"|"Fanjipann le Sucré"|"Tanaked the Stalker"|"Fantasmator Soryonara el Baby"|"Fanjo le Pilote"|"Tanno the Dominator"|"Fredtásmer Tanukuín el Chanpion"|"Fanlabiz le Véloce"|"Aperobics the Dynamic"|"Fantasmarley el Rastafari"|"Fanlagoel le Comique"|"Miomaho the Siciliano"|"Fantasmarty Mac Flyrefux el Futurista"|"Fanlmyl l'Acuité"|"Pandoracle the Opposing Force"|"Fantasmily-Celly la Madre"|"Fansiss la Brêle"|"Tanukhiraru the Gifted"|"Fantasmanson el Familiar"|"Fansissla l'Âne"|"Leorio the Haunted"|"Fantaradona el Mágico"|"Fanstatik l'Etonnant"|"Pandipoopik the Wondrous"|"Fantarmantino el Visceral"|"Fantassein le Soldat"|"Yoksai the Spirited"|"Fantazmania el Diablo"|"Fantoch le Pantin"|"Arepotair the Bespectacled"|"Fantasma Arepopins la Niñera"|"Fantôme Apero"|"Apero Ghost"|"Fantasma de aperitubo"|"Fantôme Ardent"|"Burning Ghost"|"Fantasma ardiente"|"Fantôme Arepo"|"Arepo Ghost"|"Fantasma Arepo"|"Fantôme Aux Plates"|"Plated Ghost"|"Fantasma corazado"|"Fantôme Brave"|"Brave Ghost"|"Fantasma valiente"|"Fantôme Égérie"|"Ghost Ominjry"|"Fantasma nimado"|"Fantôme Hicide"|"Ghost Hicidal"|"Fantasma sesino"|"Fantôme Léopardo"|"Leopardo Ghost"|"Fantasma Leopardo"|"Fantôme Maho Firefoux"|"Maho Firefoux Ghost"|"Fantasma Maho Firefux"|"Fantôme Pandikaze"|"Pandikaze Ghost"|"Fantasma Pandikaze"|"Fantôme Pandore"|"Pandora Ghost"|"Fantasma Pandora"|"Fantôme Pandule"|"Pandulum Ghost"|"Fantasma Pandulo"|"Fantôme Soryo Firefoux"|"Soryo Firefoux Ghost"|"Fantasma Soryo Firefux"|"Fantôme Tanukouï San"|"Tanukouï San Ghost"|"Fantasma Tanukui San"|"Fantôme Yokai Firefoux"|"Yokai Firefoux Ghost"|"Fantasma Yokai Firefux"|"Fantrask la Rêveuse"|"Ghostabrava the Tourist"|"Fantastle Vániante la Matavampiros"|"Farlon l'Enfant"|"Pighatchoo the Electrical"|"Cerduodenitis el Abdominal"|"Fauchalak"|"Reapalak"|"Siegalak"|"Faufoll la Joyeuse"|"Ryukualak the Bored"|"Siegálaher los Chulos"|"Fécorce"|"Barkritter"|"Diente pe-león"|"Félygiène"|"Felygiene"|"Highiena"|"Félyssion la Gourmande"|"Felicity the Gormandiser"|"Highia la Golosa"|"Flammèche Air"|"Air Spark"|"Llamita aire"|"Flammèche Eau"|"Water Spark"|"Llamita agua"|"Flammèche Feu"|"Fire Spark"|"Llamita fuego"|"Flammèche Terre"|"Earth Spark"|"Llamita tierra"|"Floanna la Blonde"|"Floramodovar the Stoned"|"Florivera el Muralista"|"Floribonde"|"Floramor"|"Floribundo"|"Floriste la Cannibale"|"Floristil the Pistil"|"Florista la Caníbal"|"Floristile"|"Flowistil"|"Floristilo"|"Forboyar l'Enigmatique"|"Smitherz the Licker"|"Herranor el Pizzaiolo"|"Forgeron Sombre"|"Dark Smith"|"Herrero oscuro"|"Fossamoel le Juteux"|"Koalarchitect the Balancing Force"|"Sepaulturero Kleealak el Arquitecto"|"Fossoyeur Koalak"|"Koalak Gravedigger"|"Koalak sepulturero"|"Foufayteur"|"Foxfyter"|"Fuxfaigter"|"Fouflay le Retombé"|"Fouflay the Fallen"|"Fuflé el Definflado"|"Founamboul"|"Fouxnamballist"|"Funámbola"|"Fourapin le Chaud"|"Ambushapens the Unlucky"|"Mazomorra el Rolista"|"Fourbasse"|"Ambusher"|"Mazorral"|"Gamine Zoth"|"Zoth Girl"|"Chavala zoth"|"Gamino"|"Minokid"|"Minovillo"|"Gardienne des Égouts"|"Sewer Keeper"|"Guardiana de alcantarilla"|"Gargantua la Dévoreuse"|"Gargantua the Devourer"|"Gargantúa la Devoradora"|"Gargantûl"|"Gargantula"|"Gargántula"|"Gargrouille"|"Gargoyl"|"Gárgrola"|"Garsim le Mort"|"Gargoyla the Paranoiac"|"Pulgargrolito el Astuto"|"Gastroth la Contagieuse"|"Calipzoth the Icy"|"Chavala Zotaina la Castigadora"|"Gelanal le Huileux"|"Jellvis the King"|"Gelatiris el Arenero"|"Gelaviv le Glaçon"|"Jellyposukshion the Slim"|"Gelatina Turner la Best"|"Gelée Bleue"|"Blue Jelly"|"Gelatina Azul"|"Gelée Bleuet"|"Blueberry Jelly"|"Gelatina de Aciano"|"Gelée Fraise"|"Strawberry Jelly"|"Gelatina de fresa"|"Gelée Menthe"|"Mint Jelly"|"Gelatina de menta"|"Gelée Royale Bleue"|"Royal Blue Jelly"|"Gelatina Real Azul"|"Gelée Royale Bleuet"|"Royal Blueberry Jelly"|"Gelatina Real de Aciano"|"Gelée Royale Citron"|"Royal Lemon Jelly"|"Gelatina Real de Limón"|"Gelée Royale Fraise"|"Royal Strawberry Jelly"|"Gelatina Real de Fresa"|"Gelée Royale Menthe"|"Royal Mint Jelly"|"Gelatina Real de Menta"|"Geloliaine l'Aérien"|"Jelleno the Chinny"|"Gelazquina el Men"|"Germinol l'Indigent"|"Minoknok the Visitor"|"Minocontavan Konm'astuzia el Colorado"|"Gink"|"Gink"|"Gink"|"Ginsenk le Stimulant"|"Ginsync the Hyperactive"|"Gínsenk el Estimulante"|"Gloubibou le Gars"|"Greetdoff the Gentleman"|"Zampatávoro el Miliano"|"Gloutovore"|"Greedovore"|"Zampávoro"|"Gob-trotteur"|"Gob-Trotter"|"Gobletrotter"|"Gobelin"|"Goblin"|"Goblin"|"Gobet"|"Gobnoramus"|"Gobobo"|"Gobstiniais le Têtu"|"Goblimp the Bis Kit"|"Goyablín el Afrancesado"|"Gourlo le Terrible"|"Gourlo the Terrible"|"Gurlo el Terrible"|"Grand Pa Wabbit"|"Gwandpa Wabbit"|"Awelito wabbit"|"Grandilok le Clameur"|"Gwabbit the Wunner"|"Abustin Pawits el Bocasucia"|"Grenuche la Gentille"|"Ninnyfrog the Nice"|"Raninia la Buena"|"Grenufar"|"Nenufrog"|"Ranúfar"|"Grokosto le Bosco"|"Bignstrong the Quartermaster"|"Granfortote el Contramaestre"|"Guerrier Koalak"|"Koalak Warrior"|"Guerrero koalak"|"Guerrier Zoth"|"Zoth Warrior"|"Guerrero zoth"|"Guerrite le Veilleur"|"Chukoalak the Norris"|"Guerred-Fish el Cortés"|"Guerumoth le Collant"|"Zigzoth the Indecisive"|"Guerreynor Zothia la Superviviente"|"Hanshi"|"Hanshi"|"Hanshi"|"Haute Truche"|"Cross Strich"|"Thor Pestruz"|"Hell Mina"|"Hell Mina"|"Hell Mina"|"Ignelicrobur le Guerrier"|"Ignilicrobur the Warrior"|"Ignelicrobur, el Guerrero"|"Ignerkocropos l'Affamé"|"Ignirkocropos the Famished"|"Ignirkocropos, el Hambriento"|"Ino-Naru"|"Ino-Naru"|"Ino-naru"|"Inopenope le Négatif"|"Ino-Nope the Negative"|"Kenoikenó el Negativo"|"Ishigro Pake"|"Ishibig Pak"|"Mazaishi"|"Jiangshi-Nobi"|"Jiangshi-Nobi"|"Jiangshi-Nobi"|"Jiankor le Radoteur"|"Jianamble the Rambler"|"Samurrancyo el Senil"|"Kaenekfeu le volubile"|"Kaenekfire the Voluble"|"Kaenekfeu el Parlanchín"|"Kaeneko"|"Kaeneko"|"Kaeneko"|"Kanasukr le Mielleux"|"Kaniedoss the Giggling"|"Kánudalf el Kanoso"|"Kanibière l'Encordée"|"Kanabeer the Shaken"|"Kanubirra la Fuerte"|"Kaniblou"|"Kanazure"|"Kanublú"|"Kanigrou"|"Kaniger"|"Kanugro"|"Kannémik le Maigre"|"Kannemik the Skinny"|"Klaidíbola Cerbarrow la Otra Mitad"|"Kannibal le Lecteur"|"Kannimantha the Maneater"|"Kanníbal el Lector"|"Kanniboul Archer"|"Kanniball Archer"|"Kaníbola Arquero"|"Kanniboul Ark"|"Kanniballbo"|"Kaníbola arquero"|"Kanniboul Eth"|"Kanniball Thierry"|"Kaníbola Fipi"|"Kanniboul Jav"|"Kanniball Jav"|"Kaníbola jav"|"Kanniboul Sarbak"|"Kanniball Sarbak"|"Kaníbola cerbat"|"Kanniboul Thierry"|"Kanniball Thierry"|"Kaníbola Thierry"|"Kannisterik le Forcené"|"Kannarrie the Reckless"|"Kanibúlrich Lars el Metálico"|"Kaonashi"|"Kaonashi"|"Kaonashi"|"Kaonucléair l'Instable"|"Kaonuclear the Unstable"|"Kaonuclear el Inestable"|"Kapota la Fraise"|"Kanniranda the Maniac"|"Kanábolo el Rubio"|"Kaskapointhe la Couverte"|"Snailmetalika the Garagician"|"Kasrafantásol el Parapsicólogo"|"Kaskargo"|"Snailmet"|"Kasrakol"|"Kido"|"Kido"|"Kido"|"Kido l'Âtre"|"Kidodo the Extinct"|"Kidoloroso del Calmante"|"Kilibriss"|"Kilibriss"|"Kilibris"|"Kilimanj'haro le Grimpeur"|"Killua the Assassin"|"Kílibrill vol.2 el Vengativo"|"Kimbo"|"Kimbo"|"Kimbo"|"Kirevam"|"Kirevam"|"Kirevam"|"Kiroyal le Sirupeux"|"Kirevampiro the Wrestler"|"Kiravel el Artesano"|"Kitsou Nae"|"Kitsou Nae"|"Kitsu Nae"|"Kitsou Nakwa"|"Kitsou Nakw"|"Kitsu Nakwa"|"Kitsou Nere"|"Kitsou Nere"|"Kitsu Nere"|"Kitsou Nufeu"|"Kitsou Nufeu"|"Kitsu Fogoso"|"Kitsoudbra le Malodorant"|"Kitchy the Scratcher"|"Kitsa No el Violento"|"Kitsoufre l'Explosif"|"Kitsuey the Red"|"Kitsu Positoyo el Sanador"|"Kitsoupierre le Récipient"|"Kitsewey the Blue"|"Kitsiús el Estornudo"|"Kitsoupopulère le Généreux"|"Kitsouie the Green"|"Kitsean Cónere el Agente Secreto"|"Koakofrui le Confit"|"Koaly the Fiddler"|"Koalugok el Pato"|"Koalaboi le Calorifère"|"Koalsen the Similar"|"Koayak el Destripador"|"Koalak Coco"|"Coco Koalak"|"Koalak coco"|"Koalak Farouche"|"Wild Koalak"|"Koalak salvaje"|"Koalak Forestier"|"Koalak Forester"|"Koalak forestal"|"Koalak Griotte"|"Morello Cherry Koalak"|"Koalak guinda"|"Koalak Immature"|"Immature Koalak"|"Koalak inmaduro"|"Koalak Indigo"|"Indigo Koalak"|"Koalak índigo"|"Koalak Reinette"|"Pippin Koalak"|"Koalak reineta"|"Koalak Sanguin"|"Bloody Koalak"|"Koalak sanguíneo"|"Koalastrof la Naturelle"|"Koaldmen the Grumpy"|"Kokajín el Calvo"|"Koalvissie le Chauve"|"Koaldman the Garish"|"Koelloks el Cerealista"|"Koamaembair le Coulant"|"Jackoalak the Ripper"|"Koalalia el Mudo"|"Koamag'oel le Défiguré"|"Koelloggs the Creator"|"Botalak Cabalgator el Ingenioso"|"Koarmit la Batracienne"|"Snapoalak the Redhead"|"Koaluik el Pato"|"Koaskette la Chapelière"|"Crackoalak the Blonde"|"Koalako el Pato"|"Koasossyal le Psychopathe"|"Popoalak the Mousibrown"|"Koalúkyluk el Solitario"|"Koko la Violente"|"Komko the Vexatious"|"Kukumina la Violenta"|"Kokoko"|"Kokoko"|"Kokoko"|"Kokom"|"Kwakumber"|"Kukumi"|"Koktèle le Secoué"|"Misskokoko the Channel"|"Kokotel el Agitado"|"Kolérat"|"Kolerat"|"Kólerat"|"Kolforthe l'Indécollable"|"Koleraspootin the Anesthesialogist"|"Kolafuerte el Pegatodo"|"Koulosse"|"Koolich"|"Trankitronko"|"Krambwork"|"Burnabwork"|"Komanbwork"|"Kraméléhon"|"Khamelerost"|"Krameleón"|"Krapahut le Randonneur"|"Khameleltux the Tolerant"|"Kramelanoma el Ennegrecedor"|"Kurookin"|"Kurookin"|"Kurookin"|"Kwak de Flamme"|"Fire Kwak"|"Kwak de llamas"|"Kwak de Glace"|"Ice Kwak"|"Kwak de hielo"|"Kwak de Terre"|"Earth Kwak"|"Kwak de tierra"|"Kwak de Vent"|"Wind Kwak"|"Kwak de viento"|"Kwakamole l'Appétissant"|"Kwakamole the Appetising"|"Kwakamole el Apetitoso"|"Kwaké le Piraté"|"Kwaked the Pirated"|"Kwakeado el Pirateado"|"Kwakolak le Chocolaté"|"Kwadbury the Chocolaty"|"Kwakaolatt el Chocolateado"|"Kwakwatique le Trempé"|"Kwakwatic the Soaked"|"Kwakwático el Mojado"|"Kwoan"|"Kwoan"|"Kwoan"|"Kwoanneur le Frimeur"|"Kwoanium the Smart"|"Kwane el Ciudadano"|"La Ouassingue"|"Ouassingal"|"La stropajo"|"Larchimaide la Poussée"|"Larvadelaide the Ozie"|"Larvémming el Descerebrado"|"Larvaloeil l'Émue"|"Grubby the Tubby"|"Larvidriosa la Emocionada"|"Larvapstrè le Subjectif"|"Larvalencia the Orange"|"Larvangoj el Desorejado"|"Larve Bleue"|"Blue Larva"|"Larva azul"|"Larve Champêtre"|"Plains Larva"|"Larva campestre"|"Larve Jaune"|"Yellow Larva"|"Larva amarilla"|"Larve Orange"|"Orange Larva"|"Larva naranja"|"Larve Verte"|"Green Larva"|"Larva verde"|"Larvomatik le Propre"|"Larvamatic the Pragmatic"|"Larvado el Limpio"|"Larvonika l'Instrument"|"Larvalaska the Cold"|"Larvichuela Jack la Mágica"|"Le Flib"|"Ze Flib"|"Flib"|"Le Ouassingue"|"Ouassingue"|"El stropajo"|"Le Ouassingue Entourbé"|"Boggedown Ouassingue"|"Stropajo turbado"|"Léopardo"|"Leopardo"|"Loopardo"|"Léopolnor le Barde"|"Leopardon the Sorry"|"Looparpel el Dip"|"Let Emoliug"|"Let Emoliug"|"Let emoliug"|"Let le Rond"|"Lert Macraken the Used Emo"|"Led Empling el Ascensorista"|"Lichangora l'Immaculée"|"Lichangora the Immaculate"|"Lichangora la Inmaculada"|"Lichangoro"|"Lichangoro"|"Gorolichang"|"Lolojiki"|"Tatatojiki"|"Senojiki"|"Macien"|"Macian"|"Mata"|"Madgang le Docteur"|"Madgang the Doctor"|"Majarola el Doctor"|"Madura"|"Madura"|"Majaro"|"Maho Firefoux"|"Maho Firefoux"|"Maho Firefux"|"Mahoku le Botté"|"MoMaho the Modernist"|"Maho Firel-Tux el Paciente"|"Maître Amboat le Moqueur"|"Lord Lacedhat the Vampiric"|"Maéstrick Vaggerpiro el Canto Rodado"|"Maître Bolet"|"Fungi Master"|"Maestro boletus"|"Maître Champeur le Sabreur"|"Blackmush Master the Swordsman"|"Maestro Champavor el Espadachín"|"Maître Corbac"|"Lord Crow"|"Maestro Cuerbok"|"Maître Koalak"|"Koalak Master"|"Maestro koalak"|"Maître Koantik le Théoricien"|"Koalakropolis the King of the Hill"|"Maestro Peado la Cuenta"|"Maître Onom le Régulier"|"Fung Ku the Master"|"Maestrónomo, el Estrellado"|"Maître Pandore"|"Pandora Master"|"Maestro Pandora"|"Maître Vampire"|"Vampire Master"|"Vampiro jefe"|"Maître Zoth"|"Zoth Master"|"Maestro zoth"|"Malle Outillée"|"Equipped Chest"|"Cofre equipado"|"Mallopiée l'Épineuse"|"Quippy the Equippy Chest"|"Cofresno el Espinoso"|"Mama Koalak"|"Mama Koalak"|"Mamá koalak"|"Mamakomou l'Âge"|"Mamankalak the Bibliomaniac"|"Mamoon el Grande"|"Mandalo l'Aqueuse"|"Salamaa the Henpeck"|"Mandreinas las Nueve"|"Mandrine"|"Manderisha"|"Mandrina"|"Marude l'ensablé"|"Marude the Sandy"|"Marude la Enarenada"|"Médibwork"|"Mabwork"|"Medibwork"|"Mégabwork"|"Megabwork"|"Megabwork"|"Meulou"|"Moowolf"|"Maxilubo"|"Meupette"|"Moopet"|"Mopet"|"Meuroup le Prêtre"|"Moops the Bubbleboy"|"Mospetero el Triple"|"Milimulou"|"Miliboowolf"|"Minilubo"|"Milipatte la Griffe"|"Milivanilli the Mime"|"Mililupin el Tercero"|"Milipussien le Géant"|"Miliopold the Bloomer"|"Milirratatúi el Gastrónomo"|"Milirat d'Egoutant malade"|"Sick Grossewer Milirat"|"Milirata de alcantarillas enferma"|"Milirat Strubien"|"Strubian Milirat"|"Milirrata strubiense"|"Mineur Sombre"|"Dark Miner"|"Minero oscuro"|"Minoskito"|"Minoskito"|"Minoskito"|"Minoskour le Sauveur"|"Milikkybum the Informer"|"Mitoskorleone el Buen Padre"|"Minotoror"|"Minotoror"|"Minotauroro"|"Minotot"|"Minotot"|"Minotot"|"Minsinistre l'Elu"|"Minoskittle the Coloured"|"Minerón el Incendiario"|"Mob l'Éponge"|"Sponge Mob"|"Mob Lasponja"|"Momie Koalak"|"Koalak Mummy"|"Momia koalak"|"Momikonos la Bandelette"|"Jackoalak the Moonwalker"|"Momíller Frának el Umbrío"|"Mominotor"|"Mumminotor"|"Mominotauro"|"Moon"|"Moon"|"Moon"|"Mosketère le Dévoué"|"Moskoitus the Interruptor"|"Mashkira la Rubia de Bote"|"Moskito"|"Moskito"|"Moskito"|"Moumoule"|"Mumussel"|"Almejillón"|"Moumoute la Douce"|"Mastostroke the Strokable"|"Peluca la Suavita"|"Mufafah"|"Mufafah"|"Mufafah"|"Mufguedin le Suprême"|"Mufavabeenz the Cannibal"|"Munchfavard el Gritador"|"Mulou"|"Boowolf"|"Mediulubo"|"Muloufok l'Hilarant"|"Booty the Beast"|"Miluigi el Fontanero"|"Nakunbra"|"Hazwonarm"|"Notyebra"|"Nakuneuye le Borgne"|"Hazwonball the Hickler"|"Nakúmbat el Mortal"|"Nanashi le Virtuose"|"Nanashi the Virtuoso"|"Nanashi el Virtuoso"|"Nebgib"|"Nebgib"|"Nebgib"|"Nelvin le Boulet"|"Nebuchadnezzar the Conqueror"|"Nelgibson el Letal"|"Nerbe"|"Gwass"|"Nierba"|"Nerdeubeu le Flagellant"|"Supergwass the Free"|"Nieruba el Poeta"|"Neufedur le Flottant"|"Eyemi the Narcissist"|"Nujosawa el Emperador"|"Nipul"|"Nipul"|"Nipul"|"Nipulnislip l'Exhibitionniste"|"Niptuk the Plasticynic"|"Nipultay Dea el Poco Inspirado"|"Nodkoko"|"Kokonut"|"Nozdekoko"|"Nodkoku le Trahi"|"Kokonan the Talker"|"Nozdoku el Numérico"|"Noeul"|"Neye"|"Nujo"|"Onabu-Geisha"|"Onabu-Geisha"|"Onabu-Geisha"|"Onabuémangé la Rassasiée"|"Onabinge the Gulletfull"|"Melokomitó la Saciada"|"Oni"|"Oni"|"Oni"|"Onihylis le Destructeur"|"Oni'orses the Foolish"|"Onicienta la de Medianoche"|"Onirakam"|"Onirakam"|"Onirakam"|"Onistérique le déchainé"|"Onisterical the Unleashed"|"Onistérico el Desenfrenado"|"Orfélin"|"Orfan"|"Huerfelino"|"Orfélyre le Charmeur"|"Orfaniel the Charmer"|"Huerfeliz el Encantador"|"Osurc"|"Osurc"|"Osurc"|"Osuxion le Vampirique"|"Osurcus the Tamer"|"Osurce Kodes el Problemático"|"Ouashouash l'Exubérant"|"Ouassingiam the Tyrant"|"El Strópala Otravez el Sam"|"Ouassébo l'Esthète"|"Ouassup the Irritating"|"El Stronyjok el Pajarillo"|"Ouature la Mobile"|"Ougineemo the Lost"|"El Strat el Vampiro"|"Ougah"|"Ougaa"|"Ugah"|"Ougaould le Parasite"|"Ougathard the Fortunate"|"Uginukem, el Duque"|"Ougaould le Parasite"|"Ougathard the Fortunate"|"Uginukem el Duque"|"Ouginak"|"Ouginak"|"Uginak"|"Palmbytch la Bronzée"|"Palmella the Hefty"|"Palmiró el Tetradimensional"|"Palmiche le Serein"|"Palmoleaf the Greasy"|"Palmila la Vigilante"|"Palmiflette le Convivial"|"Naypalm the Herbivorous"|"Palmifred Passteroh el Bailarín"|"Palmifleur Kouraçao"|"Kurasso Palmflower"|"Palmiflor kuraçao"|"Palmifleur Malibout"|"Mahlibuh Palmflower"|"Palmiflor malibut"|"Palmifleur Morito"|"Mojeeto Palmflower"|"Palmiflor mohito"|"Palmifleur Passaoh"|"Passaoh Palmflower"|"Palmiflor passaoh"|"Palmito le Menteur"|"Palmpilot the Yuppie"|"Palmiscor Pions los Amantes"|"Pandalette Ivre"|"Drunk Pandalette"|"Pandita Borracha"|"Pandanlagl la Saoule"|"Pandarwin the Naturist"|"Panpítar el Niño"|"Pandawa Ivre"|"Drunken Pandawa"|"Pandawa Borracho"|"Pandikaze"|"Pandikaze"|"Pandikaze"|"Pandimaensh l'Animateur"|"Pandartmoore the Dogged"|"Pandiánayons el Aventurero"|"Pandimy le Contagieux"|"Pandaltry the Unknown"|"Pandarwin el Evolucionista"|"Pandit"|"Pandit"|"Pandido"|"Pandive le Végétarien"|"Pandahl the Rolled"|"Pan Dórrison el Hosco"|"Pandore"|"Pandora"|"Pandora"|"Pandouille le Titubant"|"Pandan the Desperate"|"Pandahl Borroaldcho el Cuentacuentos"|"Pandule"|"Pandulum"|"Pandulo"|"Pangraive le Militant"|"Pandali the Surreal"|"Pángdulo el Revienta-Bolas"|"Pantacour le Long"|"Heera Bighero"|"Tigredo el Rápido"|"Panthègros"|"Bigheera"|"Tigredón"|"Parapadkouï l'Émasculé"|"Paranotackle the Emasculated"|"Akujune el Capado"|"Parashukouï"|"Parashutackle"|"Kojonuki"|"Pékeualak"|"Fisheralak"|"Pekewalak"|"Pékeutar le Tireur"|"Fisheralf the Stewart"|"Peketchup el Hamburguesero"|"Péki Péki"|"Peki Peki"|"Peki Peki"|"Pétarfoutu le Mouillé"|"Bumbartifoux the Farty"|"Jopetas el Mojado"|"Pétartifoux"|"Bangartifoux"|"Petafux"|"Pichakoté le Dégoutant"|"Snappy the Fishfrier"|"Gazpischos el Refrescante"|"Pichdourse le Puissant"|"Snappu the Shopkeep"|"Pischurrasco el Braseado"|"Pichduitre le Totem"|"Snapple the Wise"|"Pischili Conkarne el Fuerte"|"Pichon Blanc"|"White Snapper"|"Pischis blanco"|"Pichon Bleu"|"Blue Snapper"|"Pischis azul"|"Pichon Kloune"|"Kloon Snapper"|"Pischis payaso"|"Pichon Orange"|"Brown Snapper"|"Pischis naranja"|"Pichon Vert"|"Green Snapper"|"Pischis verde"|"Picht le Brioché"|"Snappster the Sued"|"Pischto, el Tomatoso"|"Pichtoire l'Erudit"|"Snapp the Dragon"|"Pischistorra la Cárnica"|"Piou Bleu"|"Blue Piwi"|"Pío azul"|"Piou Jaune"|"Yellow Piwi"|"Pío amarillo"|"Piou Rose"|"Pink Piwi"|"Pío rosa"|"Piou Rouge"|"Red Piwi"|"Pío rojo"|"Piou Vert"|"Green Piwi"|"Pío verde"|"Piou Violet"|"Purple Piwi"|"Pío violeta"|"Pioufe la Maquillée"|"Piwi the Ermine"|"Pido el Greñas"|"Pioukas la Plante"|"Piwiliam the Brave"|"Spionter Vellde el Peligroso"|"Pioulbrineur le Mercenaire"|"Piwicker the Manly"|"Piokacho el Eléctrico"|"Pioulette la Coquine"|"Piwilde the Bossie"|"Capioricito Rojo el Forestal"|"Pioussokrim le Délétère"|"Piwinston the Churlish"|"Spío el Dragón"|"Pioustone le Problème"|"Piwiki the Witty"|"Pioch el Arenil"|"Piradain le Pingre"|"Piralhaka the Intimidator"|"Piralhaka el Maorí"|"Piralak"|"Piralak"|"Piralak"|"Pissdane l'Insipide"|"Dandel the Boy"|"Diente de Lennon el Universal"|"Pissenlit Diabolique"|"Evil Dandelion"|"Diente de león diabólico"|"Poolay"|"Cheeken"|"Pohoyo"|"Poolopo la Traditionnelle"|"Cheech the Pussycat"|"Pohozí el Jorobado"|"Porfavor le Quémandeur"|"Pigstol the Sexy"|"Pastortilla el Huevo"|"Porsalé le Râleur"|"Baconolia the Salty"|"Serranito el Montadito"|"Porsalu"|"Pignolia"|"Cerdo serrano"|"Preskapwal le Tendancieux"|"Prestreet the Fighter"|"Prestrit el Luchador"|"Prespic"|"Prespic"|"Prespic"|"Radoutable le Craint"|"Ratatouille the Stirrer"|"Ratokio de Alcontel la Despeinada"|"Ramane d'Égoutant"|"Grossewer Raeman"|"Ramán de alcantarilla"|"Ramitant le Dilettante"|"Ralftime the Dilettante"|"Charratos el Diletante"|"Rat Blanc"|"White Rat"|"Rata Blanca"|"Rat d'Égoutant"|"Grossewer Rat"|"Rata de alcantarilla"|"Rat d'Egoutant Malade"|"Sick Grossewer Rat"|"Rata de Alcantarilla Enferma"|"Rat d'Hyoactif"|"Hyoactive Rat"|"Rata hyoactiva"|"Rat Noir"|"Black Rat"|"Rata Negra"|"Ratéhaifaim le Professeur"|"Ratilla the Hun"|"Ratila el Huno"|"Ratlbol l'Aigri"|"Rattle the Hummer"|"Rafa de Alnadalillas el Canibal"|"Raul Mops"|"Raul Mops"|"Raúl mops"|"Rauligo le Sale"|"Raul Modrid the Chulo"|"Raúl Cera la Péptica"|"Rib"|"Rib"|"Rib"|"Ribibi le Cher"|"Rib the Torn"|"Arib Abá el de los 40"|"Robiolego l'Assemblé"|"Robiolego the Assembled"|"Robiolego el Ensamblado"|"Robionicle"|"Robionicle"|"Robionicle"|"Robocoop l'Échangé"|"Robocoop the Switched"|"Robocoop el Intercambiado"|"Robot Fléau"|"Robo Mace"|"Robot mangual"|"Robot Fléau"|"Robo Mace"|"Robot mangual"|"Roissingue"|"Mopy King"|"Rey stropajo"|"Rooroku l'Imposant"|"Rookin the Caped Kinster"|"Kurogordu el Imponente"|"Rose Démoniaque"|"Demonic Rose"|"Rosa demoníaca"|"Rose Obscure"|"Dark Rose"|"Rosa oscura"|"Rostensyl la Cuisinière"|"Zorrose the Messican"|"Rostetricia la Reproductiva"|"Rouquette"|"Gingerocket"|"Rojiva"|"Roy le Merlin"|"Roy the Rover"|"Reyuna el Dibujante"|"Roz la Magicienne"|"Roseanne the Yanker"|"Rochavo Democho el Chispotiado"|"Sakkado la transporteuse"|"Sakkado the Carrier"|"Sakkado la Transportista"|"Saltik"|"Jumparak"|"Araquesalta"|"Saltoavan la Gymnaste"|"Summersalt the Gymnast"|"Saltolante la Gimnasta"|"Sampi l'Eternel"|"Boarealis the Bright"|"Jabulio de la Llanesias el Portero"|"Sanglier"|"Boar"|"Jabalí"|"Sanglier des Plaines"|"Plain Boar"|"Jabalí de las llanuras"|"Sangria le Fruité"|"Boarnigen the Damasker"|"Jabachlí el Casto"|"Sarkapwane"|"Blokapwane"|"Kuanuto"|"Sarkastik l'Incompris"|"Snarkapwane the Snarky"|"Kuantista el Incomprendido"|"Scapé l'Epée"|"Scaratheef the Pincher"|"Escarumais los Siete"|"Scarabosse Doré"|"Golden Scarabugly"|"Escarajefe Dorado"|"Scarafeuille Blanc"|"White Scaraleaf"|"Escarahoja blanco"|"Scarafeuille Bleu"|"Blue Scaraleaf"|"Escarahoja azul"|"Scarafeuille Rouge"|"Red Scaraleaf"|"Escarahoja rojo"|"Scarafeuille Vert"|"Green Scaraleaf"|"Escarahoja verde"|"Scaramel le Fondant"|"Scaramel the Melty"|"Escaramelo el Derretido"|"Scaratos"|"Scaratos"|"Escarato"|"Scaratyn l'Huître"|"Scaraheath the Hanger"|"Escarálibur la Legendaria"|"Scarfayss le Balafré"|"Scarahazad the Storyteller"|"Estarausija Azul el del Danubio"|"Scarouarze l'Epopée"|"Scarabreef the Short"|"Escorobeitor el Malvado"|"Scélérat Strubien"|"Strubian Sickrat"|"Miserata strubiense"|"Scorbute"|"Scurvion"|"Scorbuto"|"Scorpitène l'Enflammé"|"Scorbison the Lonely"|"Scorbuthoveen el Sordo"|"Sergent Zoth"|"Zoth Sergeant"|"Sargento zoth"|"Seripoth l'Ennemi"|"Zouzoth the Cuddly"|"Sargende Michael el Narrador"|"Serpentin"|"Plissken"|"Culebrón"|"Serpiplume"|"Quetsnakiatl"|"Serpipluma"|"Serpistol l'Illustre"|"Serpico the Honest"|"Culebretty la Fea"|"Serpistule le Purulent"|"Quetnin the Fictional"|"Serpistol el Afónico"|"Shin Larve"|"Shin Larva"|"Shin Larva"|"Silf le Rasboul Majeur"|"Silf the Greater Bherb"|"Silf el Rasgabola Mayor"|"Skeunk"|"Skeunk"|"Skonk"|"Soryo Firefoux"|"Soryo Firefoux"|"Soryo Firefux"|"Soryonara le Poli"|"Sorgyo Quiretox the Chatterbox"|"Sairyó Hrdanfux el Volador"|"Souris Grise"|"Grey Mouse"|"Ratón Gris"|"Souris Verte"|"Green Mouse"|"Ratón verde"|"Souristiti l'Immortalisée"|"Cheesy the Immortalised"|"Patata la Inmortalizada"|"Sourizoto le Collant"|"Famouse the Little-Known"|"Ratom Raider, la Curvas"|"Sousouris Grise"|"Grey Moumouse"|"Raratón gris"|"Sousourizoto le Collant"|"Famoumouse the Little-Known"|"Raratom Raider la Curvas"|"Sparo"|"Sparo"|"Sparo"|"Sparoket le Lanceur"|"Sparodi the Python"|"Sparito el Feo"|"Sphincter Cell"|"Sphincter Cell"|"Sfinter Cell"|"Susbewl l'Hypocrite"|"Suzessman the Enthusiastic"|"Sushij el Makisan"|"Susej"|"Susej"|"Susej"|"Tambouille le Gastronome"|"Drumurosh the Nosher"|"Tambulé el Gastrónomo"|"Tambouraï"|"Drumurai"|"Tamburái"|"Tanukouï San"|"Tanukouï San"|"Tanukui San"|"Terraburkal le Perfide"|"Terraburkahl the Perfidious"|"Terraburkal, el Pérfido"|"Terrakoubiak le Guerrier"|"Terrakubiack the Warrior"|"Terrakubiack, el Guerrero"|"Tétonée la Plantureuse"|"Titinaynay the Swayer"|"Mamachichu la Exuberante"|"Tétonuki"|"Titinuki"|"Mamanuki"|"Tikoko"|"Tikokoko"|"Pekekoko"|"Tikosto le Mousse"|"Eskoko the Baron"|"Pekekokoitu el Interruptus"|"Tilolo la Bien Moulée"|"Tatatojiki the Squeaky"|"Jikitita la Moldeada"|"Tiwa'Missou le Gateux"|"Tiwana the Tokin'"|"Black Tegerwoddit el Swinguero"|"Tiwabbit"|"Tiwabbit"|"Pekewabbit"|"Tiwabbit Kiafin"|"Tiwabbit Wosungwee"|"Pekewabbit Hambriento"|"Tiwalpé le Dévêtu"|"Tiwaldo the Hidden"|"Pekewasqhabit el Juguetón"|"Tiwoflan le Lâche"|"Tiwascal the Wapper"|"Pekiwbyt Hambriento el Glotón"|"Tofu"|"Tofu"|"Tofu"|"Tofu Malade"|"Sick Tofu"|"Tofu enfermo"|"Tofu Maléfique"|"Evil Tofu"|"Tofu maléfico"|"Tofu Royal"|"Royal Tofu"|"Tofu Real"|"Tofuldebeu l'Explosif"|"Tofudd the Hunter"|"Satofu el PlasticPaddy"|"Tofumanchou l'Empereur"|"Tofulsom the Jailer"|"Tofumantxú el Mítico"|"Tofurapin le Pétri"|"Tofull the Optimist"|"Tofumado el Alucinado"|"Tortenssia la Fleurie"|"Turtan'ernie the Streetwise"|"Tortugadget el Inspector"|"Torthur la Lutte"|"Turture the Hooded"|"Tortugríssom el Doctor"|"Tortilleur le Coulé"|"Turtrenalds the Tragic"|"Tortruquini el Inspector"|"Tortorak le Cornu"|"Turticorn the Horned"|"Tortugo Projatt el Corto"|"Tortue Bleue"|"Blue Turtle"|"Tortuga azul"|"Tortue Jaune"|"Yellow Turtle"|"Tortuga amarilla"|"Tortue Rouge"|"Red Turtle"|"Tortuga roja"|"Tortue Verte"|"Green Turtle"|"Tortuga verde"|"Touchparak"|"Touchparak"|"Araknola"|"Toufou le Benêt"|"Prikoko the Witless"|"Tufóbico el Miedoso"|"Tour le Vice"|"Hunflower the Sinful"|"Gearsol Metalvaje el Espía"|"Tourbassingue"|"Mopeat"|"Barrostropajo"|"Tourbiket le Virevoletant"|"Mopfeet the Circular"|"Barrostroporosis el Frágil"|"Tournesol Affamé"|"Famished Sunflower"|"Girasol Hambriento"|"Tournesol Sauvage"|"Wild Sunflower"|"Girasol salvaje"|"Toutouf le Velu"|"Follikoko the Tufted"|"Tufaldo Aréneo el Marielito"|"Tromperelle"|"Trumperelle"|"Trompseta"|"Tromplamor le Survivant"|"Trumpaynor the Survivor"|"Trompsosis el Cardiaco"|"Tronknyde"|"Trunknid"|"Tronkónido"|"Tronkoneuz la Tranchante"|"Trunkbeard the Gentle"|"Trroncky III el Tigre"|"Tronquette la Réduite"|"Ginger the Clincher"|"Rojevita la Pequeña"|"Trooll"|"Trool"|"Trooll"|"Troollaraj"|"Troolaraj"|"Trooloko"|"Trooyé l'Oxydé"|"Trooligan the Bulldogg"|"Troolbin el de los Bolsquels"|"Trukikol"|"Glukoko"|"Kosakepega"|"Trukul le Lent"|"Glukoko the Slow"|"Kosakhiin el Lunítico"|"Tsucékoi la Colporteuse"|"Saywhatinochi the Gossipy"|"Sabeké la Vendedora"|"Tsukinochi"|"Tsukinochi"|"Tsukinoichi"|"Tsumani l'Inondeur"|"Tsumani the Flooder"|"Tsumani el Inundador"|"Tsume-bozu"|"Tsume-Bozu"|"Tsume-bozu"|"Tynril Ahuri"|"Stunned Tynril"|"Tynril Atónito"|"Tynril Consterné"|"Dismayed Tynril"|"Tynril Estupefacto"|"Tynril Déconcerté"|"Disconcerted Tynril"|"Tynril Absorto"|"Tynril Perfide"|"Perfidious Tynril"|"Tynril Pérfido"|"Vampire"|"Vampire"|"Vampiro"|"Vampunor le Glacial"|"Vamp the Impalest"|"Vamorespirros el Múltiple"|"Vétauran"|"Vetauran"|"Vetorano"|"Vétaurine l'Énergisé"|"Vetaurine the Energised"|"Vetaurino el Energizado"|"Wa Wabbit"|"Wa Wabbit"|"Wey Wabbit"|"Wabbit"|"Wabbit"|"Wabbit"|"Wabbit Gm"|"GM Wabbit"|"Wabbit GM"|"Wabbit Squelette"|"Skeleton Wabbit"|"Wabbit esqueleto"|"Wabbitud le Constant"|"McWhabbit the Diehard"|"Wogew Wabbit el Engañado"|"Wagnagnah le Sanglant"|"Wabbin the Wich"|"Blackowibbit el Imaginativo"|"Wara l'Amer"|"Worka the Willful"|"Warjamer el Miniaturista"|"Warko Marron"|"Brown Warko"|"Warko marrón"|"Warko Violet"|"Purple Warko"|"Warko violeta"|"Warkolad l'Etreinte"|"Warko the Inky"|"Warkamole el Apetitoso"|"Watdogue le Bien Nommé"|"Wabbitor the Apt"|"Waybbit Esquelite el Rebelde"|"Wo Wabbit"|"Wo Wabbit"|"Wabbit wodo"|"Wokènrôl le Danseur"|"Wowalker the Egyptian"|"Wabibip Woyote el Persistente"|"Yokai Firefoux"|"Yokai Firefoux"|"Yokai Firefux"|"YokaiKoral le Duel"|"Yokai the Choral"|"Yocái Ipehkaíto el Frito"|"Yukisamara"|"Yukisamara"|"Yukisamara"} MonsterName
381
+
382
+ * @typedef {"Amakna"|"Astrub"|"Baie de Sufokia"|"Sufokia Bay"|"Bahía de Sufokia"|"Bonta"|"Brâkmar"|"Brakmar"|"Foire du Trool"|"Trool Fair"|"Feria del Trool"|"Forêt des Abraknydes"|"Treechnid Forest"|"Bosque de los abráknidos"|"Forêt Maléfique"|"Evil Forest"|"Bosque Maléfico"|"Gelaxième Dimension"|"Jellith Dimension"|"Gelexta Dimensión"|"Île d'Otomaï"|"Otomai Island"|"Isla de Otomai"|"Île de Grobe"|"Nolifis Island"|"Isla de Grobe"|"Île de Moon"|"Moon Island"|"Isla de Moon"|"Île de Pandala"|"Pandala Island"|"Isla de Pandala"|"Île des Wabbits"|"Wabbit Islands"|"Archipiélago wabbit"|"Île du Minotoror"|"Minotoror Island"|"Isla del Minotauroro"|"Labyrinthe du Dragon Cochon"|"Dragon Pig's Maze"|"Laberinto del Dragocerdo"|"Landes de Sidimote"|"Sidimote Moors"|"Landas de Sidimote"|"Montagne des Koalaks"|"Koalak Mountain"|"Montaña de los koalaks"|"Plaines de Cania"|"Cania Plains"|"Llanuras de Cania"} ZoneName
383
+
384
+ * @typedef {"Akadémie des Gobs"|"Gob Akademy"|"Akademia de los Goblins"|"Antre de Crocabulia"|"Crocabulia's Lair"|"Antro de Cocabulia"|"Bord de la forêt maléfique"|"Edge of the Evil Forest"|"Linde del Bosque Maléfico"|"Campagne d'Amakna"|"Amakna Countryside"|"Campo de Amakna"|"Campement des Bworks"|"The Bwork Camp"|"Campamento de los bworks"|"Campement des Gobelins"|"The Goblin Camp"|"Campamento de los goblins"|"Champ des Ingalsse"|"Ingalsses' Fields"|"Campo de los Ingals"|"Cimetière"|"Cemetery"|"Cementerio"|"Clairière de Brouce Boulgoure"|"Brouce Boulgoure's Clearing"|"Claro de Brus Bulguro"|"Cloaque d'Amakna"|"Amakna Sewers"|"Cloaca de Amakna"|"Coin des Boos"|"Mushd Corner"|"Rincón de los Boos"|"Coin des Bouftous"|"Gobball Corner"|"Rincón de los Jalatós"|"Côte d'Asse"|"Asse Coast"|"Costa del Rano"|"Cryptes du cimetière"|"Cemetery Crypts"|"Criptas del cementerio"|"Donjon des Bworks"|"Bwork Dungeon"|"Mazmorra de los Bworks"|"Donjon des Forgerons"|"Blacksmith Dungeon"|"Mazmorra de los Herreros"|"Donjon des Larves"|"Larva Dungeon"|"Mazmorra de las Larvas"|"Donjon des Scarafeuilles"|"Scaraleaf Dungeon"|"Mazmorra de los Escarahojas"|"Donjon des Squelettes"|"Skeleton Dungeon"|"Mazmorra de los Esqueletos"|"Donjon des Tofus"|"Tofu House"|"Mazmorra de los Tofus"|"Épreuve de Draegnerys"|"Draegnerys's Trial"|"Prueba de Dragenerys"|"Forêt d'Amakna"|"Amakna Forest"|"Bosque de Amakna"|"La forêt maléfique"|"The Evil Forest"|"El Bosque Maléfico"|"Marécages d'Amakna"|"Amakna Swamps"|"Pantanos de Amakna"|"Milifutaie"|"Milicluster"|"Mililameda"|"Montagne basse des Craqueleurs"|"Low Crackler Mountain"|"Piedemonte de los crujidores"|"Montagne des Craqueleurs"|"Crackler Mountain"|"La montaña de los crujidores"|"Nid du Kwakwa"|"The Kwakwa's Nest"|"Nido de Kwoknan"|"Passage vers Brâkmar"|"Passage to Brakmar"|"Pasaje hacia Brakmar"|"Péninsule des gelées"|"Jelly Peninsula"|"Península de las gelatinas"|"Pitons Rocheux des Craqueleurs"|"Crackler's Rocky Peaks"|"Picos Rocosos de los Crujidores"|"Plaine des Scarafeuilles"|"Scaraleaf Plain"|"Llanura de los escarahojas"|"Port de Madrestam"|"Madrestam Harbour"|"Puerto de Madrestam"|"Presqu'île des Dragoeufs"|"Dreggon Peninsula"|"Península de los dragohuevos"|"Repaire de Sphincter Cell"|"Sphincter Cell's Lair"|"Guarida de Sfinter Cell"|"Repaire du Kharnozor"|"Kharnotaurus's Lair"|"Guarida de Karnozor"|"Rivière Kawaii"|"Kawaii River"|"Río Kawaii"|"Sanctuaire des Dragoeufs"|"The Dreggons' Sanctuary"|"Santuario de los dragohuevos"|"Souterrains"|"Tunnels"|"Subterráneos"|"Souterrains des Dragoeufs"|"Dreggon Tunnels"|"Subterráneos de los dragohuevos"|"Territoire des Bandits"|"Bandit Territory"|"Territorio de los bandidos"|"Territoire des Porcos"|"Porco Territory"|"Territorio de los porcos"|"Territoire souterrain des Porcos"|"Underground Porco Territory"|"Territorio subterráneo de los porcos"|"Tofulailler Royal"|"Royal Tofu House"|"Tofullinero Real"|"Village d'Amakna"|"Amakna Village"|"Pueblo de Amakna"|"Village des Bworks"|"Bwork Village"|"Pueblo de los bworks"|"Village des Dragoeufs"|"Dreggon Village"|"Pueblo de los dragohuevos"|"Calanques d'Astrub"|"Astrub Rocky Inlet"|"Calas de Astrub"|"Champs d'Astrub"|"Astrub Fields"|"Campos de Astrub"|"Château Ensablé"|"Sandy Castle"|"Castillo de Arena"|"Cimetière d'Astrub"|"Astrub Cemetery"|"Cementerio de Astrub"|"Cité d'Astrub"|"Astrub City"|"Ciudad de Astrub"|"Cour du Bouftou Royal"|"Royal Gobball's Court"|"Corte del Jalató Real"|"Égouts d'Astrub"|"Astrub Sewers"|"Alcantarillas de Astrub"|"Forêt d'Astrub"|"Astrub Forest"|"Bosque de Astrub"|"Grange du Tournesol Affamé"|"Famished Sunflower's Barn"|"Granero del Girasol Hambriento"|"Prairies d'Astrub"|"Astrub Meadow"|"Praderas de Astrub"|"Souterrains d'Astrub"|"Astrub Tunnels"|"Subterráneos de Astrub"|"Tainéla"|"Tainela"|"Rivage sufokien"|"Sufokian Shoreline"|"Ribera del golfo sufokeño"|"Sufokia"|"Canaux méphitiques"|"Foul-Smelling Canals"|"Canales mefíticos"|"Cimetière des Héros"|"Heroes' Cemetery"|"Cementerio de los héroes"|"Cœur immaculé"|"Immaculate Heart"|"Corazón Inmaculado"|"Faubourgs des artisans"|"Crafters' District"|"Arrabales de los artesanos"|"Garde-manger du Rat Blanc"|"White Rat's Pantry"|"Despensa de la Rata Blanca"|"Havres d'ivoire"|"Ivory Harbours"|"Puertos de marfil"|"Promontoire des cieux"|"Promontory of the Heavens"|"Promontorio de los cielos"|"Rives iridescentes"|"Iridescent Shores"|"Riberas iridiscentes"|"Bordure de Brâkmar"|"Brakmar City Walls"|"Afueras de Brakmar"|"Cimetière des Torturés"|"Cemetery of the Tortured"|"Cementerio de los Torturados"|"Entrailles de Brâkmar"|"Bowels of Brakmar"|"Entrañas de Brakmar"|"L'Ancre"|"The Anchor"|"Ancla"|"L'Enclume"|"The Anvil"|"Yunque"|"La Cuirasse"|"The Breastplate"|"Coraza"|"La Marmite"|"The Pot"|"Marmita"|"Sousouricière du Rat Noir"|"Black Rat's Moumousetrap"|"Raratonera de la Rata Negra"|"Maison Fantôme"|"Haunted House"|"Mansión Encantada"|"Bois des Arak-haï"|"Arak-hai Forest"|"Bosque de las arak-hais"|"Clairière du Chêne Mou"|"Soft Oak Clearing"|"Claro del Roble Blando"|"Domaine Ancestral"|"Ancestral Domain"|"Territorio Ancestral"|"Forêt Sombre"|"Dark Forest"|"Bosque Oscuro"|"Orée de la forêt des Abraknydes"|"Edge of the Treechnid Forest"|"Linde del bosque de los abráknidos"|"Tertre du long sommeil"|"Long Slumber's Barrow"|"Túmulo del Largo Sueño"|"Gelaxième dimension"|"Jellith Dimension"|"Gelexta Dimensión"|"Antre du Kralamoure Géant"|"Lair of the Giant Kralove"|"Antro del Kralamar Gigante"|"Arche d'Otomaï"|"Otomai's Ark"|"Arca de Otomai"|"Cale de l'arche d'Otomaï"|"Hold of Otomai's Ark"|"Cala del Arca de Otomai"|"Canopée du Kimbo"|"Kimbo's Canopy"|"Canopea del Kimbo"|"Feuillage de l'arbre Hakam"|"Tree Keeholo Foliage"|"Las ramas del árbol Hakam"|"Goulet du Rasboul"|"Bherb's Gully"|"Boca del Rasgabola"|"Grotte Hesque"|"Grotto Hesque"|"Gruta Grut'Hesqua"|"Île des naufragés"|"Castaway Island"|"Isla de los Náufragos"|"Jungle obscure"|"Dark Jungle"|"Oscura Jungla"|"Laboratoire du Tynril"|"Tynril Lab"|"Laboratorio del Tynril"|"Plage de Corail"|"Coral Beach"|"Playa de coral"|"Plaines herbeuses"|"Grassy Plains"|"Llanuras Herbosas"|"Tourbière nauséabonde"|"Putrid Peat Bog"|"Turbera nauseabunda"|"Tourbière sans fond"|"Bottomless Peat Bog"|"Turbera sin fondo"|"Tronc de l'arbre Hakam"|"Tree Keeholo Trunk"|"Tronco del árbol Hakam"|"Village de la Canopée"|"Canopy Village"|"Pueblo de la canopea"|"Village des Zoths"|"Zoth Village"|"Pueblo de los zoths"|"Cimetière de Grobe"|"Nolifis Cemetery"|"Cementerio de Grobe"|"Demeure des Esprits"|"Spirit Abode"|"Casa de los Espíritus"|"Île de Grobe"|"Nolifis Island"|"La Isla de Grobe"|"Mont des Tombeaux"|"Mount Tombs"|"Monte de las Tumbas"|"Plan Astral"|"Plan Matériel"|"Tombe du Shogun Tofugawa"|"Shogun Tofugawa's Tomb"|"Tumba del Shogun Tofugawa"|"Arbre de Moon"|"Moon's Tree"|"Árbol de Moon"|"Bateau du Chouque"|"LeChouque's Boat"|"Navío de Le Chuko"|"Chemin du Crâne"|"Skull Path"|"Camino de los cráneos"|"Forêt des Masques"|"The Forest of Masks"|"Bosque de las máscaras"|"Jungle Interdite"|"The Forbidden Jungle"|"Jungla Prohibida"|"La jungle profonde de Moon"|"The Deep Moon Jungle"|"La selva profunda de Moon"|"Le chemin vers Moon"|"The Road to Moon"|"El camino hacia Moon"|"Plage de la Tortue"|"Turtle Beach"|"Playa Tortuga"|"Village Kanniboul"|"Kanniball Village"|"Pueblo Kaníbola"|"Aerdala"|"Airedala"|"Akwadala"|"Atelier du Tanukouï San"|"Tanukouï San's Workshop"|"Taller de Tanukui San"|"Bambusaie de Damadrya"|"Damadrya's Bamboo Grove"|"Bambusería de Kodámade"|"Bordure d'Aerdala"|"Border of Aerdala"|"Alrededores de Airedala"|"Bordure d'Akwadala"|"Border of Akwadala"|"Alrededores de Akwadala"|"Bordure de Feudala"|"Border of Feudala"|"Alrededores de Fuegodala"|"Bordure de Terrdala"|"Border of Terrdala"|"Alrededores de Tierradala"|"Dojo du Vent"|"Wind Dojo"|"Dojo del Viento"|"Donjon des Firefoux"|"Firefoux Dungeon"|"Mazmorra de los Firefux"|"Fabrique de foux d'artifice"|"Fouxwork Factory"|"Fábrica de Fux Artificiales"|"Feudala"|"Fuegodala"|"Forêt de Pandala"|"Pandala Forest"|"Bosque de Pandala"|"Pandala Neutre"|"Neutral Pandala"|"Pandala Neutral"|"Plantala"|"Repaire des Pandikazes - Huitième plate-forme"|"Pandikazes' Hideout - Eighth Platform"|"Guarida de los Pandikazes - Octava plataforma"|"Terrdala"|"Tierradala"|"Vallée de la Dame des eaux"|"Valley of the Lady of the Water"|"Valle de la Dama del Agua"|"Village de Feudala"|"Feudala Village"|"Château du Wa Wabbit"|"Wa Wabbit's Castle"|"Castillo del Wey Wabbit"|"Île de la Cawotte"|"Cawwot Island"|"Isla Zanahowia"|"Îlot de la Couronne"|"Isle of the Cwown"|"Islote La Cowona"|"Îlot de Waldo"|"Gwimace Island"|"Islote Waldo Nald"|"Îlot des Tombeaux"|"Gwavestone Island"|"Islote Sepultuwa"|"Souterrains des Wabbits"|"Wabbit Tunnels"|"Subterráneos de los wabbits"|"Centre du labyrinthe du Minotoror"|"Inner Labyrinth of the Minotoror"|"Centro del Laberinto del Minotauroro"|"Île du Minotoror"|"Minotoror Island"|"Isla del Minotauroro"|"Labyrinthe du Minotoror"|"Labyrinth of the Minotoror"|"Laberinto del Minotauroro"|"Antre du Dragon Cochon"|"Dragon Pig Dungeon"|"Antro del Dragocerdo"|"Labyrinthe du Dragon Cochon"|"The Dragon Pig's Maze"|"El laberinto del Dragocerdo"|"Caverne des Fungus"|"Fungus Cavern"|"Caverna de los fongos"|"Désolation de Sidimote"|"Desolation of Sidimote"|"Tierras desoladas de Sidimote"|"Domaine des Fungus"|"Fungus Domain"|"Territorio de los fongos"|"Grotte du Bworker"|"Bworker's Cave"|"Cueva de Bworker"|"Hauts des Hurlements"|"Howling Heights"|"Altos de los Aullidos"|"Laboratoire de Brumen Tinctorias"|"Brumen Tinctorias's LaboRATory"|"Laboratorio de Brumen Tinctorias"|"Tanière du Meulou"|"Moowolf Lair"|"Guarida del Maxilubo"|"Temple du Grand Ougah"|"Temple of the Great Ougaa"|"Templo del Gran Ugah"|"Terres Désacrées"|"Desecrated Highlands"|"Tierras Desacralizadas"|"Antre du Koulosse"|"Koolich's Lair"|"Antro del Trankitronko"|"Canyon sauvage"|"Wild Canyon"|"Cañón Salvaje"|"Caverne du Koulosse"|"Koolich Cavern"|"Cueva del Trankitronko"|"Cimetière primitif"|"Primitive Cemetery"|"Cementerio primitivo"|"Forêt de Kaliptus"|"Kaliptus Forest"|"Bosque de kaliptos"|"Lacs enchantés"|"Enchanted Lakes"|"Lagos encantados"|"Marécages nauséabonds"|"Nauseating Swamps"|"Pantanos nauseabundos"|"Marécages sans fond"|"Bottomless Swamps"|"Pantanos sin fondo"|"Repaire de Skeunk"|"Skeunk's Hideout"|"Guarida de Skonk"|"Territoire des Dragodindes Sauvages"|"Wild Dragoturkey Territory"|"Territorio de los dragopavos salvajes"|"Vallée de la Morh'Kitu"|"Agony V'Helley"|"Valle de la Muertekemata"|"Antre du Blop Multicolore Royal"|"Royal Rainbow Blop Lair"|"Antro del Blop Multicolor Real"|"Baie de Cania"|"Cania Bay"|"Bahía de Cania"|"Bibliothèque du Maître Corbac"|"Lord Crow's Library"|"Biblioteca del Maestro Cuerbok"|"Bois de Litneg"|"Eltneg Wood"|"Bosque de Litneg"|"Champs de Cania"|"Cania Fields"|"Campos de Cania"|"Clos des Blops"|"Blop Fields"|"Enclave de los Blops"|"Dents de Pierre"|"Stontusk Desert"|"Dientes de Piedra"|"Grotte de Kanigroula"|"Kanigrula's Cave"|"Gruta de Kanígrula"|"Lac de Cania"|"Cania Lake"|"Lago de Cania"|"Massif de Cania"|"Cania Massif"|"Sierra de Cania"|"Mine des Dopeuls"|"Dopple Mine"|"Mina de los dopeuls"|"Pénates du Corbac"|"The Crow's Domain"|"Morada de cuerbok"|"Pics de Cania"|"Cania Peaks"|"Picos de Cania"|"Plaine des Porkass"|"Lousy Pig Plain"|"Llanura de los puerkazos"|"Plaines Rocheuses"|"Rocky Plains"|"Llanuras Rocosas"|"Routes Rocailleuses"|"Rocky Roads"|"Caminos rocosos"|"Salle de lecture du Maître Corbac"|"Lord Crow's Reading Room"|"Sala de lectura del Maestro Cuerbok"|"Village des Dopeuls"|"Dopple Village"|"Pueblo de los dopeuls"|"Village des Kanigs"|"Kanig Village"|"Pueblo de los kanigs"|"Volière de la Haute Truche"|"Cross Strich's Aviary"|"Pajarera de Thor Pestruz"} SubZoneName
385
+
386
+ * @typedef {object} SubZone
387
+ * @property {number} id
388
+ * @property {Record<"en"|"es"|"fr", SubZoneName>} name
389
+
390
+ * @typedef {object} Zone
391
+ * @property {number} id
392
+ * @property {Record<"fr"|"en"|"es", ZoneName>} name
393
+ * @property {Array<SubZone>} subzones
394
+
395
+ * @typedef {object} Monster
396
+ * @property {number} id
397
+ * @property {Record<"fr"|"en"|"es", MonsterName>} name
398
+ * @property {string} image
399
+ * @property {number} level_min
400
+ * @property {number} level_max
401
+ * @property {MonsterType} type
402
+ * @property {object} [reference]
403
+ * @property {number} [reference.id]
404
+ * @property {Record<"fr"|"en"|"es", MonsterName>} [reference.name]
405
+ * @property {Array<Zone>} zones
406
+
407
+ * @typedef {object} Pagination
408
+ * @property {number} total
409
+ * @property {number} limit
410
+ * @property {number} offset
411
+
412
+ * @param {object} [options]
413
+ * @param {MonsterName} [options.monster_name]
414
+ * @param {MonsterTypeName} [options.monster_type]
415
+ * @param {number} [options.limit]
416
+ * @param {number} [options.offset]
417
+
418
+ * @returns {Promise<{
419
+ * ok: boolean,
420
+ * status: number,
421
+ * statusText: string,
422
+ * error?: string,
423
+ * retryAfter?: number,
424
+ * data?: Array<Monster>|Monster,
425
+ * pagination?: Pagination,
426
+ * }>}
427
+
428
+ * @example
429
+ * getMonsters(); // Liste des monstres avec pagination et filtres (Array + pagination)
430
+ * getMonsters({ monster_name: "Aboub" }); // Recherche par nom (français, anglais ou espagnol) (Object)
431
+ * getMonsters({ type: "archimonstre" }) // Filtrer par type (Array + pagination)
432
+ * getMonsters({ limit: 200, offset: 200 }); // Renvoi les 200 monstres à offset 200 (Array + pagination)
433
+ */
434
+ async getMonsters(options) {
435
+ let path = `${base}/monsters`;
436
+ const queries = [];
437
+
438
+ if (options?.monster_name) {
439
+ const monster = monsterByName(options.monster_name);
440
+
441
+ if (!monster) {
442
+ throw new Error(`monster_name '${options.monster_name}' doesn't exist`);
443
+ }
444
+
445
+ path += `/${monster.id}`;
446
+ } else {
447
+ if (options?.monster_type) {
448
+ const monsterType = monsterTypeByName(options.monster_type);
449
+
450
+ if (!monsterType) {
451
+ throw new Error(
452
+ `monster_type '${options.monster_type}' doesn't exist`,
453
+ );
454
+ }
455
+
456
+ queries.push(`type=${monsterType.id}`);
457
+ }
458
+ if (options?.limit) {
459
+ queries.push(`limit=${options.limit}`);
460
+ }
461
+ if (options?.offset) {
462
+ queries.push(`offset=${options.offset}`);
463
+ }
464
+
465
+ path += `?${queries.join("&")}`;
466
+ }
467
+
468
+ let result = {};
469
+
470
+ try {
471
+ const _ = await fetch(path, {
472
+ headers: {
473
+ Authorization: `Bearer ${this.#api_key}`,
474
+ },
475
+ });
476
+
477
+ result.ok = _.ok;
478
+ result.status = _.status;
479
+ result.statusText = _.statusText;
480
+
481
+ if (_.status === 429) {
482
+ result.retryAfter = _.headers.get("Retry-After");
483
+ }
484
+
485
+ let data = {};
486
+
487
+ if (result.ok) {
488
+ data = convertIds(await _.json());
489
+
490
+ if (nodeComfort.isArray(data.data)) {
491
+ for (const monster of data.data) {
492
+ if (!monster.zones) {
493
+ monster.zones = cache.monsters.find(
494
+ (m) => m.id === monster.id,
495
+ ).zones;
496
+ }
497
+ }
498
+ } else {
499
+ if (!data.data.zones) {
500
+ data.data.zones = cache.monsters.find(
501
+ (m) => m.id === data.data.id,
502
+ ).zones;
503
+ }
504
+ }
505
+ }
506
+
507
+ result = {
508
+ ...result,
509
+ ...data,
510
+ };
511
+ } catch (error) {
512
+ result.ok = false;
513
+ result.status = 500;
514
+ result.statusText = "Internal Server Error";
515
+ result.error = error.message ?? error.toString();
516
+ }
517
+
518
+ return result;
519
+ }
520
+
521
+ /**
522
+ * ### Événements Kralamoure
523
+
524
+ * @typedef {object} Kralove
525
+ * @property {number} id
526
+ * @property {string} event_datetime
527
+ * @property {string} description
528
+ * @property {string} creator
529
+ * @property {number} participants_count
530
+ * @property {number} character_count
531
+ * @property {number} messages_count
532
+ * @property {Server} server
533
+
534
+ * @typedef {object} Participant
535
+ * @property {string} username
536
+ * @property {number} character_count
537
+
538
+ * @typedef {object} Message
539
+ * @property {string} username
540
+ * @property {string} content
541
+ * @property {string} created_at
542
+
543
+ * @typedef {object} KraloveDetail
544
+ * @property {number} id
545
+ * @property {string} event_datetime
546
+ * @property {string} description
547
+ * @property {string} creator
548
+ * @property {Server} server
549
+ * @property {Array<Participant>} participants
550
+ * @property {Array<Message>} messages
551
+
552
+ * @param {object} [options]
553
+ * @param {number} [options.id] - Détails d'un événement avec participants et messages
554
+ * @param {ServerName} [options.server_name] - Filtrer par serveur
555
+ * @param {string} [options.start_date] - Date de début au format `YYYY-MM-DD`. Par défaut: aujourd'hui
556
+
557
+ * @returns {Promise<{
558
+ * ok: boolean,
559
+ * status: number,
560
+ * statusText: string,
561
+ * error?: string,
562
+ * retryAfter?: number,
563
+ * data?: Array<Kralove>|KraloveDetail
564
+ * }>}
565
+
566
+ * @example
567
+ * getKraloves(); // Liste tous les événements Kralamoure (Array)
568
+ * getKraloves({ server_name: "Brial" }); // Filtrer par serveur (Array)
569
+ * getKraloves({ start_date: "2026-01-27" }); // Date de début (Array)
570
+ * getKraloves({ id: 1 }); // Détails d'un événement avec participants et messages (Object)
571
+ */
572
+ async getKraloves(options) {
573
+ let path = `${base}/kralove`;
574
+ const queries = [];
575
+
576
+ if (options?.id) {
577
+ path += `/${options.id}`;
578
+ } else {
579
+ if (options?.server_name) {
580
+ const server = serverByName(options.server_name);
581
+
582
+ if (!server) {
583
+ throw new Error(`server_name '${options.server_name}' doesn't exist`);
584
+ }
585
+
586
+ queries.push(`server=${server.id}`);
587
+ }
588
+
589
+ if (options?.start_date) {
590
+ queries.push(`from=${options.start_date}`);
591
+ }
592
+
593
+ path += `?${queries.join("&")}`;
594
+ }
595
+
596
+ let result = {};
597
+
598
+ try {
599
+ const _ = await fetch(path, {
600
+ headers: {
601
+ Authorization: `Bearer ${this.#api_key}`,
602
+ },
603
+ });
604
+
605
+ result.ok = _.ok;
606
+ result.status = _.status;
607
+ result.statusText = _.statusText;
608
+
609
+ if (_.status === 429) {
610
+ result.retryAfter = _.headers.get("Retry-After");
611
+ }
612
+
613
+ let data = {};
614
+
615
+ if (result.ok) {
616
+ data = convertIds(await _.json());
617
+ }
618
+
619
+ result = {
620
+ ...result,
621
+ ...data,
622
+ };
623
+ } catch (error) {
624
+ result.ok = false;
625
+ result.status = 500;
626
+ result.statusText = "Internal Server Error";
627
+ result.error = error.message ?? error.toString();
628
+ }
629
+
630
+ return result;
631
+ }
632
+
633
+ /**
634
+ * ### Modèles de quête
635
+ * Les modèles de quête décrivent la liste des monstres à capturer pour chaque version du jeu.
636
+
637
+ * @typedef {object} QuestTemplate
638
+ * @property {number} id
639
+ * @property {GameVersion} game_version
640
+ * @property {number} monster_count
641
+ * @property {number} step_count
642
+
643
+ * @typedef {object} QuestTemplateDetail
644
+ * @property {number} id
645
+ * @property {GameVersion} game_version
646
+ * @property {Array<Monster & { step: number }>} monsters
647
+ * @property {Pagination} pagination
648
+
649
+ * @param {object} [options]
650
+ * @param {GameName} [options.game_name]
651
+ * @param {number} [options.step]
652
+ * @param {number} [options.limit]
653
+ * @param {number} [options.offset]
654
+
655
+ * @returns {Promise<{
656
+ * ok: boolean,
657
+ * status: number,
658
+ * statusText: string,
659
+ * error?: string,
660
+ * retryAfter?: number,
661
+ * data?: Array<QuestTemplate>|QuestTemplateDetail,
662
+ * }>}
663
+
664
+ * @example
665
+ * getQuestTemplates(); // (Array)
666
+ * getQuestTemplates({ game_name: "Dofus (Unity)" }); // Retourne un modèle avec la liste de ses monstres. Supporte la pagination et le filtre par étape.
667
+ */
668
+ async getQuestTemplates(options) {
669
+ let path = `${base}/quest-templates`;
670
+
671
+ if (options?.game_name) {
672
+ const game = gameByName(options.game_name);
673
+
674
+ if (!game) {
675
+ throw new Error(`game_name '${options.game_name}' doesn't exist`);
676
+ }
677
+
678
+ path += `/${game.id}`;
679
+ const queries = [];
680
+
681
+ if (options?.step) {
682
+ queries.push(`step=${options.step}`);
683
+ }
684
+ if (options?.limit) {
685
+ queries.push(`limit=${options.limit}`);
686
+ }
687
+ if (options?.offset) {
688
+ queries.push(`offset=${options.offset}`);
689
+ }
690
+
691
+ path += `?${queries.join("&")}`;
692
+ }
693
+
694
+ let result = {};
695
+
696
+ try {
697
+ const _ = await fetch(path, {
698
+ headers: {
699
+ Authorization: `Bearer ${this.#api_key}`,
700
+ },
701
+ });
702
+
703
+ result.ok = _.ok;
704
+ result.status = _.status;
705
+ result.statusText = _.statusText;
706
+
707
+ if (_.status === 429) {
708
+ result.retryAfter = _.headers.get("Retry-After");
709
+ }
710
+
711
+ let data = {};
712
+
713
+ if (result.ok) {
714
+ data = convertIds(await _.json());
715
+
716
+ if (!nodeComfort.isArray(data.data)) {
717
+ data.data.monsters = data.data.monsters.map((m) => ({
718
+ ...cache.monsters.find((x) => x.id === m.id),
719
+ step: m.step,
720
+ }));
721
+ }
722
+ }
723
+
724
+ result = {
725
+ ...result,
726
+ ...data,
727
+ };
728
+ } catch (error) {
729
+ result.ok = false;
730
+ result.status = 500;
731
+ result.statusText = "Internal Server Error";
732
+ result.error = error.message ?? error.toString();
733
+ }
734
+
735
+ return result;
736
+ }
737
+
738
+ /**
739
+ * ### Rechercher des utilisateurs
740
+ * Recherche des utilisateurs ayant des quêtes publiques. Nécessite un terme de recherche d'au moins 3 caractères.
741
+
742
+ * @typedef {object} UserAvatar
743
+ * @property {number} id
744
+ * @property {Record<"fr"|"en"|"es", string>} name
745
+ * @property {string} image
746
+
747
+ * @typedef {object} Search
748
+ * @property {string} username
749
+ * @property {UserAvatar} avatar
750
+ * @property {string} last_active
751
+
752
+ * @param {string} query - Terme de recherche (min 3 caractères)
753
+ * @param {object} [options]
754
+ * @param {ServerName} [options.server_name]
755
+ * @param {number} [options.active_within_days] - Utilisateurs actifs dans les N derniers jours (défaut : 90, max : 365)
756
+ * @param {number} [options.limit] - Nombre de résultats (défaut : 20, max : 50)
757
+ * @param {number} [options.offset] - Décalage pour la pagination (défaut : 0)
758
+
759
+ * @returns {Promise<{
760
+ * ok: boolean;
761
+ * status: number;
762
+ * statusText: string;
763
+ * retryAfter?: number;
764
+ * error?: string;
765
+ * data?: Array<Search>;
766
+ * pagination?: Pagination;
767
+ * }>}
768
+
769
+ * @example
770
+ * searchUser("jean"); // Recherche utilisateurs (Array)
771
+ * searchUser("jean", { server_name: "Brial" }); // Filtrer par ID de serveur (Array)
772
+ * searchUser("jean", { active_within_days: 365, limit:50, offset: 10 }); // 50 Utilisateurs actifs dans les 365 derniers jours, passe les 10 premiers (Array)
773
+ */
774
+ async searchUsers(query, options) {
775
+ let path = `${base}/users/search`;
776
+ const queries = [];
777
+
778
+ queries.push(`q=${query}`);
779
+
780
+ if (options?.server_name) {
781
+ const server = serverByName(options.server_name);
782
+
783
+ if (!server) {
784
+ throw new Error(`server_name '${options.server_name}' doesn't exist`);
785
+ }
786
+
787
+ queries.push(`server_id=${server.id}`);
788
+ }
789
+ if (options?.active_within_days) {
790
+ queries.push(`active_within_days=${options.active_within_days}`);
791
+ }
792
+ if (options?.limit) {
793
+ queries.push(`limit=${options.limit}`);
794
+ }
795
+ if (options?.offset) {
796
+ queries.push(`offset=${options.offset}`);
797
+ }
798
+
799
+ path += `?${queries.join("&")}`;
800
+
801
+ let result = {};
802
+
803
+ try {
804
+ const _ = await fetch(path, {
805
+ headers: {
806
+ Authorization: `Bearer ${this.#api_key}`,
807
+ },
808
+ });
809
+
810
+ result.ok = _.ok;
811
+ result.status = _.status;
812
+ result.statusText = _.statusText;
813
+
814
+ if (_.status === 429) {
815
+ result.retryAfter = _.headers.get("Retry-After");
816
+ }
817
+
818
+ let data = {};
819
+
820
+ if (result.ok) {
821
+ data = convertIds(await _.json());
822
+ }
823
+
824
+ result = {
825
+ ...result,
826
+ ...data,
827
+ };
828
+ } catch (error) {
829
+ result.ok = false;
830
+ result.status = 500;
831
+ result.statusText = "Internal Server Error";
832
+ result.error = error.message ?? error.toString();
833
+ }
834
+
835
+ return result;
836
+ }
837
+
838
+ /**
839
+ * ### Profil utilisateur
840
+
841
+ * @typedef {object} User
842
+ * @property {string} username
843
+ * @property {string} bio
844
+ * @property {UserAvatar} avatar
845
+ * @property {string} created_at
846
+ * @property {string} last_active
847
+
848
+ * @param {string} username
849
+
850
+ * @returns {Promise<{
851
+ * ok: boolean,
852
+ * status: number,
853
+ * statusText: string,
854
+ * error?: string,
855
+ * retryAfter?: number,
856
+ * data?: User,
857
+ * }>}
858
+
859
+ * @example
860
+ * getUser("ix-xs"); // Profil utilisateur (Object)
861
+ */
862
+ async getUser(username) {
863
+ let path = `${base}/users/${username}`;
864
+
865
+ let result = {};
866
+
867
+ try {
868
+ const _ = await fetch(path, {
869
+ headers: {
870
+ Authorization: `Bearer ${this.#api_key}`,
871
+ },
872
+ });
873
+
874
+ result.ok = _.ok;
875
+ result.status = _.status;
876
+ result.statusText = _.statusText;
877
+
878
+ if (_.status === 429) {
879
+ result.retryAfter = _.headers.get("Retry-After");
880
+ }
881
+
882
+ let data = {};
883
+
884
+ if (result.ok) {
885
+ data = convertIds(await _.json());
886
+
887
+ delete data.data.quests;
888
+ }
889
+
890
+ result = {
891
+ ...result,
892
+ ...data,
893
+ };
894
+ } catch (error) {
895
+ result.ok = false;
896
+ result.status = 500;
897
+ result.statusText = "Internal Server Error";
898
+ result.error = error.message ?? error.toString();
899
+ }
900
+
901
+ return result;
902
+ }
903
+
904
+ /**
905
+ * ### Liste des quêtes d'un utilisateur
906
+ * Retourne les quêtes publiques avec le nombre de monstres recherchés et proposés.
907
+
908
+ * @typedef {object} Quest
909
+ * @property {string} slug
910
+ * @property {string} character_name
911
+ * @property {number} current_step
912
+ * @property {number} parallel_quests
913
+ * @property {number} wanted_count
914
+ * @property {number} offered_count
915
+ * @property {Server} server
916
+ * @property {QuestTemplate} quest_template
917
+
918
+ * @param {string} username
919
+
920
+ * @returns {Promise<{
921
+ * ok: boolean,
922
+ * status: number,
923
+ * statusText: string,
924
+ * error?: string,
925
+ * retryAfter?: number,
926
+ * data?: Array<Quest>,
927
+ * }>}
928
+
929
+ * @example
930
+ * getUserQuests("ix-xs"); // Liste des quêtes de l'utilisateur (Array)
931
+ */
932
+ async getUserQuests(username) {
933
+ let path = `${base}/users/${username}/quests`;
934
+
935
+ let result = {};
936
+
937
+ try {
938
+ const _ = await fetch(path, {
939
+ headers: {
940
+ Authorization: `Bearer ${this.#api_key}`,
941
+ },
942
+ });
943
+
944
+ result.ok = _.ok;
945
+ result.status = _.status;
946
+ result.statusText = _.statusText;
947
+
948
+ if (_.status === 429) {
949
+ result.retryAfter = _.headers.get("Retry-After");
950
+ }
951
+
952
+ let data = {};
953
+
954
+ if (result.ok) {
955
+ data = convertIds(await _.json());
956
+ }
957
+
958
+ result = {
959
+ ...result,
960
+ ...data,
961
+ };
962
+ } catch (error) {
963
+ result.ok = false;
964
+ result.status = 500;
965
+ result.statusText = "Internal Server Error";
966
+ result.error = error.message ?? error.toString();
967
+ }
968
+
969
+ return result;
970
+ }
971
+
972
+ /**
973
+ * ### Détail d'une quête
974
+ * Retourne une quête avec la liste de ses monstres. Supporte la pagination et les filtres.
975
+
976
+ * @param {string} username - Nom d'utilisateur
977
+ * @param {string} quest_slug - Id (slug) de la quête de l'utilisateur
978
+ * @param {object} [options]
979
+ * @param {"wanted"|"offered"} [options.status] - wanted (recherchés) ou offered (proposés). Par défaut : tous
980
+ * @param {number} [options.step] - Filtrer par numéro d'étape
981
+ * @param {number} [options.limit] - Nombre de résultats (défaut : 50, max : 200)
982
+ * @param {number} [options.offset] - Décalage pour la pagination (défaut : 0)
983
+
984
+ * @returns {Promise<{
985
+ * ok: boolean;
986
+ * status: number;
987
+ * statusText: string;
988
+ * error?: string,
989
+ * retryAfter?: number;
990
+ * data?: Array<Monster & { step: number, owned: number, status: number }>;
991
+ * pagination?: Pagination;
992
+ * }>}
993
+
994
+ * @example
995
+ * getUserQuestMonsters("ix-xs", "abcdef"); // Liste des quêtes de l'utilisateur
996
+ * getUserQuestMonsters("ix-xs", "abcdef", { status: "offered" }); // Filtrer par status
997
+ * getUserQuestMonsters("ix-xs", "abcdef", { step: 5 }); // Filtrer par numéro d'étape
998
+ * getUserQuestMonsters("ix-xs", "abcdef", { limit: 200, offset: 10 }); // 200 monstres, passe les 10 premiers
999
+ */
1000
+ async getUserQuestMonsters(username, quest_slug, options) {
1001
+ let path = `${base}/users/${username}/quests/${quest_slug}`;
1002
+ const queries = [];
1003
+
1004
+ if (options?.status) {
1005
+ queries.push(`status=${options.status}`);
1006
+ }
1007
+ if (options?.step) {
1008
+ queries.push(`step=${options.step}`);
1009
+ }
1010
+ if (options?.limit) {
1011
+ queries.push(`limit=${options.limit}`);
1012
+ }
1013
+ if (options?.offset) {
1014
+ queries.push(`offset=${options.offset}`);
1015
+ }
1016
+
1017
+ path += `?${queries.join("&")}`;
1018
+
1019
+ let result = {};
1020
+
1021
+ try {
1022
+ const _ = await fetch(path, {
1023
+ headers: {
1024
+ Authorization: `Bearer ${this.#api_key}`,
1025
+ },
1026
+ });
1027
+
1028
+ result.ok = _.ok;
1029
+ result.status = _.status;
1030
+ result.statusText = _.statusText;
1031
+
1032
+ if (_.status === 429) {
1033
+ result.retryAfter = _.headers.get("Retry-After");
1034
+ }
1035
+
1036
+ let data = {};
1037
+
1038
+ if (result.ok) {
1039
+ data = convertIds(await _.json());
1040
+
1041
+ data.data.monsters = data.data.monsters.map((m) => ({
1042
+ ...m,
1043
+ ...cache.monsters.find((x) => x.id === m.id),
1044
+ }));
1045
+
1046
+ data.pagination = data.data.pagination;
1047
+ data.data = data.data.monsters;
1048
+ }
1049
+
1050
+ result = {
1051
+ ...result,
1052
+ ...data,
1053
+ };
1054
+ } catch (error) {
1055
+ result.ok = false;
1056
+ result.status = 500;
1057
+ result.statusText = "Internal Server Error";
1058
+ result.error = error.message ?? error.toString();
1059
+ }
1060
+
1061
+ return result;
1062
+ }
1063
+
1064
+ /**
1065
+ * ### Partenaires d'échange
1066
+ * Trouve des utilisateurs avec qui échanger des monstres. Cet endpoint analyse votre quête et cherche d'autres joueurs sur le même serveur qui :
1067
+ * * Proposent des monstres que vous recherchez
1068
+ * * Recherchent des monstres que vous proposez
1069
+
1070
+
1071
+ * **💡 Note :**
1072
+ * Les résultats sont triés par match_score décroissant (nombre total de monstres en commun). Pour chaque monstre, available indique la quantité disponible à l'échange, needed le besoin, et covers_need si l'offre couvre entièrement le besoin.
1073
+
1074
+ * @typedef {object} MatchQuest
1075
+ * @property {string} slug
1076
+ * @property {string} character_name
1077
+ * @property {number} parallel_quests
1078
+
1079
+ * @typedef {Monster & { available: number, needed: number, covers_need: boolean }} Wanted
1080
+
1081
+ * @typedef {object} MatchData
1082
+ * @property {Array<Wanted>} they_have_you_want
1083
+ * @property {Array<Wanted>} you_have_they_want
1084
+
1085
+ * @typedef {object} Match
1086
+ * @property {Search} user
1087
+ * @property {MatchQuest} quest
1088
+ * @property {MatchData} matches
1089
+ * @property {number} match_score
1090
+
1091
+ * @param {string} user_api_key - Clé API de l'utilisateur
1092
+ * @param {string} quest_slug - Id (slug) de la quête de l'utilisateur
1093
+ * @param {object} [options]
1094
+ * @param {"they_have"|"they_want"|"both"} [options.direction] - Type de match à rechercher (défaut: both)
1095
+ * @param {number} [options.active_within_days] - Utilisateurs actifs dans les N derniers jours (défaut : 30, max : 365)
1096
+ * @param {number} [options.min_parallel_quests] - Nombre minimum de quêtes en parallèle du partenaire (défaut : 1, max : 20). Utile pour filtrer les joueurs "hardcore".
1097
+ * @param {number} [options.limit] - Nombre de résultats (défaut : 20, max : 50)
1098
+ * @param {number} [options.offset] - Décalage pour la pagination (défaut : 0)
1099
+
1100
+ * @returns {Promise<{
1101
+ * ok: boolean,
1102
+ * status: number,
1103
+ * statusText: string,
1104
+ * retryAfter?: number,
1105
+ * error?: string;
1106
+ * data?: Array<Match>,
1107
+ * pagination?: Pagination,
1108
+ * }>}
1109
+
1110
+ * @example
1111
+ * matchUserQuest("jean_api_key", "acbdef"); // Liste des correspondances (Array)
1112
+ * matchUserQuest("jean_api_key", "abcdef", { direction: "they_have" }); // Utilisateurs proposant les monstres que jean recherche
1113
+ * matchUserQuest("jean_api_key", "abcdef", { direction: "they_want" }); // Utilisateurs recherchant les monstres que jean propose
1114
+ * matchUserQuest("jean_api_key", "abcdef", { active_within_days: 365, limit: 50, offset: 10 }); // 50 Utilisateurs actifs dans les 365 derniers jours, passe les 10 premiers
1115
+ */
1116
+ async matchUserQuest(user_api_key, quest_slug, options) {
1117
+ let path = `${base}/quests/${quest_slug}/matches`;
1118
+ const queries = [];
1119
+
1120
+ if (options?.direction) {
1121
+ queries.push(`direction=${options.direction}`);
1122
+ }
1123
+ if (options?.active_within_days) {
1124
+ queries.push(`active_within_days=${options.active_within_days}`);
1125
+ }
1126
+ if (options?.min_parallel_quests) {
1127
+ queries.push(`min_parallel_quests=${options.min_parallel_quests}`);
1128
+ }
1129
+ if (options?.limit) {
1130
+ queries.push(`limit=${options.limit}`);
1131
+ }
1132
+ if (options?.offset) {
1133
+ queries.push(`offset=${options.offset}`);
1134
+ }
1135
+
1136
+ path += `?${queries.join("&")}`;
1137
+
1138
+ let result = {};
1139
+
1140
+ try {
1141
+ const _ = await fetch(path.replace("?", ""), {
1142
+ headers: {
1143
+ Authorization: `Bearer ${user_api_key}`,
1144
+ },
1145
+ });
1146
+
1147
+ result.ok = _.ok;
1148
+ result.status = _.status;
1149
+ result.statusText = _.statusText;
1150
+
1151
+ if (_.status === 429) {
1152
+ result.retryAfter = _.headers.get("Retry-After");
1153
+ }
1154
+
1155
+ let data = {};
1156
+
1157
+ if (result.ok) {
1158
+ data = convertIds(await _.json());
1159
+
1160
+ data.data = data.data.map((m) => ({
1161
+ ...m,
1162
+ matches: {
1163
+ they_have_you_want: m.matches.they_have_you_want.map((x) => ({
1164
+ ...x,
1165
+ ...cache.monsters.find((z) => z.id === x.id),
1166
+ })),
1167
+ you_have_they_want: m.matches.you_have_they_want.map((x) => ({
1168
+ ...x,
1169
+ ...cache.monsters.find((z) => z.id === x.id),
1170
+ })),
1171
+ },
1172
+ }));
1173
+ }
1174
+
1175
+ result = {
1176
+ ...result,
1177
+ ...data,
1178
+ };
1179
+ } catch (error) {
1180
+ result.ok = false;
1181
+ result.status = 500;
1182
+ result.statusText = "Internal Server Error";
1183
+ result.error = error.message ?? error.toString();
1184
+ }
1185
+
1186
+ return result;
1187
+ }
1188
+
1189
+ /**
1190
+ * ### Modifier les paramètres d'une quête
1191
+
1192
+ * @typedef {object} QuestUpdated
1193
+ * @property {string} slug
1194
+ * @property {string} [character_name]
1195
+ * @property {number} [parallel_quests]
1196
+ * @property {number} [current_step]
1197
+ * @property {boolean} [show_trades]
1198
+ * @property {number} [trade_mode]
1199
+ * @property {number|null} [trade_offer_threshold]
1200
+ * @property {number|null} [trade_want_threshold]
1201
+ * @property {boolean} [never_offer_normal]
1202
+ * @property {boolean} [never_want_normal]
1203
+ * @property {boolean} [never_offer_boss]
1204
+ * @property {boolean} [never_want_boss]
1205
+ * @property {boolean} [never_offer_archi]
1206
+ * @property {boolean} [never_want_archi]
1207
+
1208
+ * @param {string} user_api_key - Clé API de l'utilisateur
1209
+ * @param {string} quest_slug - Id (slug) de la quête de l'utilisateur
1210
+ * @param {object} options
1211
+ * @param {string} [options.character_name] - Nom du personnage (max 200 caractères)
1212
+ * @param {number} [options.parallel_quests] - Nombre de quêtes en parallèle (1-20)
1213
+ * @param {number} [options.current_step] - Étape courante (1-34)
1214
+ * @param {boolean} [options.show_trades] - Visibilité de la quête dans la communauté
1215
+ * @param {number} [options.trade_mode] - Mode de trading (0 = Automatique, 1 = Mode expert)
1216
+ * @param {number|null} [options.trade_offer_threshold] - Seuil minimal pour proposer en mode expert (0-30)
1217
+ * @param {number|null} [options.trade_want_threshold] - Seuil maximal pour rechercher en mode expert (0-30)
1218
+ * @param {boolean} [options.never_offer_normal] - Ne jamais proposer les monstres normaux (étapes 1-16)
1219
+ * @param {boolean} [options.never_want_normal] - Ne jamais rechercher les monstres normaux (étapes 1-16)
1220
+ * @param {boolean} [options.never_offer_boss] - Ne jamais proposer les boss (étapes 17-19)
1221
+ * @param {boolean} [options.never_want_boss] - Ne jamais rechercher les boss (étapes 17-19)
1222
+ * @param {boolean} [options.never_offer_archi] - Ne jamais proposer les archimonstres (étapes 20+)
1223
+ * @param {boolean} [options.never_want_archi] - Ne jamais rechercher les archimonstres (étapes 20+)
1224
+
1225
+ * @returns {Promise<{
1226
+ * ok: boolean,
1227
+ * status: number,
1228
+ * statusText: string,
1229
+ * error?: string,
1230
+ * retryAfter?: number,
1231
+ * data?: QuestUpdated,
1232
+ * }>}
1233
+
1234
+ * @example
1235
+ * updateUserQuest("jean_api_key", "abcdef", {
1236
+ * character_name: "Mon personnage",
1237
+ * parallel_quests: 5,
1238
+ * current_step: 12,
1239
+ * show_trades: true,
1240
+ * trade_mode: 1,
1241
+ * trade_offer_threshold: 6,
1242
+ * trade_want_threshold: 1
1243
+ * }); // Met à jour la quête de l'utilisateur et renvoi le payload (Object)
1244
+ */
1245
+ async updateUserQuest(user_api_key, quest_slug, options) {
1246
+ let path = `${base}/quests/${quest_slug}`;
1247
+ const _body = {};
1248
+
1249
+ if (options?.character_name) {
1250
+ _body.character_name = options.character_name;
1251
+ }
1252
+ if (options?.parallel_quests) {
1253
+ _body.parallel_quests = options.parallel_quests;
1254
+ }
1255
+ if (options?.current_step) {
1256
+ _body.current_step = options.current_step;
1257
+ }
1258
+ if (options?.show_trades) {
1259
+ _body.show_trades = options.show_trades;
1260
+ }
1261
+ if (options?.trade_mode) {
1262
+ _body.trade_mode = options.trade_mode;
1263
+ }
1264
+ if (options?.trade_offer_threshold) {
1265
+ _body.trade_offer_threshold = options.trade_offer_threshold;
1266
+ }
1267
+ if (options?.trade_want_threshold) {
1268
+ _body.trade_want_threshold = options.trade_want_threshold;
1269
+ }
1270
+ if (options?.never_offer_normal) {
1271
+ _body.never_offer_normal = options.never_offer_normal;
1272
+ }
1273
+ if (options?.never_want_normal) {
1274
+ _body.never_want_normal = options.never_want_normal;
1275
+ }
1276
+ if (options?.never_offer_boss) {
1277
+ _body.never_offer_boss = options.never_offer_boss;
1278
+ }
1279
+ if (options?.never_want_boss) {
1280
+ _body.never_want_boss = options.never_want_boss;
1281
+ }
1282
+ if (options?.never_offer_archi) {
1283
+ _body.never_offer_archi = options.never_offer_archi;
1284
+ }
1285
+ if (options?.never_want_archi) {
1286
+ _body.never_want_archi = options.never_want_archi;
1287
+ }
1288
+
1289
+ let result = {};
1290
+
1291
+ try {
1292
+ const _ = await fetch(path, {
1293
+ method: "PATCH",
1294
+ headers: {
1295
+ "Content-Type": "application/json",
1296
+ Authorization: `Bearer ${user_api_key}`,
1297
+ },
1298
+ body: JSON.stringify(_body),
1299
+ });
1300
+
1301
+ result.ok = _.ok;
1302
+ result.status = _.status;
1303
+ result.statusText = _.statusText;
1304
+
1305
+ if (_.status === 429) {
1306
+ result.retryAfter = _.headers.get("Retry-After");
1307
+ }
1308
+
1309
+ let data = {};
1310
+
1311
+ if (result.ok) {
1312
+ data = convertIds(await _.json());
1313
+ }
1314
+
1315
+ result = {
1316
+ ...result,
1317
+ ...data,
1318
+ };
1319
+ } catch (error) {
1320
+ result.ok = false;
1321
+ result.status = 500;
1322
+ result.statusText = "Internal Server Error";
1323
+ result.error = error.message ?? error.toString();
1324
+ }
1325
+
1326
+ return result;
1327
+ }
1328
+
1329
+ /**
1330
+ * ### Modifier plusieurs monstres d'un utilisateur
1331
+ * > **Limites** : - `quantity` : entre 0 et 30 - Maximum 200 monstres
1332
+
1333
+ * @typedef {object} UpdatedMonsters
1334
+ * @property {number} updated_count
1335
+ * @property {Array<Monster & { quantity: number, owned: number, status: number, effective_offer: number, effective_want: number }>} monsters
1336
+
1337
+ * @typedef {object} MonsterInput
1338
+ * @property {MonsterName} monster_name
1339
+ * @property {number} quantity
1340
+
1341
+ * @param {string} user_api_key
1342
+ * @param {string} quest_slug
1343
+ * @param {Array<MonsterInput>} monsters
1344
+
1345
+ * @returns {Promise<{
1346
+ * ok: boolean,
1347
+ * status: number,
1348
+ * statusText: string,
1349
+ * retryAfter?: number,
1350
+ * error?: string,
1351
+ * data?: UpdatedMonsters,
1352
+ * }>}
1353
+
1354
+ * @example
1355
+ * updateUserQuestMonsters("jean_api_key", "abcdef", [
1356
+ * { monster_name: "Aboub", quantity: 5 },
1357
+ * // ...autres monstres
1358
+ * ]); // Met à jour les monstres de l'utilisateur et renvoi le payload (Object)
1359
+ */
1360
+ async updateUserQuestMonsters(user_api_key, quest_slug, monsters) {
1361
+ let path = `${base}/quests/${quest_slug}/monsters`;
1362
+
1363
+ for (const monster of monsters) {
1364
+ const m = monsterByName(monster.monster_name);
1365
+
1366
+ if (!m) {
1367
+ throw new Error(`monster_name '${monster.monster_name}' doesn't exist`);
1368
+ }
1369
+ }
1370
+
1371
+ const _body = {
1372
+ monsters: monsters.map((m) => ({
1373
+ monster_id: monsterByName(m.monster_name).id,
1374
+ quantity: m.quantity,
1375
+ })),
1376
+ };
1377
+
1378
+ let result = {};
1379
+
1380
+ try {
1381
+ const _ = await fetch(path, {
1382
+ method: "PATCH",
1383
+ headers: {
1384
+ "Content-Type": "application/json",
1385
+ Authorization: `Bearer ${user_api_key}`,
1386
+ },
1387
+ body: JSON.stringify(_body),
1388
+ });
1389
+
1390
+ result.ok = _.ok;
1391
+ result.status = _.status;
1392
+ result.statusText = _.statusText;
1393
+
1394
+ if (_.status === 429) {
1395
+ result.retryAfter = _.headers.get("Retry-After");
1396
+ }
1397
+
1398
+ let data = {};
1399
+
1400
+ if (result.ok) {
1401
+ data = convertIds(await _.json());
1402
+
1403
+ data.data.monsters = data.data.monsters.map((m) => {
1404
+ const monster = cache.monsters.find((x) => x.id === m.monster_id);
1405
+
1406
+ delete m.monster_id;
1407
+
1408
+ return {
1409
+ ...monster,
1410
+ ...m,
1411
+ };
1412
+ });
1413
+ }
1414
+
1415
+ result = {
1416
+ ...result,
1417
+ ...data,
1418
+ };
1419
+ } catch (error) {
1420
+ result.ok = false;
1421
+ result.status = 500;
1422
+ result.statusText = "Internal Server Error";
1423
+ result.error = error.message ?? error.toString();
1424
+ }
1425
+
1426
+ return result;
1427
+ }
1428
+
1429
+ /**
1430
+ * ### Paramètres de trade manuels
1431
+ * Permet de forcer les quantités proposées et recherchées pour un monstre, au lieu d'utiliser le calcul automatique basé sur le statut.
1432
+
1433
+ * @typedef {object} TradeInput
1434
+ * @property {number|null} [trade_offer] - Quantité à proposer (0 à owned). null = calcul automatique
1435
+ * @property {number|null} [trade_want] - Quantité recherchée (0 à 30). null = calcul automatique
1436
+
1437
+ * @param {string} user_api_key
1438
+ * @param {string} quest_slug
1439
+ * @param {MonsterName} monster_name
1440
+ * @param {TradeInput} options
1441
+
1442
+ * @returns {Promise<{
1443
+ * ok: boolean,
1444
+ * status: number,
1445
+ * statusText: string,
1446
+ * retryAfter?: number,
1447
+ * error? string,
1448
+ * data?: Monster & { trade_offer: number|null, trade_want: number|null },
1449
+ * }>}
1450
+
1451
+ * @example
1452
+ * updateUserQuestMonsterTrade("jean_api_key", "abcdef", "Aboub", { trade_offer: 1, trade_want: null }); // Met a jour le monstre Aboub (propose = 1, recherche = calcul automatique) et renvoi le payload ()
1453
+ */
1454
+ async updateUserQuestMonsterTrade(
1455
+ user_api_key,
1456
+ quest_slug,
1457
+ monster_name,
1458
+ options,
1459
+ ) {
1460
+ const monster = monsterByName(monster_name);
1461
+
1462
+ if (!monster) {
1463
+ throw new Error(`monster_name '${monster_name}' doesn't exist`);
1464
+ }
1465
+
1466
+ let path = `${base}/quests/${quest_slug}/monsters/${monster.id}/trade`;
1467
+
1468
+ const _body = {
1469
+ trade_offer: options.trade_offer,
1470
+ trade_want: options.trade_want,
1471
+ };
1472
+
1473
+ console.log("BODY SENT:", JSON.stringify(_body, null, 2));
1474
+
1475
+ let result = {};
1476
+
1477
+ try {
1478
+ const _ = await fetch(path, {
1479
+ method: "PATCH",
1480
+ headers: {
1481
+ "Content-Type": "application/json",
1482
+ Authorization: `Bearer ${user_api_key}`,
1483
+ },
1484
+ body: JSON.stringify(_body),
1485
+ });
1486
+
1487
+ result.ok = _.ok;
1488
+ result.status = _.status;
1489
+ result.statusText = _.statusText;
1490
+
1491
+ if (_.status === 429) {
1492
+ result.retryAfter = _.headers.get("Retry-After");
1493
+ }
1494
+
1495
+ let data = {};
1496
+
1497
+ if (result.ok) {
1498
+ data = convertIds(await _.json());
1499
+
1500
+ data.data = {
1501
+ ...cache.monsters.find((x) => x.id === data.data.monster_id),
1502
+ ...data.data,
1503
+ };
1504
+
1505
+ delete data.data.monster_id;
1506
+ }
1507
+
1508
+ result = {
1509
+ ...result,
1510
+ ...data,
1511
+ };
1512
+ } catch (error) {
1513
+ result.ok = false;
1514
+ result.status = 500;
1515
+ result.statusText = "Internal Server Error";
1516
+ result.error = error.message ?? error.toString();
1517
+ }
1518
+
1519
+ return result;
1520
+ }
1521
+ };