├── .gitignore ├── favicon.ico ├── favicon.png ├── img ├── github.webp ├── preview.jpg ├── logos │ ├── wiki.png │ ├── twitter.svg │ ├── discord.svg │ ├── reddit.svg │ ├── steam.svg │ ├── tiktok.svg │ └── instagram.svg └── characters │ ├── poe.gif │ ├── arca.gif │ ├── cosmo.gif │ ├── gains.gif │ ├── gallo.gif │ ├── ghoul.gif │ ├── lama.gif │ ├── leda.gif │ ├── mccoy.gif │ ├── menya.gif │ ├── miang.gif │ ├── onna.gif │ ├── osole.gif │ ├── porta.gif │ ├── ramba.gif │ ├── rose.gif │ ├── sammy.gif │ ├── sigma.gif │ ├── smith.gif │ ├── tony.gif │ ├── ambrojoe.gif │ ├── antonio.gif │ ├── assunta.gif │ ├── avatar.gif │ ├── cavallo.gif │ ├── clerici.gif │ ├── concetta.gif │ ├── divano.gif │ ├── dommario.gif │ ├── eleanor.gif │ ├── exdash.gif │ ├── gennaro.gif │ ├── giovanna.gif │ ├── imelda.gif │ ├── jeneviv.gif │ ├── keitha.gif │ ├── krochi.gif │ ├── maruto.gif │ ├── minnah.gif │ ├── peppino.gif │ ├── poppea.gif │ ├── pugnala.gif │ ├── random.gif │ ├── reaper.gif │ ├── scorej.gif │ ├── syuuto.gif │ ├── toastie.gif │ ├── trouser.gif │ ├── christine.gif │ ├── genevieve.gif │ ├── gyorunton.gif │ ├── luminaire.gif │ ├── marrabbio.gif │ ├── megamenya.gif │ ├── megasyuuto.gif │ ├── mortaccio.gif │ └── pasqualina.gif ├── .prettierrc ├── scripts ├── build-images.js └── build-data.js └── index2.html /.gitignore: -------------------------------------------------------------------------------- 1 | scripts -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/favicon.ico -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/favicon.png -------------------------------------------------------------------------------- /img/github.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/github.webp -------------------------------------------------------------------------------- /img/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/preview.jpg -------------------------------------------------------------------------------- /img/logos/wiki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/logos/wiki.png -------------------------------------------------------------------------------- /img/characters/poe.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/poe.gif -------------------------------------------------------------------------------- /img/characters/arca.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/arca.gif -------------------------------------------------------------------------------- /img/characters/cosmo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/cosmo.gif -------------------------------------------------------------------------------- /img/characters/gains.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/gains.gif -------------------------------------------------------------------------------- /img/characters/gallo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/gallo.gif -------------------------------------------------------------------------------- /img/characters/ghoul.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/ghoul.gif -------------------------------------------------------------------------------- /img/characters/lama.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/lama.gif -------------------------------------------------------------------------------- /img/characters/leda.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/leda.gif -------------------------------------------------------------------------------- /img/characters/mccoy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/mccoy.gif -------------------------------------------------------------------------------- /img/characters/menya.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/menya.gif -------------------------------------------------------------------------------- /img/characters/miang.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/miang.gif -------------------------------------------------------------------------------- /img/characters/onna.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/onna.gif -------------------------------------------------------------------------------- /img/characters/osole.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/osole.gif -------------------------------------------------------------------------------- /img/characters/porta.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/porta.gif -------------------------------------------------------------------------------- /img/characters/ramba.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/ramba.gif -------------------------------------------------------------------------------- /img/characters/rose.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/rose.gif -------------------------------------------------------------------------------- /img/characters/sammy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/sammy.gif -------------------------------------------------------------------------------- /img/characters/sigma.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/sigma.gif -------------------------------------------------------------------------------- /img/characters/smith.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/smith.gif -------------------------------------------------------------------------------- /img/characters/tony.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/tony.gif -------------------------------------------------------------------------------- /img/characters/ambrojoe.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/ambrojoe.gif -------------------------------------------------------------------------------- /img/characters/antonio.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/antonio.gif -------------------------------------------------------------------------------- /img/characters/assunta.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/assunta.gif -------------------------------------------------------------------------------- /img/characters/avatar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/avatar.gif -------------------------------------------------------------------------------- /img/characters/cavallo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/cavallo.gif -------------------------------------------------------------------------------- /img/characters/clerici.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/clerici.gif -------------------------------------------------------------------------------- /img/characters/concetta.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/concetta.gif -------------------------------------------------------------------------------- /img/characters/divano.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/divano.gif -------------------------------------------------------------------------------- /img/characters/dommario.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/dommario.gif -------------------------------------------------------------------------------- /img/characters/eleanor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/eleanor.gif -------------------------------------------------------------------------------- /img/characters/exdash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/exdash.gif -------------------------------------------------------------------------------- /img/characters/gennaro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/gennaro.gif -------------------------------------------------------------------------------- /img/characters/giovanna.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/giovanna.gif -------------------------------------------------------------------------------- /img/characters/imelda.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/imelda.gif -------------------------------------------------------------------------------- /img/characters/jeneviv.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/jeneviv.gif -------------------------------------------------------------------------------- /img/characters/keitha.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/keitha.gif -------------------------------------------------------------------------------- /img/characters/krochi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/krochi.gif -------------------------------------------------------------------------------- /img/characters/maruto.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/maruto.gif -------------------------------------------------------------------------------- /img/characters/minnah.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/minnah.gif -------------------------------------------------------------------------------- /img/characters/peppino.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/peppino.gif -------------------------------------------------------------------------------- /img/characters/poppea.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/poppea.gif -------------------------------------------------------------------------------- /img/characters/pugnala.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/pugnala.gif -------------------------------------------------------------------------------- /img/characters/random.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/random.gif -------------------------------------------------------------------------------- /img/characters/reaper.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/reaper.gif -------------------------------------------------------------------------------- /img/characters/scorej.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/scorej.gif -------------------------------------------------------------------------------- /img/characters/syuuto.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/syuuto.gif -------------------------------------------------------------------------------- /img/characters/toastie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/toastie.gif -------------------------------------------------------------------------------- /img/characters/trouser.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/trouser.gif -------------------------------------------------------------------------------- /img/characters/christine.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/christine.gif -------------------------------------------------------------------------------- /img/characters/genevieve.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/genevieve.gif -------------------------------------------------------------------------------- /img/characters/gyorunton.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/gyorunton.gif -------------------------------------------------------------------------------- /img/characters/luminaire.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/luminaire.gif -------------------------------------------------------------------------------- /img/characters/marrabbio.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/marrabbio.gif -------------------------------------------------------------------------------- /img/characters/megamenya.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/megamenya.gif -------------------------------------------------------------------------------- /img/characters/megasyuuto.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/megasyuuto.gif -------------------------------------------------------------------------------- /img/characters/mortaccio.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/mortaccio.gif -------------------------------------------------------------------------------- /img/characters/pasqualina.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danmer/vs-build-planner/HEAD/img/characters/pasqualina.gif -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 1000, 3 | "semi": false, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "endOfLine": "lf" 7 | } 8 | -------------------------------------------------------------------------------- /img/logos/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /img/logos/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /scripts/build-images.js: -------------------------------------------------------------------------------- 1 | const { readFileSync, existsSync } = require('fs') 2 | const { execSync } = require('child_process') 3 | 4 | const src = `${__dirname}/src` 5 | const dst = `${__dirname}/img` 6 | 7 | // dlc1: 'moonspell', 'moonspellChars' 8 | // dlc2: 'foscariChars', 'weaponData_Foscari', 'stageData_Foscari' 9 | const sprites = ['characters', 'enemies', 'enemies2', 'enemies3', 'enemiesM', 'items', 'randomazzo', 'UI'] 10 | 11 | try { 12 | for (const sprite of sprites) { 13 | const framesById = getFramesById(sprite) 14 | for (const id in framesById) { 15 | const frames = framesById[id] 16 | for (const frame of frames) { 17 | const framePath = `${dst}/${sprite}_${frame.filename}` 18 | if (!existsSync(framePath)) { 19 | execSync(`magick ${src}/${sprite}.png -crop ${frame.w}x${frame.h}+${frame.x}+${frame.y} ${framePath}`) 20 | } 21 | } 22 | if (frames.length > 1) { 23 | console.log(`making gif for ${sprite} / ${id}`) 24 | const maxW = frames.reduce((max, frame) => Math.max(frame.w, max), 0) 25 | const maxH = frames.reduce((max, frame) => Math.max(frame.h, max), 0) 26 | // TODO: make sure if gravity and page size work? 27 | execSync(`magick -loop 0 -dispose previous -delay 20 -gravity South -page ${maxW}x${maxH}+0+0 ${dst}/${sprite}_${id}*.png ${dst}/${sprite}_${id}.gif`) 28 | } 29 | } 30 | } 31 | } catch (error) { 32 | console.error(error) 33 | } 34 | 35 | function getFramesById(sprite) { 36 | const data = JSON.parse('' + readFileSync(`${src}/${sprite}.json`)) 37 | if (!data.textures || !data.textures[0]) { 38 | return {} 39 | } 40 | const texture = data.textures[0] 41 | const framesById = {} 42 | for (const frame of texture.frames) { 43 | // ignore death animations of enemies 44 | const [filename, id, index] = sprite.includes('enemies') ? frame.filename.match(/(\w+)_i(\d+)\.png/) || [] : frame.filename.match(/(\w+)_(\d\d)\.png/) || frame.filename.match(/([a-zA-Z_]+i?\d?)(\d)\.png/i) || frame.filename.match(/(.+)\.png/) || [] 45 | // const [filename, id, index] = (sprite.includes('enemies') ? frame.filename.match(/(\w+)_i(\d+)\.png/) : frame.filename.match(/(.+)\.png/)) || [] 46 | if (id) { 47 | const { w, h, x, y } = frame.frame 48 | framesById[id] = framesById[id] || [] 49 | framesById[id].push({ id, index, filename, w, h, x, y }) 50 | } 51 | } 52 | return framesById 53 | } 54 | -------------------------------------------------------------------------------- /img/logos/reddit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /img/logos/steam.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | -------------------------------------------------------------------------------- /img/logos/tiktok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | -------------------------------------------------------------------------------- /img/logos/instagram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /scripts/build-data.js: -------------------------------------------------------------------------------- 1 | // Script parses the game files and collect the data and css-icons for the website 2 | // magick.exe - must be available to execute from command line (ImageMagick) 3 | 4 | const { readFileSync, writeFileSync } = require('fs') 5 | const { execSync } = require('child_process') 6 | 7 | console.log('building...') 8 | 9 | // src must contain main.bundle.js from Vampire Survivors/resources/app/.webpack/renderer 10 | // and all used files from Vampire Survivors\resources\app\.webpack\renderer\assets\img and from DLC: Vampire Survivors\2230760\scripts\data 11 | // characters.json, characters.png, enemies.json, enemies.png, enemies2.json, enemies2.png, enemies3.json, enemies3.png, enemiesM.json, enemiesM.png, items.json, items.png, randomazzo.json, randomazzo.png, UI.json, UI.png 12 | // moonspell.json, moonspell.png, moonspellChars.json, moonspellChars.png, characterData_Moonspell.json, weaponData_Moonspell.json, stageData_Moonspell.json 13 | const src = `${__dirname}/src` 14 | // dst will contain data.js and icons.css 15 | const dst = `${__dirname}/dst` 16 | 17 | const defaultCharacterStats = { maxHp: 100, armor: 0, regen: 0, moveSpeed: 1, power: 1, cooldown: 1, area: 1, speed: 1, duration: 1, amount: 0, luck: 1, growth: 1, greed: 1, curse: 1, magnet: 0, revivals: 0, rerolls: 0, skips: 0, banish: 0 } 18 | const defaultWeaponStats = { area: 1, speed: 1, amount: 1, chance: 1 } 19 | const defaultStageStats = { ClockSpeed: 1, EnemySpeed: 1, EnemyHealth: 1, GoldMultiplier: 1, XPBonus: 1, TimeLimit: 1800, PlayerPxSpeed: 1, ProjectileSpeed: 1, LuckBonus: 0, tips: '' } 20 | 21 | const characters = [ 22 | { id: 'antonio', name: 'Antonio', emoji: ':charAntonioVS:', itemIds: ['whip'] }, 23 | { id: 'imelda', name: 'Imelda', emoji: ':charImeldaVS:', itemIds: ['magicwand'] }, 24 | { id: 'pasqualina', name: 'Pasqualina', emoji: ':charPasqualinaVS:', itemIds: ['runetracer'] }, 25 | { id: 'gennaro', name: 'Gennaro', emoji: ':charGennaroVS:', itemIds: ['knife'] }, 26 | { id: 'arca', name: 'Arca', emoji: ':charArcaVS:', itemIds: ['firewand'] }, 27 | { id: 'porta', name: 'Porta', emoji: ':charPortaVS:', itemIds: ['lightning'] }, 28 | { id: 'lama', name: 'Lama', emoji: ':charLamaVS:', itemIds: ['axe'] }, 29 | { id: 'poe', name: 'Poe', emoji: ':charPoeVS:', itemIds: ['garlic'] }, 30 | { id: 'clerici', name: 'Clerici', emoji: ':charClericiVS:', itemIds: ['water'] }, 31 | { id: 'dommario', name: 'Dommario', emoji: ':charDommarioVS:', itemIds: ['bible'] }, 32 | { id: 'krochi', name: 'Krochi', emoji: ':charKrochiVS:', itemIds: ['cross'] }, 33 | { id: 'christine', name: 'Christine', emoji: ':charChristineVS:', itemIds: ['pentagram'] }, 34 | { id: 'pugnala', name: 'Pugnala', emoji: ':charPugnalaVS:', itemIds: ['guns1', 'guns2'] }, 35 | { id: 'poppea', name: 'Poppea', emoji: ':charPoppeaVS:', itemIds: ['mana'] }, 36 | { id: 'mortaccio', name: 'Mortaccio', emoji: ':charMortaccioVS:', itemIds: ['bone'] }, 37 | { id: 'cavallo', name: 'Cavallo', emoji: ':charYattaCavalloVS', itemIds: ['cherry'] }, 38 | { id: 'ramba', name: 'Ramba', emoji: ':charBiancaRambaVS:', itemIds: ['cart'] }, 39 | { id: 'osole', name: 'O`Sole', emoji: ':charOSoleMeeo2VS:', itemIds: ['flowers'] }, 40 | { id: 'giovanna', name: 'Giovanna', emoji: ':charGiovannaVS:', itemIds: ['cat'] }, 41 | { id: 'concetta', name: 'Concetta', emoji: ':charConcettaVS:', itemIds: ['pinion'] }, 42 | { id: 'gallo', name: 'Gallo', emoji: ':charIguanaGalloVS:', itemIds: ['lancet'] }, 43 | { id: 'divano', name: 'Divano', emoji: ':charDivanoVS:', itemIds: ['laurel'] }, 44 | { id: 'assunta', name: 'Zi`Assunta', emoji: ':charZiAssuntaVS:', itemIds: ['vento'] }, 45 | { id: 'ambrojoe', name: 'Ambrojoe', emoji: ':charSirAmbrojoeVS:', itemIds: ['furniture'] }, 46 | { id: 'sigma', name: 'Sigma', emoji: ':charQueenSigmaVS:', itemIds: ['sword'] }, 47 | { id: 'shemoon', name: 'She-Moon', emoji: ':question:', itemIds: ['fandango'] }, 48 | // dlc 1 49 | { id: 'miang', name: 'Miang', emoji: ':charMiangVS:', itemIds: ['wind'], dlc: true }, 50 | { id: 'menya', name: 'Menya', emoji: ':charMenyaVS:', itemIds: ['seasons'], dlc: true }, 51 | { id: 'syuuto', name: 'Syuuto', emoji: ':charSyuutoVS:', itemIds: ['night'], dlc: true }, 52 | { id: 'megamenya', name: 'Menya', emoji: ':charMegaloMenyaVS:', itemIds: ['bocce'], dlc: true }, 53 | { id: 'megasyuuto', name: 'Syuuto', emoji: ':charMegaloSyuutoVS:', itemIds: ['muramasa'], dlc: true }, 54 | { id: 'onna', name: 'Babi-Onna', emoji: ':charBabiOnnaVS:', itemIds: ['mirage'], dlc: true }, 55 | { id: 'tony', name: "Gab'Et-Oni", emoji: ':charGavEtOniVS:', itemIds: ['bolle'], dlc: true }, 56 | { id: 'mccoy', name: 'McCoy-Oni', emoji: ':charMccoyVS:', itemIds: ['bocce'], dlc: true }, 57 | // dlc 2 58 | { id: 'eleanor', name: 'Eleanor Uziron', emoji: ':charEleanorVS:', itemIds: ['spell1'], dlc2: true }, 59 | { id: 'maruto', name: 'Maruto Cuts', emoji: ':charMarutoVS:', itemIds: ['eskizzibur'], dlc2: true }, 60 | { id: 'keitha', name: 'Keitha Muort', emoji: ':charKeithaVS:', itemIds: ['arrow'], dlc2: true }, 61 | { id: 'luminaire', name: 'Luminaire Foscari', emoji: ':charLuminaireVS:', itemIds: ['prism'], dlc2: true }, 62 | { id: 'genevieve', name: 'Genevieve Gruyère', emoji: ':charGenevieveVS:', itemIds: ['servant'], dlc2: true }, 63 | { id: 'jeneviv', name: 'Je-Ne-Viv', emoji: ':charJeNeVivVS:', itemIds: ['servant'], dlc2: true }, 64 | { id: 'sammy', name: 'Sammy', emoji: ':charSammyVS:', itemIds: ['cat_'], dlc2: true }, 65 | { id: 'ghoul', name: "Rottin'Ghoul", emoji: ':charRottinGhoulVS:', itemIds: ['popper'], dlc2: true }, 66 | // secret 67 | { id: 'exdash', name: 'Exdash', emoji: ':charExdashVS:', itemIds: ['bird2'], special: true }, 68 | { id: 'toastie', name: 'Toastie', emoji: ':charToastieVS:', itemIds: ['bird1'], special: true }, 69 | { id: 'smith', name: 'Smith', emoji: ':charSmithVS:', itemIds: ['bird_'], special: true }, 70 | { id: 'leda', name: 'Leda', emoji: ':charLedaVS:', itemIds: ['magicwand_'], special: true }, 71 | { id: 'reaper', name: 'Red Death', emoji: ':charReddeathVS:', itemIds: ['axe_'], special: true }, 72 | { id: 'marrabbio', name: 'Marrabbio', emoji: ':charBoonMarrabbioVS:', itemIds: ['knife_'], special: true }, 73 | { id: 'minnah', name: 'Minnah', emoji: ':charMinnahVS:', itemIds: ['whip_'], special: true }, 74 | { id: 'peppino', name: 'Peppino', emoji: ':charPeppinoVS:', itemIds: ['garlic_'], special: true }, 75 | { id: 'gains', name: 'Gains', emoji: ':charGainsBorosVS:', itemIds: ['cross_'], special: true }, 76 | { id: 'gyorunton', name: 'Gyorunton', emoji: ':charGyoruntonVS:', itemIds: ['bracelet'], special: true }, 77 | { id: 'cosmo', name: 'Cosmo', emoji: ':animCosmoPavoneVS:', itemIds: ['bird1', 'bird2'], special: true }, 78 | { id: 'trouser', name: 'Trouser', emoji: ':BigTrouserVS:', itemIds: ['candybox'], special: true }, 79 | { id: 'random', name: 'Random', emoji: ':charRandomVS:', itemIds: [], special: true }, 80 | { id: 'avatar', name: 'Avatar', emoji: ':charAvatarInfernasVS:', itemIds: ['flame'], special: true }, 81 | { id: 'scorej', name: 'Scorej-Oni', emoji: ':charScorejOniVS:', itemIds: ['lightning'], special: true }, 82 | { id: 'rose', name: 'Rose De Infernas', emoji: ':question:', itemIds: ['vento_'], special: true }, 83 | ] 84 | 85 | const weapons = [ 86 | { id: 'magicwand', name: 'Magic Wand', emoji: ':magicwandVS:' }, 87 | { id: 'firewand', name: 'Fire Wand', emoji: ':firewandVS:' }, 88 | { id: 'axe', name: 'Axe', emoji: ':axeVS:' }, 89 | { id: 'lightning', name: 'Lightning Ring', emoji: ':lightningringVS:' }, 90 | { id: 'knife', name: 'Knife', emoji: ':knifeVS:' }, 91 | { id: 'bible', name: 'King Bible', emoji: ':kingbibleVS:' }, 92 | { id: 'cross', name: 'Cross', emoji: ':crossVS:' }, 93 | { id: 'garlic', name: 'Garlic', emoji: ':garlicVS:' }, 94 | { id: 'pentagram', name: 'Pentagram', emoji: ':pentagramVS:' }, 95 | { id: 'runetracer', name: 'Runetracer', emoji: ':runetracerVS:' }, 96 | { id: 'water', name: 'Santa Water', emoji: ':santawaterVS:' }, 97 | { id: 'whip', name: 'Whip', emoji: ':whipVS:' }, 98 | { id: 'guns1', name: 'Phiera Der Tuphello', emoji: ':phieraVS:' }, 99 | { id: 'guns2', name: 'Eight The Sparrow', emoji: ':sparrowVS:' }, 100 | { id: 'bird1', name: 'Peachone', emoji: ':peachoneVS:' }, 101 | { id: 'bird2', name: 'Ebony Wings', emoji: ':ebonywingsVS:' }, 102 | { id: 'mana', name: 'Song Of Mana', emoji: ':manaVS:' }, 103 | { id: 'cat', name: 'Gatti Amari', emoji: ':catVS:' }, 104 | { id: 'pinion', name: 'Shadow Pinion', emoji: ':shadowpinionVS:' }, 105 | { id: 'vento', name: 'Vento Sacro', emoji: ':ventosacroVS:' }, 106 | { id: 'lancet', name: 'Clock Lancet', emoji: ':lancetVS:' }, 107 | { id: 'laurel', name: 'Laurel', emoji: ':laurelVS:' }, 108 | { id: 'sword', name: 'Victory Sword', emoji: ':victoryswordVS:' }, 109 | { id: 'flame', name: 'Flames of Misspell', emoji: ':flamesofmisspellVS:' }, 110 | { id: 'jubilee', name: 'Greatest Jubilee', emoji: ':greatestjubileeVS:' }, 111 | { id: 'bracelet', name: 'Bracelet', emoji: ':braceletVS:' }, 112 | { id: 'bone', name: 'Bone', emoji: ':boneVS:', special: true }, 113 | { id: 'cherry', name: 'Cherry Bomb', emoji: ':cherrybombVS:', special: true }, 114 | { id: 'cart', name: 'Carréllo', emoji: ':carrelloVS:', special: true }, 115 | { id: 'flowers', name: 'Celestial Dusting', emoji: ':celestialdustingVS:', special: true }, 116 | { id: 'furniture', name: 'La Robba', emoji: ':larobbaVS:', special: true }, 117 | { id: 'fandango', name: 'Glass Fandango', emoji: ':glassfandangoVS:', special: true }, 118 | // dlc 1 119 | { id: 'wind', name: 'Silver Wind', emoji: ':SilverWindVS:', dlc: true }, 120 | { id: 'seasons', name: 'Four Seasons', emoji: ':FourSeasonsVS:', dlc: true }, 121 | { id: 'night', name: 'Summon Night', emoji: ':SummonNightVS:', dlc: true }, 122 | { id: 'mirage', name: 'Mirage Robe', emoji: ':MirageRobeVS:', dlc: true }, 123 | { id: 'muramasa', name: 'Night Sword', emoji: ':NightSwordVS:', dlc: true }, 124 | { id: 'bolle', name: 'Mille Bolle Blu', emoji: ':MilleBolleBluVS:', dlc: true }, 125 | { id: 'bocce', name: '108 Bocce', emoji: ':108BocceVS:', dlc: true }, 126 | // dlc 2 127 | { id: 'spell1', name: 'SpellString', emoji: ':SpellStringVS:', dlc2: true }, 128 | { id: 'spell2', name: 'SpellStream', emoji: ':SpellStreamVS:', dlc2: true }, 129 | { id: 'spell3', name: 'SpellStrike', emoji: ':SpellStrikeVS:', dlc2: true }, 130 | { id: 'eskizzibur', name: 'Eskizzibur', emoji: ':EskizziburVS:', dlc2: true }, 131 | { id: 'arrow', name: 'Flash Arrow', emoji: ':FlashArrowVS:', dlc2: true }, 132 | { id: 'prism', name: 'Prismatic Missile', emoji: ':PrismaticMissileVS:', dlc2: true }, 133 | { id: 'servant', name: 'Shadow Servant', emoji: ':ShadowServantVS:', dlc2: true }, 134 | { id: 'popper', name: 'Party Popper', emoji: ':PartyPopperVS:', dlc2: true }, 135 | ] 136 | 137 | const evolutions = [ 138 | { id: 'magicwand_', name: 'Holy Wand', emoji: ':holywandVS:', itemIds: ['magicwand', 'cooldown'] }, 139 | { id: 'firewand_', name: 'Hellfire', emoji: ':hellfireVS:', itemIds: ['firewand', 'might'] }, 140 | { id: 'axe_', name: 'Death Spiral', emoji: ':deathspiralVS:', itemIds: ['axe', 'area'] }, 141 | { id: 'lightning_', name: 'Thunder Loop', emoji: ':thunderloopVS:', itemIds: ['lightning', 'amount'] }, 142 | { id: 'knife_', name: 'Thousand Edge', emoji: ':thousandedgeVS:', itemIds: ['knife', 'speed'] }, 143 | { id: 'bible_', name: 'Unholy Vespers', emoji: ':unholyvespersVS:', itemIds: ['bible', 'duration'] }, 144 | { id: 'cross_', name: 'Heaven Sword', emoji: ':heavenswordVS:', itemIds: ['cross', 'luck'] }, 145 | { id: 'garlic_', name: 'Soul Eater', emoji: ':souleaterVS:', itemIds: ['garlic', 'recovery'] }, 146 | { id: 'pentagram_', name: 'Gorgeous Moon', emoji: ':gorgeousmoonVS:', itemIds: ['pentagram', 'growth'] }, 147 | { id: 'runetracer_', name: 'NO FUTURE', emoji: ':NOFUTUREVS:', itemIds: ['runetracer', 'armor'] }, 148 | { id: 'water_', name: 'La Borra', emoji: ':laborraVS:', itemIds: ['water', 'magnet'] }, 149 | { id: 'mana_', name: 'Mannajja', emoji: ':mannajjaVS:', itemIds: ['mana', 'curse'] }, 150 | { id: 'whip_', name: 'Bloody Tear', emoji: ':bloodytearVS:', itemIds: ['whip', 'health'] }, 151 | { id: 'cat_', name: 'Vicious Hunger', emoji: ':vicioushungerVS:', itemIds: ['cat', 'greed'] }, 152 | { id: 'pinion_', name: 'Valkyrie Turner', emoji: ':valkyrieVS:', itemIds: ['pinion', 'wings'] }, 153 | { id: 'sword_', name: 'Sole Solution', emoji: ':solesolutionVS:', itemIds: ['sword', 'torrona'] }, 154 | { id: 'flame_', name: 'Ashes of Muspell', emoji: ':ashesofmuspellVS:', itemIds: ['flame', 'torrona'] }, 155 | { id: 'lancet_', name: 'Infinite Corridor', emoji: ':infinitecorridorVS:', itemIds: ['lancet', 'ring1', 'ring2'] }, 156 | { id: 'laurel_', name: 'Crimson Shroud', emoji: ':cloakVS:', itemIds: ['laurel', 'sign1', 'sign2'] }, 157 | // special 158 | { id: 'bird_', name: 'Vandalier', emoji: ':vandalierVS:', itemIds: ['bird1', 'bird2'], special: true }, 159 | { id: 'guns_', name: 'Phieraggi', emoji: ':phieraggiVS:', itemIds: ['guns1', 'guns2', 'revival'], special: true }, 160 | { id: 'vento_', name: 'Fuwalafuwaloo', emoji: ':fuwalafuwalooVS:', itemIds: ['vento', 'whip_'], special: true }, 161 | { id: 'bracelet_', name: 'Bi-Bracelet', emoji: ':bibraceletVS:', itemIds: ['bracelet'], special: true }, 162 | { id: 'bracelet__', name: 'Tri-Bracelet', emoji: ':tribraceletVS:', itemIds: ['bracelet_'], special: true }, 163 | // dlc 1 164 | { id: 'wind_', name: 'Festive Winds', itemIds: ['wind', 'recovery'], emoji: ':FestiveWindsVS:', dlc: true }, 165 | { id: 'muramasa_', name: 'Muramasa', itemIds: ['muramasa', 'greed'], emoji: ':MuraMasaVS:', dlc: true }, 166 | { id: 'seasons_', name: 'Godai Shuffle', itemIds: ['seasons', 'might', 'area'], emoji: ':GodaiShuffleVS:', dlc: true }, 167 | { id: 'night_', name: "Night's Reach", itemIds: ['night', 'amount'], emoji: ':EchoNightVS:', dlc: true }, 168 | { id: 'mirage_', name: "J'Odore", itemIds: ['mirage', 'magnet'], emoji: ':JOdoreVS:', dlc: true }, 169 | { id: 'bolle_', name: 'Boo Roo Boolle', itemIds: ['bolle', 'duration'], emoji: ':BooRooBoolleVS:', dlc: true }, 170 | // dlc 2 171 | { id: 'spell_', name: 'SpellStrom', itemIds: ['spell1', 'spell2', 'spell3'], emoji: ':SpellStromVS:', dlc2: true }, 172 | { id: 'eskizzibur_', name: 'Legionnaire', itemIds: ['eskizzibur', 'armor'], emoji: ':LegionnaireVS:', dlc2: true }, 173 | { id: 'arrow_', name: 'Millionaire', itemIds: ['arrow', 'speed', 'luck'], emoji: ':MillionaireVS:', dlc2: true }, 174 | { id: 'prism_', name: 'Luminaire Luminaire', itemIds: ['prism', 'growth'], emoji: ':LuminaireVS:', dlc2: true }, 175 | { id: 'servant_', name: 'Ophion', itemIds: ['servant', 'curse'], emoji: ':OphionVS:', dlc2: true }, 176 | ] 177 | 178 | const passives = [ 179 | { id: 'cooldown', name: 'Empty Tome', emoji: ':emptytomeVS:' }, 180 | { id: 'might', name: 'Spinach', emoji: ':spinachVS:' }, 181 | { id: 'area', name: 'Candelabrador', emoji: ':candelabradorVS:' }, 182 | { id: 'amount', name: 'Duplicator', emoji: ':duplicatorVS:' }, 183 | { id: 'speed', name: 'Bracer', emoji: ':bracerVS:' }, 184 | { id: 'duration', name: 'Spellbinder', emoji: ':spellbinderVS:' }, 185 | { id: 'luck', name: 'Clover', emoji: ':cloverVS:' }, 186 | { id: 'recovery', name: 'Pummarola', emoji: ':pummarolaVS:' }, 187 | { id: 'growth', name: 'Crown', emoji: ':crownVS:' }, 188 | { id: 'armor', name: 'Armor', emoji: ':armorVS:' }, 189 | { id: 'magnet', name: 'Attractorb', emoji: ':attractorbVS:' }, 190 | { id: 'curse', name: 'Skull O`Maniac', emoji: ':curseVS:' }, 191 | { id: 'health', name: 'Hollow Heart', emoji: ':hollowheartVS:' }, 192 | { id: 'revival', name: 'Tiragisú', emoji: ':tiragisuVS:' }, 193 | { id: 'wings', name: 'Wings', emoji: ':wingsVS:' }, 194 | { id: 'greed', name: 'Stone Mask', emoji: ':maskVS:' }, 195 | { id: 'torrona', name: 'Torrona`s Box', emoji: ':torronasboxVS:' }, 196 | { id: 'ring1', name: 'Silver Ring', emoji: ':silverringVS:', special: true }, 197 | { id: 'ring2', name: 'Gold Ring', emoji: ':goldringVS:', special: true }, 198 | { id: 'sign1', name: 'Metaglio Left', emoji: ':metaglioleftVS:', special: true }, 199 | { id: 'sign2', name: 'Metaglio Right', emoji: ':metagliorightVS:', special: true }, 200 | { id: 'badge', name: 'Academy Badge', emoji: ':AcademyBadgeVS:', dlc2: true }, 201 | ] 202 | 203 | const powerups = [ 204 | { id: 'cooldown', name: 'Cooldown', emoji: ':emptytomeVS:' }, 205 | { id: 'might', name: 'Might', emoji: ':spinachVS:' }, 206 | { id: 'area', name: 'Area', emoji: ':candelabradorVS:' }, 207 | { id: 'amount', name: 'Amount', emoji: ':duplicatorVS:' }, 208 | { id: 'speed', name: 'Speed', emoji: ':bracerVS:' }, 209 | { id: 'duration', name: 'Duration', emoji: ':spellbinderVS:' }, 210 | { id: 'luck', name: 'Luck', emoji: ':cloverVS:' }, 211 | { id: 'recovery', name: 'Recovery', emoji: ':pummarolaVS:' }, 212 | { id: 'growth', name: 'Growth', emoji: ':crownVS:' }, 213 | { id: 'armor', name: 'Armor', emoji: ':armorVS:' }, 214 | { id: 'magnet', name: 'Magnet', emoji: ':attractorbVS:' }, 215 | { id: 'curse', name: 'Curse', emoji: ':curseVS:' }, 216 | { id: 'health', name: 'Max Health', emoji: ':hollowheartVS:' }, 217 | { id: 'revival', name: 'Revival', emoji: ':tiragisuVS:' }, 218 | { id: 'wings', name: 'MoveSpeed', emoji: ':wingsVS:' }, 219 | { id: 'greed', name: 'Greed', emoji: ':maskVS:' }, 220 | { id: 'omni', name: 'Omni', emoji: ':torronasboxVS:', special: true }, 221 | { id: 'reroll', name: 'Reroll', emoji: ':rerollVS:', special: true }, 222 | { id: 'skip', name: 'Skip', emoji: ':skipVS:', special: true }, 223 | { id: 'banish', name: 'Banish', emoji: ':banishVS:', special: true }, 224 | ] 225 | 226 | const arcanas = [ 227 | { id: 'arcana0', name: 'Game Killer', emoji: ':0GameKillerVS:', itemIds: ['experience', 'chest'] }, 228 | { id: 'arcana1', name: 'Gemini', emoji: ':IGeminiVS:', itemIds: ['bird1', 'bird2', 'bird_', 'guns1', 'guns2', 'guns_', 'cat', 'cat_', 'popper', 'servant', 'servant_', 'fritta'] }, 229 | { id: 'arcana2', name: 'Twilight Requiem', emoji: ':IITwilightRequiemVS:', itemIds: ['bible', 'bible_', 'lightning', 'lightning_', 'bird1', 'bird2', 'runetracer', 'pinion', 'bone', 'flowers', 'bracelet_', 'wind', 'wind_', 'prism', 'prism_'] }, 230 | { id: 'arcana3', name: 'Tragic Princess', emoji: ':IIITragicPrincess:', itemIds: ['cooldown', 'garlic', 'garlic_', 'water', 'water_', 'lightning', 'lightning_', 'cart'] }, 231 | { id: 'arcana4', name: 'Awake', emoji: ':4VS:', itemIds: ['revival', 'health', 'armor', 'might', 'area', 'duration', 'speed', 'eskizzibur'] }, 232 | { id: 'arcana5', name: 'Chaos in the Dark Night', emoji: ':5VS:', itemIds: ['speed'] }, 233 | { id: 'arcana6', name: 'Sarabande of Healing', emoji: ':6VS:', itemIds: ['recovery', 'revival', 'whip_', 'vento_', 'garlic_', 'wind', 'wind_', 'muramasa', 'muramasa_', 'chicken', 'flowers'] }, 234 | { id: 'arcana7', name: 'Iron Blue Will', emoji: ':7VS:', itemIds: ['knife', 'knife_', 'axe', 'axe_', 'guns1', 'guns2', 'cart', 'arrow', 'arrow_'] }, 235 | { id: 'arcana8', name: 'Mad Groove', emoji: ':VIIIMadGrooveVS:', itemIds: [] }, 236 | { id: 'arcana9', name: 'Divine Bloodline', emoji: ':IXDivineBloodlinesVS:', itemIds: ['armor', 'health', 'cross', 'bible', 'garlic', 'water', 'lightning', 'mana', 'vento', 'sword', 'wind'] }, 237 | { id: 'arcana10', name: 'Beginning', emoji: ':XBeginningVS:', itemIds: ['amount', 'bone', 'cherry', 'cart', 'flowers', 'furniture'] }, 238 | { id: 'arcana11', name: 'Waltz of Pearls', emoji: ':11VS:', itemIds: ['magicwand', 'magicwand_', 'firewand', 'firewand_', 'cross', 'cross_', 'cart'] }, 239 | { id: 'arcana12', name: 'Out of Bounds', emoji: ':XIIOutOfBounds:', itemIds: ['lancet', 'lancet_', 'mirage', 'mirage_', 'orologion'] }, 240 | { id: 'arcana13', name: 'Wicked Season', emoji: ':XIIIWickedSeasonVS:', itemIds: ['growth', 'luck', 'greed', 'curse'] }, 241 | { id: 'arcana14', name: 'Jail of Crystal', emoji: ':XIVJailOfCrystal:', itemIds: ['magicwand', 'magicwand_', 'runetracer', 'runetracer_', 'bracelet', 'guns2', 'guns4', 'bird3', 'prism', 'prism_'] }, 242 | { id: 'arcana15', name: 'Disco of Gold', emoji: ':XVDiscoOfGoldVS:', itemIds: ['cat_', 'greed', 'coin', 'coinbag', 'richcoinbag', 'bigcoinbag', 'gildedclover', 'chest'] }, 243 | { id: 'arcana16', name: 'Slash', emoji: ':16VS:', itemIds: ['knife', 'knife_', 'whip', 'whip_', 'axe', 'axe_', 'vento', 'vento_', 'cross_', 'sword', 'muramasa_', 'eskizzibur', 'eskizzibur_', 'arrow', 'arrow_'] }, 244 | { id: 'arcana17', name: 'Lost & Found Painting', emoji: ':17VS:', itemIds: ['duration'] }, 245 | { id: 'arcana18', name: 'Boogaloo of Illusions', emoji: ':XVIIIBoogalOofIllusions:', itemIds: ['area'] }, 246 | { id: 'arcana19', name: 'Heart of Fire', emoji: ':19VS:', itemIds: ['firewand', 'firewand_', 'guns1', 'pinion_', 'bracelet__', 'prism', 'prism_', 'guns3', 'bird4', 'brazier'] }, 247 | { id: 'arcana20', name: 'Silent Old Sanctuary', emoji: ':XXSilentOldSanctuary:', itemIds: ['reroll', 'skip', 'banish'] }, 248 | { id: 'arcana21', name: 'Blood Astronomia', emoji: ':XXIBloodAstronomiaVS:', itemIds: ['amount', 'magnet', 'garlic', 'garlic_', 'pentagram', 'pentagram_', 'mana', 'mana_', 'lancet', 'laurel'] }, 249 | ] 250 | 251 | const stages = [ 252 | { id: 'forest', name: 'Mad Forest', itemIds: ['might', 'luck', 'health', 'recovery', 'curse'] }, 253 | { id: 'library', name: 'Inlaid Library', itemIds: ['cooldown', 'greed'] }, 254 | { id: 'plant', name: 'Dairy Plant', itemIds: ['magnet', 'area', 'wings', 'armor'] }, 255 | { id: 'tower', name: 'Gallo Tower', itemIds: ['speed', 'duration'] }, 256 | { id: 'capella', name: 'Cappella Magna', itemIds: ['growth', 'revival', 'amount'] }, 257 | { id: 'moonspell', name: 'Mt.Moonspell', itemIds: ['might', 'recovery', 'area', 'amount', 'magnet', 'greed', 'muramasa'], dlc: true }, 258 | { id: 'lake', name: 'Lake Foscari', itemIds: ['armor', 'speed', 'luck', 'growth', 'curse', 'badge' /* BoxArcana */], dlc2: true }, 259 | { id: 'abyss', name: 'Abyss Foscari', itemIds: ['cooldown', 'magnet', 'greed', 'armor', 'growth', 'curse', 'torrona', 'badge', 'prism', 'servant'], dlc2: true }, 260 | { id: 'acres', name: 'Green Acres', itemIds: [], special: true }, 261 | { id: 'bonezone', name: 'The Bone Zone', itemIds: [], special: true }, 262 | { id: 'molise', name: 'Il Molise', itemIds: [], special: true }, 263 | { id: 'bridge', name: 'Tiny Bridge', itemIds: ['wings', 'vento', 'magnet', 'lightning'], special: true }, 264 | { id: 'moongolow', name: 'Moongolow', itemIds: ['cooldown', 'might', 'area', 'amount', 'speed', 'duration', 'luck', 'recovery', 'growth', 'armor', 'magnet', 'curse', 'health', 'revival', 'wings', 'greed'], special: true }, 265 | { id: 'bosses', name: 'Boss Rash', itemIds: ['cooldown', 'might', 'area', 'amount', 'speed', 'duration', 'luck', 'recovery', 'growth', 'armor', 'magnet', 'curse', 'health', 'revival', 'wings', 'greed'], special: true }, 266 | { id: 'whiteout', name: 'Whiteout', itemsIds: ['fandanga', 'might', 'wings', 'curse'], special: true }, 267 | { id: 'bats', name: 'Bat Country', itemsIds: [], special: true }, 268 | { id: 'astral', name: 'Astral Stair', itemsIds: ['guns1', 'guns2'], special: true }, 269 | ] 270 | 271 | const pickups = [ 272 | { id: 'bigcoinbag', name: 'Big Coin Bag' }, 273 | { id: 'coinbag', name: 'Coin Bag' }, 274 | { id: 'experience', name: 'Experience Gem' }, 275 | { id: 'chicken', name: 'Floor Chicken' }, 276 | { id: 'gildedclover', name: 'Gilded Clover' }, 277 | { id: 'glassvizard', name: 'Glass Vizard' }, 278 | { id: 'coin', name: 'Gold Coin' }, 279 | { id: 'egg', name: 'Golden Egg' }, 280 | { id: 'littleclover', name: 'Little Clover' }, 281 | { id: 'littleheart', name: 'Little Heart' }, 282 | { id: 'banger', name: 'Magic Banger' }, 283 | { id: 'map', name: 'Milky Way Map' }, 284 | { id: 'fritta', name: 'Nduja Fritta' }, 285 | { id: 'orologion', name: 'Orologion' }, 286 | { id: 'randomazzo', name: 'Randomazzo' }, 287 | { id: 'richcoinbag', name: 'Rich Coin Bag' }, 288 | { id: 'rosary', name: 'Rosary' }, 289 | { id: 'tears', name: 'Sorceress` Tears' }, 290 | { id: 'chest', name: 'Treasure Chest' }, 291 | { id: 'arcana', name: 'BoxArcana' }, 292 | { id: 'vacuum', name: 'Vacuum' }, 293 | { id: 'yellowsign', name: 'Yellow Sign' }, 294 | ] 295 | 296 | const structures = [ 297 | { id: 'lampost', name: 'Lampost' }, 298 | { id: 'wagon', name: 'Cart' }, 299 | { id: 'brazier', name: 'Brazier' }, 300 | { id: 'brazier2', name: 'Brazier2' }, 301 | { id: 'candelabrone', name: 'Candelabrone' }, 302 | ] 303 | 304 | const counterparts = [ 305 | { id: 'bird3', name: 'Cygnus', itemsIds: [] }, 306 | { id: 'bird4', name: 'Zhar Ptytsia', itemsIds: [] }, 307 | { id: 'guns3', name: 'Red Muscle', itemsIds: [] }, 308 | { id: 'guns4', name: 'Twice Upon a Time', itemsIds: [] }, 309 | { id: 'cat2', name: 'Flock Destroyer', itemsIds: [] }, 310 | { id: 'pooper', name: 'Party Pooper', dlc2: true }, 311 | { id: 'sliver', name: 'Silver Sliver', dlc2: true }, 312 | { id: 'insatiable', name: 'Insatiable', dlc2: true }, 313 | { id: 'candybox', name: 'Candybox', special: true }, 314 | { id: 'candybox_', name: 'Super Candybox II Turbo', special: true }, 315 | ] 316 | 317 | const enemies = [] 318 | const achievements = [] 319 | const authors = [] 320 | 321 | const items = [...characters, ...weapons, ...counterparts, ...passives, ...powerups, ...arcanas, ...stages, ...pickups, ...structures] 322 | 323 | const allFramesById = {} 324 | const powerupParams = ['level', 'maxHp', 'armor', 'regen', 'moveSpeed', 'power', 'cooldown', 'area', 'speed', 'duration', 'amount', 'luck', 'growth', 'greed', 'curse', 'magnet', 'revivals', 'rerolls', 'skips', 'banish'] 325 | 326 | // all: 'amount', 'area', 'bulletType', 'chance', 'charges', 'code', 'critChance', 'critMul', 'desc', 'description', 'duration', 'evoInto', 'evolvesFrom', 'evoSynergy', 'frameName', 'hidden', 'hitBoxDelay', 'hitsWalls', 'hitVFX', 'interval', 'intervalDependsOnDuration', 'isEvolution', 'isUnlocked', 'knockback', 'level', 'name', 'penetrating', 'poolLimit', 'power', 'rarity', 'repeatInterval', 'requires', 'requiresMax', 'speed', 'texture', 'tips', 'volume', 327 | // ignored: 'bulletType', 'code', 'evoInto', 'evolvesFrom', 'evoSynergy', 'frameName', 'hidden', 'hitBoxDelay', 'hitVFX', 'intervalDependsOnDuration', 'isEvolution', 'isUnlocked', 'name', 'requires', 'requiresMax', 'texture', 'volume', 328 | const baseWeaponParams = ['description', 'poolLimit', 'critChance', 'critMul', 'hitsWalls', 'knockback', 'rarity', 'tips'] 329 | const levelWeaponParams = [...powerupParams, 'chance', 'charges', 'desc', 'penetrating', 'interval', 'repeatInterval'] 330 | 331 | // all: 'amount', 'area', 'armor', 'banish', 'bgm', 'charName', 'code', 'completedStages', 'cooldown', 'curse', 'debugEnemies', 'debugTime', 'description', 'duration', 'exLevels', 'exWeapons', 'flipper', 'greed', 'growth', 'hidden', 'isBought', 'level', 'luck', 'magnet', 'maxHp', 'moveSpeed', 'noHurt', 'onEveryLevelUp', 'portraitName', 'power', 'prefix', 'price', 'regen', 'rerolls', 'revivals', 'showcase', 'skips', 'speed', 'spriteName', 'startingWeapon', 'surname', 'textureName', 'walkFrameRate', 'walkingFrames', 'sineMight','sineSpeed','sineDuration','sineArea','sineCooldown'} 332 | // ignored: 'charName', 'code', 'exWeapons' (pungala), 'flipper' (leda), 'hidden', 'isBought', 'noHurt' (flowers), 'portraitName', 'showcase', 'spriteName', 'startingWeapon', 'textureName', 'walkFrameRate', 'walkingFrames', 333 | const baseCharacterParams = ['prefix', 'surname', 'description', 'onEveryLevelUp', 'price', 'sineMight', 'sineSpeed', 'sineDuration', 'sineArea', 'sineCooldown'] 334 | const levelCharacterParams = [...powerupParams] 335 | 336 | // all: 'armor', 'bulletType', 'code', 'description', 'frameName', 'isPowerUp', 'level', 'name', 'price', 'rarity', 'texture', 'unlockedRank' (armor), 337 | // ignored: 'bulletType', 'code', 'frameName', 'isPowerUp', 'texture', 'unlockedRank', 338 | const basePassiveParams = ['description', 'isPowerUp', 'name', 'rarity', 'price'] 339 | const levelPassiveParams = [...powerupParams] 340 | 341 | // all: 'arcanaHolder', 'arcanaTreasure', 'background', 'BGM', 'BGMMOD', 'BGTextureName', 'bosses', 'cff', 'code', 'dayNight', 'description', 'destructibleChance', 'destructibleChanceMax', 'destructibleFreq', 'destructibleType', 'enemies', 'events', 'frameName', 'frameNameUnlock', 'frequency', 'hidden', 'hyper', 'hyperTips', 'legacyBGM', 'maxDestructibles', 'minimum', 'minute', 'mods', 'pizzaEvents', 'pizzaIntervalInMilliSeconds', 'spawnType', 'stageName', 'stageNumber', 'startingSpawns', 'texture', 'tilemapPos', 'tilemapTiledIMG', 'tilemapTiledJSON', 'tileset', 'tips', 'treasure', 'uiFrame', 'uiTexture', 'unlocked', 342 | // mods: 'BGM_detune', 'BGM_ignoreModsForNewSoundtrack', 'BGM_new_rate', 'BGM_rate', 'ClockSpeed', 'EnemyHealth', 'EnemyMinimumMul', 'EnemySpeed', 'GoldMultiplier', 'LuckBonus', 'PlayerPxSpeed', 'ProjectileSpeed', 'StartingSpawns', 'TimeLimit', 'tips', 'unlocked', 'XPBonus', 343 | // ignored: 'arcanaHolder', 'code', 'arcanaTreasure', 'background', 'BGM', 'BGTextureName', 'BGMMOD', 'bosses', 'cff', 'dayNight', 'destructibleChance', 'destructibleChanceMax', 'destructibleFreq', 'destructibleType', 'enemies', 'events', 'frameName', 'frameNameUnlock', 'frequency', 'hidden', 'hyper', 'hyperTips', 'legacyBGM', 'maxDestructibles', 'minimum', 'minute', 'pizzaEvents', 'pizzaIntervalInMilliSeconds', 'spawnType', 'stageName', 'stageNumber', 'startingSpawns', 'texture', 'tilemapPos', 'tilemapTiledIMG', 'tilemapTiledJSON', 'tileset', 'tips', 'treasure', 'uiFrame', 'uiTexture', 'unlocked', 344 | const baseStageParams = ['description', 'mods', 'hyper'] 345 | const modStageParams = ['ClockSpeed', 'EnemyHealth', 'EnemySpeed', 'GoldMultiplier', 'LuckBonus', 'PlayerPxSpeed', 'ProjectileSpeed', 'TimeLimit', 'tips', 'XPBonus'] 346 | const minuteStageParams = [] 347 | 348 | // all: 'achievementTips', 'description', 'feverMS', 'frameName', 'hidden', 'inTreasures', 'isRare', 'isRelic', 'name', 'pickedupAmount', 'rarity', 'seen', 'texture', 'tips', 'unlocksAt', 'value', 349 | // ignored: 'frameName', 'hidden', 'pickedupAmount', ''seen', 'texture', 350 | const basePickupParams = ['achievementTips', 'description', 'feverMS', 'inTreasures', 'isRare', 'isRelic', 'name', 'rarity', 'tips', 'unlocksAt', 'value'] 351 | 352 | // all: 'destroyedAmount', 'frameName', 'maxHp', 'textureName', 353 | // ignored: 'destructibleType', 'destroyedAmount', 'textureName', 354 | const baseDestructibleParams = ['maxHp', 'frameName'] 355 | 356 | // all: 'arcanaType', 'description', 'enabled', 'frameName', 'items', 'major', 'name', 'texture', 'unlocked', 'weapons', 357 | // ignored: 'arcanaType', 'enabled', 'frameName', 'items', 'texture', 'unlocked', 'weapons', 358 | const baseArcanaParams = ['description', 'major', 'name'] 359 | 360 | // all: 'alias', 'alpha', 'bulletType', 'code', 'colliderOverride', 'deathKB', 'end', 'feverValue', 'fireDelay', 'flagName', 'frameNames', 'idleFrameCount', 'killedAmount', 'knockback', 'level', 'lives' 'maxHp', 'maxKnockback', 'maxSpeed', 'moreX', 'moreY' 'passThroughWalls', 'patrolDuration', 'power', 'res_Debuffs', 'res_Freeze', 'res_Knockback', 'res_Rosary', 'scale', 'skills', 'speed', 'textureName', 'tint', 'xp', 361 | const baseEnemyParams = [] 362 | 363 | const imagesById = {} 364 | 365 | try { 366 | let data = '' + readFileSync(`${src}/main.bundle.js`) 367 | let fixedData = '' 368 | 369 | const groupsMatch = data.matchAll(/[a-z0-9]+=(\{\[.+?(?=;|},_))/g) 370 | 371 | for (const [, rawGroupString] of groupsMatch) { 372 | const fixedGroupString = fixJsonString(rawGroupString) 373 | console.log(fixedGroupString) 374 | fixedData += '\n\n' + fixedGroupString 375 | try { 376 | const parsedGroup = JSON.parse(fixedGroupString) 377 | if (fixedGroupString.includes('"startingWeapon"')) { 378 | Object.assign(parsedGroup, require(`${__dirname}/src/characterData_Moonspell.json`)) 379 | Object.assign(parsedGroup, require(`${__dirname}/src/characterData_Foscari.json`)) 380 | } 381 | if (fixedGroupString.includes('"evoSynergy"')) { 382 | Object.assign(parsedGroup, require(`${__dirname}/src/weaponData_Moonspell.json`)) 383 | Object.assign(parsedGroup, require(`${__dirname}/src/weaponData_Foscari.json`)) 384 | } 385 | if (fixedGroupString.includes('"stageName"')) { 386 | Object.assign(parsedGroup, require(`${__dirname}/src/stageData_Moonspell.json`)) 387 | Object.assign(parsedGroup, require(`${__dirname}/src/stageData_Foscari.json`)) 388 | } 389 | const objects = [] 390 | for (const code in parsedGroup) { 391 | if (parsedGroup[code][0]) { 392 | parsedGroup[code][0].code = code 393 | } 394 | objects.push(parsedGroup[code]) 395 | } 396 | const object0 = objects[0][0] || objects[0] 397 | const type = object0.textureName || object0.texture || object0.uiTexture || '' 398 | if (type === 'characters') { 399 | for (const character of characters) { 400 | const levels = objects.find((lvls) => lvls[0] && lvls[0].charName === character.name && (!character.id.includes('mega') || lvls[0].prefix?.includes('Megalo'))) 401 | if (levels) { 402 | levels.sort((a, b) => a.level - b.level) 403 | Object.assign(character, pickFields(levels[0], baseCharacterParams)) 404 | character.levels = levels.map((level) => pickFields(level, levelCharacterParams)) 405 | // delete extra growth 406 | for (let index = 1; index < character.levels.length; index++) { 407 | const level = character.levels[index] 408 | if (level.growth) { 409 | if (level.growth >= 1) { 410 | level.growth = parseFloat((level.growth - 1).toFixed(3)) 411 | } 412 | if (level.growth <= -1) { 413 | level.growth = parseFloat((level.growth + 1).toFixed(3)) 414 | } 415 | if (level.growth === 0) { 416 | delete level.growth 417 | } 418 | } 419 | } 420 | // delete default values 421 | for (const param in defaultCharacterStats) { 422 | if (typeof character.levels[0][param] === 'undefined' || character.levels[0][param] === defaultCharacterStats[param]) { 423 | delete character.levels[0][param] 424 | } else { 425 | character.levels[0][param] = parseFloat((character.levels[0][param] - defaultCharacterStats[param]).toFixed(3)) 426 | } 427 | } 428 | character.levels = character.levels.filter((level) => Object.values(level).length > 1) 429 | // save image 430 | imagesById[character.id] = getImage(levels[0].textureName, levels[0].spriteName) 431 | } 432 | } 433 | } else if (type === 'items') { 434 | for (const weapon of weapons.concat(evolutions).concat(counterparts)) { 435 | const levels = objects.find((lvls) => lvls[0] && lvls[0].name === weapon.name) 436 | if (levels) { 437 | for (let level = 0; level < levels.length; level++) { 438 | levels[level].level = levels[level].level || level + 1 439 | } 440 | Object.assign(weapon, pickFields(levels[0], baseWeaponParams)) 441 | weapon.levels = levels.map((level) => pickFields(level, levelWeaponParams)) 442 | // delete default values 443 | for (const param in defaultWeaponStats) { 444 | if (!weapon.levels[0][param] || weapon.levels[0][param] === defaultWeaponStats[param]) { 445 | delete weapon.levels[0][param] 446 | } else { 447 | // weapon.levels[0][param] = parseFloat((weapon.levels[0][param] - defaultWeaponStats[param]).toFixed(3)) 448 | } 449 | } 450 | // delete some useless params 451 | if (weapon.id.includes('firewand') || weapon.id.includes('axe')) { 452 | delete weapon.levels[0].duration 453 | } 454 | if (weapon.id.includes('vento')) { 455 | delete weapon.levels[0].duration 456 | } 457 | if (weapon.id === 'laurel') { 458 | delete weapon.levels[0].power 459 | } 460 | // save image 461 | imagesById[weapon.id] = getImage(levels[0].texture, levels[0].frameName) 462 | } 463 | } 464 | 465 | for (const passive of passives.concat(powerups)) { 466 | const levels = objects.find((lvls) => lvls[0] && lvls[0].name === passive.name) 467 | if (levels) { 468 | for (let level = 0; level < levels.length; level++) { 469 | levels[level].level = levels[level].level || level + 1 470 | } 471 | Object.assign(passive, pickFields(levels[0], basePassiveParams)) 472 | passive.levels = levels.map((level) => pickFields(level, levelPassiveParams)) 473 | // save image 474 | imagesById[passive.id] = getImage(levels[0].texture, levels[0].frameName) 475 | } 476 | } 477 | 478 | for (const pickup of pickups) { 479 | const object = objects.find((obj) => !obj[0] && obj.name === pickup.name) 480 | if (object) { 481 | Object.assign(pickup, pickFields(object, basePickupParams)) 482 | // save image 483 | imagesById[pickup.id] = getImage(object.texture, object.frameName) 484 | } 485 | } 486 | 487 | for (const destructible of structures) { 488 | const object = objects.find((obj) => !obj[0] && obj.frameName === destructible.name) 489 | if (object) { 490 | Object.assign(destructible, pickFields(object, baseDestructibleParams)) 491 | // save image 492 | imagesById[destructible.id] = getImage(object.textureName, object.frameName) 493 | } 494 | } 495 | } else if (type === 'randomazzo') { 496 | for (const arcana of arcanas) { 497 | const object = objects.find((obj) => obj.name.includes(arcana.name)) 498 | if (object) { 499 | Object.assign(arcana, pickFields(object, baseArcanaParams)) 500 | // save image 501 | imagesById[arcana.id] = getImage(object.texture, object.frameName.padStart(2, 0)) 502 | } 503 | } 504 | } else if (type === 'UI') { 505 | for (const minutes of objects) { 506 | const stage = stages.find((stg) => minutes[0] && minutes[0].stageName === stg.name) 507 | if (stage) { 508 | Object.assign(stage, pickFields(minutes[0], baseStageParams)) 509 | stage.hyper = pickFields(stage.hyper, modStageParams) 510 | stage.mods = pickFields(stage.mods, modStageParams) 511 | // delete default values 512 | for (const param in defaultStageStats) { 513 | if (!stage.mods[param] || stage.mods[param] === defaultStageStats[param]) { 514 | delete stage.mods[param] 515 | } 516 | if (!stage.hyper[param] || stage.hyper[param] === defaultStageStats[param]) { 517 | delete stage.hyper[param] 518 | } 519 | } 520 | // stage.minutes = minutes.map((minute) => pickFields(minute, minuteStageParams)) 521 | // save image 522 | imagesById[stage.id] = getImage(minutes[0].uiTexture, minutes[0].uiFrame) 523 | } else { 524 | // console.log(`Stage ${stage.name} not found`) 525 | } 526 | } 527 | //console.log(parsedObjects) 528 | } else if (type.includes('enemies')) { 529 | // enemies 530 | } else if ('achieved' in objects[0]) { 531 | // achievements 532 | } else if ('author' in objects[0]) { 533 | // authors 534 | } else if ('hitFrameName' in objects[0]) { 535 | // hits 536 | } else { 537 | const specialStages = objects.filter((a) => a.length && a.stageName) 538 | for (const minutes of specialStages) { 539 | const stage = stages.find((stg) => minutes && minutes[0].stageName === stg.name) 540 | if (stage) { 541 | Object.assign(stage, pickFields(minutes[0], baseStageParams)) 542 | stage.hyper = pickFields(stage.hyper, modStageParams) 543 | stage.mods = pickFields(stage.mods, modStageParams) 544 | // delete default values 545 | for (const param in defaultStageStats) { 546 | if (!stage.mods[param] || stage.mods[param] === defaultStageStats[param]) { 547 | delete stage.mods[param] 548 | } 549 | if (!stage.hyper[param] || stage.hyper[param] === defaultStageStats[param]) { 550 | delete stage.hyper[param] 551 | } 552 | } 553 | // stage.minutes = minutes.map((minute) => pickFields(minute, minuteStageParams)) 554 | // save image 555 | imagesById[stage.id] = getImage(minutes[0].uiTexture, minutes[0].uiFrame) 556 | } 557 | } 558 | } 559 | } catch (error) { 560 | console.log(error) 561 | // writeFileSync(`${src}/error_${Date.now()}.js`, fixedGroupString) 562 | } 563 | } 564 | 565 | // collect all fields from all levels 566 | // const fields = new Set() 567 | // for (const levelOrMinute of levelOrMinutes) { 568 | // for (const feild in levelOrMinute) { 569 | // fields.add(feild) 570 | // } 571 | // } 572 | // console.log(fields) 573 | 574 | // temporary delete levels 575 | for (const object of characters.concat(weapons, evolutions, counterparts, passives, powerups)) { 576 | delete object.levels 577 | } 578 | 579 | writeFileSync(`${dst}/main.bundle_extracted.js`, fixedData) 580 | writeFileSync(`${dst}/data.js`, `window.vs = ${JSON.stringify({ characters, weapons, evolutions, counterparts, passives, powerups, arcanas, pickups, structures, stages }, null, 2)}`) 581 | // writeFileSync(`${dst}/data.js`, `window.vs = ${JSON.stringify({ characters, weapons, evolutions, passives, powerups, arcanas, pickups, structures, stages, defaultCharacterStats, defaultWeaponStats, defaultStageStats }, null, 2)}`) 582 | // writeFileSync(`${dst}/data.min.js`, `window.vs = ${JSON.stringify({ characters, weapons, evolutions, passives, powerups, arcanas, pickups, structures, stages, defaultCharacterStats, defaultWeaponStats, defaultStageStats })}`) 583 | writeFileSync(`${dst}/icons.css`, getCSS()) 584 | } catch (error) { 585 | console.log(error) 586 | } 587 | 588 | writeFileSync(`${dst}/framesById.json`, JSON.stringify(allFramesById, null, 2)) 589 | 590 | function fixJsonString(objectString) { 591 | objectString = ('' + objectString) 592 | .replaceAll(/\\'/g, `\``) // protect escaped quotes from replacing 593 | .replaceAll(/'"/g, `"\\"`) // protect double quotes 594 | .replaceAll(/"'/g, `\\""`) // protect double qoutes 595 | .replaceAll(/'(.*?(?='))'/g, '"$1"') // replace single quotes to double quotes 596 | .replaceAll(/\\`/g, `'`) // bring protected quotes back 597 | .replaceAll(/_?\w+\(((_|\w)+)\)/g, '"$1"') // replace encrypted functions 598 | .replaceAll(/_?\w+\["((_|\w)+)"\]/g, '"$1"') // replace encrypted objects 599 | .replaceAll(/"\w+"\[/g, `[`) // remove extra wrappers 600 | .replaceAll(/"\w+"\:\[\],/g, ``) // remove empty arrays 601 | .replaceAll(/\[\[("\w+")]]:/g, '$1:') // unwrap double array-keys 602 | .replaceAll(/\[("\w+")]:/g, '$1:') // unwrap array-keys 603 | .replaceAll(':!0x1', ':false') 604 | .replaceAll(':!0x0', ':true') 605 | 606 | // find hexadecimal numbers 607 | const numbersMatch = objectString.matchAll(/(0x[a-f0-9]+)/gim) 608 | // sort hexadecimal number by length to prevent replacing parts of them 609 | const uniqNumbers = [...new Set([...numbersMatch].map((numberMatch) => numberMatch[1]))].sort((a, b) => b.length - a.length) 610 | // replace hexadecimal numbers 611 | for (const uniqNumber of uniqNumbers) { 612 | objectString = objectString.replaceAll(uniqNumber, +uniqNumber) 613 | } 614 | 615 | // fix brackets 616 | let brackets = '' 617 | for (const letter of objectString) { 618 | if (letter === '{') { 619 | brackets = '}' + brackets 620 | } 621 | if (letter === '[') { 622 | brackets = ']' + brackets 623 | } 624 | if (letter === '}' || letter === ']') { 625 | brackets = brackets.replace(letter, '') 626 | } 627 | } 628 | 629 | return objectString + brackets 630 | } 631 | 632 | // levels[0].textureName, levels[0].spriteName 633 | function getImage(textureName, name) { 634 | if (!textureName || !name) { 635 | console.log(`Error: name "${name}" and/or textureName "${textureName}" are empty`) 636 | return { data: '', width: 0, height: 0 } 637 | } else { 638 | console.log(`Info: name "${name}" / textureName "${textureName}"`) 639 | } 640 | const framesById = getFramesById(textureName) 641 | const frame = Object.values(framesById) 642 | .flat() 643 | .find((frame) => frame.filename === name || frame.id === name) 644 | 645 | if (frame) { 646 | // cut borders of arcanas 647 | if (textureName === 'randomazzo') { 648 | frame.x = frame.x + 1 649 | frame.y = frame.y + 11 650 | frame.w = 54 651 | frame.h = 58 652 | } 653 | // cut borders of stages 654 | if (textureName === 'UI') { 655 | frame.x = frame.x + 5 656 | frame.y = frame.y + 32 657 | frame.w = 146 658 | frame.h = 59 659 | } 660 | const image = '' + execSync(`magick.exe ${src}/${textureName}.png -crop ${frame.w}x${frame.h}+${frame.x}+${frame.y} inline:-`) // ${dst}/character_${character.id}.png 661 | return { data: image, width: frame.w, height: frame.h } 662 | } else { 663 | console.log(`Can't find a frame of ${name} in ${textureName}`) 664 | } 665 | } 666 | 667 | function getCSS() { 668 | let content = `.icon { width: 1em; height: 1em; background-size: contain; background-position: center; background-repeat: no-repeat; }\n` 669 | for (const imageId in imagesById) { 670 | const image = imagesById[imageId] 671 | if (!image) { 672 | console.log(`Unknown image ${imageId}`) 673 | continue 674 | } 675 | const character = characters.find((c) => c.id === imageId) 676 | const backgroundData = `background-image: url("${image.data}");` 677 | const backgroundStyle = character ? `background-size: ${image.width / 10}em ${image.height / 10}em; background-position: left bottom;` : '' 678 | content += `.icon-${imageId} { ${backgroundData} ${backgroundStyle} }\n` 679 | } 680 | return content + '\n' 681 | } 682 | 683 | function getFramesById(sprite) { 684 | const data = JSON.parse('' + readFileSync(`${src}/${sprite}.json`)) 685 | const texture = data.textures[0] 686 | const framesById = {} 687 | for (const frame of texture.frames) { 688 | // ignore death animations of enemies 689 | const [filename, id, index] = frame.filename.match(/(\w+)_i(\d+)\.png/) || frame.filename.match(/(\w+)_(\d\d)\.png/) || frame.filename.match(/([a-z]+\d?)(\d)\.png/i) || frame.filename.match(/(.+)\.png/) || [] 690 | if (id) { 691 | const { w, h, x, y } = frame.frame 692 | framesById[id] = framesById[id] || [] 693 | framesById[id].push({ id, index, filename, w, h, x, y }) 694 | framesById[id] = framesById[id].sort((a, b) => (a.filename < b.filename ? -1 : 1)) 695 | } 696 | } 697 | Object.assign(allFramesById, framesById) 698 | 699 | return framesById 700 | } 701 | 702 | function pickFields(object = {}, params = []) { 703 | // console.log('extract', params, 'from', object) 704 | if (typeof object !== 'object') { 705 | return object 706 | } 707 | return params.reduce((paramsById, param) => { 708 | if (typeof object[param] !== 'undefined') { 709 | paramsById[param] = object[param] 710 | } 711 | return paramsById 712 | }, {}) 713 | } 714 | -------------------------------------------------------------------------------- /index2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |
624 |
625 |
626 |
627 |
628 |
1666 |
1667 |
1668 |
1680 |
1683 |
1684 |
1685 |
1686 |
--------------------------------------------------------------------------------