mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Files meant to be served have been moved into `play.pokemonshowdown.com/` and `pokemonshowdown.com/`. We now have three directories for the three subdomains handled by this repo: - `pokemonshowdown.com/` - `play.pokemonshowdown.com/` - `replay.pokemonshowdown.com/` Naming them after the subdomains will make it much easier to tell where the files for each go. The diff is probably useless; it'll be easier if you just look at the new tree: https://github.com/smogon/pokemon-showdown-client/tree/reorganize
790 lines
22 KiB
HTML
790 lines
22 KiB
HTML
<!doctype html>
|
|
<html lang=en>
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Sprites</title>
|
|
<link rel="stylesheet" href="../style/battle.css" />
|
|
<style>
|
|
body {
|
|
font-family: "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
|
max-width: 1365px;
|
|
}
|
|
|
|
.picon {
|
|
width: 40px;
|
|
height: 30px;
|
|
display: inline-block;
|
|
}
|
|
|
|
fieldset {
|
|
float: left;
|
|
height: 100px;
|
|
}
|
|
|
|
.dex {
|
|
height: 85px;
|
|
width: 115px;
|
|
border: 1px solid black;
|
|
}
|
|
|
|
fieldset legend {
|
|
font-weight: bold;
|
|
}
|
|
|
|
textarea {
|
|
display: block;
|
|
margin: 0px auto;
|
|
width: 432px;
|
|
height: 50px;
|
|
}
|
|
|
|
.sprites {
|
|
background-color: rgb(218, 229, 240);
|
|
border-left: 2px groove threedface;
|
|
height: 107px;
|
|
width: 180px;
|
|
float: right;
|
|
margin: -14px -12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-evenly;
|
|
}
|
|
|
|
.pixelated {
|
|
image-rendering: pixelated;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script>
|
|
exports = window;
|
|
</script>
|
|
<script src="../data/pokedex-mini.js"></script>
|
|
<script src="../data/pokedex-mini-bw.js"></script>
|
|
<script src="../data/teambuilder-tables.js"></script>
|
|
<script src="../data/abilities.js"></script>
|
|
<script src="../data/aliases.js"></script>
|
|
<script src="../data/items.js"></script>
|
|
<script src="../data/moves.js"></script>
|
|
<script src="../data/pokedex.js"></script>
|
|
<script src="../data/typechart.js"></script>
|
|
<script src="../js/battle-dex-data.js"></script>
|
|
<script src="../js/battle-dex.js"></script>
|
|
<script src="../js/battle-scene-stub.js"></script>
|
|
<script src="../data/text.js"></script>
|
|
<script src="../js/battle-text-parser.js"></script>
|
|
<script src="../js/battle.js"></script>
|
|
<script>
|
|
//#region VARIABLES
|
|
|
|
const GRAPHIC_NAMES = {
|
|
'Default': '',
|
|
'Green': 'gen1rg',
|
|
'Red/Blue': 'gen1rb',
|
|
'Yellow': 'gen1',
|
|
'Gold': 'gen2g',
|
|
'Silver': 'gen2s',
|
|
'Crystal': 'gen2',
|
|
'Ruby/Sapphire': 'gen3rs',
|
|
'FireRed/LeafGreen': 'gen3frlg',
|
|
'Emerald': 'gen3',
|
|
'Diamond/Pearl': 'gen4dp',
|
|
'Platinum': 'gen4',
|
|
'Black/White': 'gen5',
|
|
'Black/White (Animated)': 'gen5ani',
|
|
'Modern (Animated)': 'ani',
|
|
};
|
|
|
|
const GRAPHICS = {
|
|
// 'gen1rg': 1,
|
|
// 'gen1rb': 1,
|
|
'gen1': 1,
|
|
// 'gen2g': 2,
|
|
// 'gen2s': 2,
|
|
'gen2': 2,
|
|
// 'gen3rs': 3,
|
|
'gen3': 3,
|
|
// 'gen3frlg': 3,
|
|
// 'gen4dp': 4,
|
|
'gen4': 4,
|
|
'gen5': 5,
|
|
'gen5ani': 5,
|
|
'ani': 6,
|
|
};
|
|
|
|
var SETTINGS, SIDES, SCENE, STATE, BACKDROP;
|
|
const ROOT = document.getElementById('root');
|
|
|
|
const SPECIES = [];
|
|
for (let baseid in BattlePokedex) {
|
|
const species = Dex.species.get(baseid);
|
|
for (let formid of [''].concat(species.cosmeticFormes || [])) {
|
|
let spriteid = species.spriteid;
|
|
if (formid) spriteid += '-' + formid.slice(species.id.length);
|
|
const id = toID(spriteid);
|
|
SPECIES.push([formeName(species, id), species]);
|
|
}
|
|
}
|
|
|
|
SPECIES.sort(([, a], [, b]) => {
|
|
if (a.gen - b.gen) return a.gen - b.gen;
|
|
return Math.abs(a.num) - Math.abs(b.num);
|
|
});
|
|
|
|
//#endregion
|
|
//#region FUNCTIONS
|
|
|
|
function e(type, options) {
|
|
const tag = document.createElement(type);
|
|
if (!options) return tag;
|
|
|
|
if (typeof options === 'string' || typeof options === 'number') {
|
|
tag.textContent = options;
|
|
return tag;
|
|
} else if (options instanceof Node) {
|
|
tag.appendChild(options);
|
|
return tag;
|
|
}
|
|
if (options.content) {
|
|
if (typeof options.content === 'string' || typeof options.content === 'number') {
|
|
tag.textContent = options.content;
|
|
} else if (tag instanceof Node) {
|
|
tag.appendChild(options.content);
|
|
}
|
|
}
|
|
if (options.id) tag.setAttribute('id', options.id);
|
|
if (options.class) tag.classList.add(options.class);
|
|
return tag;
|
|
}
|
|
|
|
function formeName(species, spriteid) {
|
|
if (species.name === 'Toxtricity-Low-Key-Gmax') return species.name;
|
|
let forme = spriteid.slice(species.id.length);
|
|
if (!forme) return species.name;
|
|
forme = forme.charAt(0).toUpperCase() + forme.substr(1);
|
|
return `${species.name}-${forme}`;
|
|
}
|
|
|
|
function selector(label, selected, generator) {
|
|
label = e('label', label);
|
|
const select = e('select');
|
|
for (const [name, value] of generator()) {
|
|
const option = e('option', name);
|
|
option.value = value;
|
|
select.appendChild(option);
|
|
}
|
|
select.value = selected;
|
|
select.addEventListener('change', onChange);
|
|
label.appendChild(select);
|
|
return { div: e('div', label), select };
|
|
}
|
|
|
|
function genSelector(selected) {
|
|
return selector('Generation: ', selected, function* () {
|
|
for (let gen = 1; gen <= 8; gen++) {
|
|
yield [`${gen}`, gen];
|
|
}
|
|
});
|
|
}
|
|
|
|
function graphicsSelector(selected) {
|
|
return selector('Graphics: ', selected, function* () {
|
|
for (let name in GRAPHIC_NAMES) {
|
|
const val = GRAPHIC_NAMES[name];
|
|
if (!GRAPHICS[val]) continue;
|
|
yield [name, val];
|
|
}
|
|
});
|
|
}
|
|
|
|
function pokemonSelector(selected) {
|
|
return selector('Pokemon: ', selected, function* () {
|
|
for (const [name,] of SPECIES) {
|
|
yield [name, name];
|
|
}
|
|
});
|
|
}
|
|
|
|
function checkbox(label, name, checked) {
|
|
label = e('label', label);
|
|
const input = e('input');
|
|
input.type = 'checkbox';
|
|
input.name = name;
|
|
if (checked) input.checked = 'checked';
|
|
input.addEventListener('change', onChange);
|
|
label.appendChild(input);
|
|
return { div: e('div', label), input };
|
|
}
|
|
|
|
function activeRadios() {
|
|
const inputs = [];
|
|
const div = e('div');
|
|
div.textContent = 'Active: ';
|
|
for (let active = 1; active <= 3; active++) {
|
|
const input = e('input', {id: `radio${active}`});
|
|
input.type = 'radio';
|
|
input.name = 'active';
|
|
input.value = active;
|
|
input.addEventListener('change', onChange);
|
|
if (active === 1) input.checked = true;
|
|
inputs.push(input);
|
|
const label = e('label', active);
|
|
label.for =`radio${active}`;
|
|
div.appendChild(input);
|
|
div.appendChild(label);
|
|
}
|
|
return {inputs, div};
|
|
}
|
|
|
|
function createSettingsControls(selected) {
|
|
const fieldset = e('fieldset', e('legend', 'Settings'));
|
|
|
|
const fields = {
|
|
gen: genSelector(selected.gen),
|
|
graphics: graphicsSelector(selected.graphics),
|
|
scale: checkbox('Scale: ', 'scale', selected.scale),
|
|
oversize: checkbox('Oversize BW: ', 'oversize', selected.oversize),
|
|
active: activeRadios(),
|
|
};
|
|
|
|
const checkboxes = e('div');
|
|
checkboxes.style.height = '21px';
|
|
fields.scale.div.style.display = 'inline-block';
|
|
fields.oversize.div.style.display = 'inline-block';
|
|
fields.oversize.div.style.paddingLeft = '20px';
|
|
checkboxes.appendChild(fields.scale.div);
|
|
checkboxes.appendChild(fields.oversize.div);
|
|
fields.active.div.style.height = '21px';
|
|
|
|
fieldset.appendChild(fields.gen.div);
|
|
fieldset.appendChild(fields.graphics.div);
|
|
fieldset.appendChild(checkboxes);
|
|
fieldset.appendChild(fields.active.div);
|
|
|
|
fieldset.style.width = '240px';
|
|
|
|
return { fieldset, ...fields };
|
|
}
|
|
|
|
function createSideControls(legend, selected) {
|
|
const fieldset = e('fieldset', e('legend', legend));
|
|
|
|
const fields = {
|
|
pokemon: pokemonSelector(selected.pokemon),
|
|
female: checkbox('Female', 'female', selected.shiny),
|
|
shiny: checkbox('Shiny', 'shiny', selected.shiny),
|
|
dynamax: checkbox('Dynamax', 'dynamax', selected.dynamax),
|
|
picon: e('div', { class: 'picon' }),
|
|
dex: e('div', { class: 'dex' }),
|
|
};
|
|
|
|
const controls = e('div');
|
|
controls.style = 'float: left;';
|
|
controls.appendChild(fields.pokemon.div);
|
|
controls.appendChild(fields.female.div);
|
|
controls.appendChild(fields.shiny.div);
|
|
controls.appendChild(fields.dynamax.div);
|
|
|
|
const sprites = e('div', { class: 'sprites' });
|
|
sprites.appendChild(fields.picon);
|
|
sprites.appendChild(fields.dex);
|
|
|
|
fieldset.appendChild(controls);
|
|
fieldset.appendChild(sprites);
|
|
fieldset.style.width = '440px';
|
|
|
|
return { fieldset, ...fields };
|
|
}
|
|
|
|
function toState() {
|
|
const state = {
|
|
gen: SETTINGS.gen.select.value,
|
|
graphics: SETTINGS.graphics.select.value,
|
|
scale: SETTINGS.scale.input.checked,
|
|
oversize: SETTINGS.oversize.input.checked,
|
|
active: SETTINGS.active.inputs.findIndex(input => input.checked) + 1,
|
|
};
|
|
for (const s in SIDES) {
|
|
state[s] = {};
|
|
state[s].pokemon = SIDES[s].pokemon.select.value;
|
|
state[s].female = SIDES[s].female.input.checked;
|
|
state[s].shiny = SIDES[s].shiny.input.checked;
|
|
state[s].dynamax = SIDES[s].dynamax.input.checked;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
function fromState(state) {
|
|
if ('gen' in state) SETTINGS.gen.select.value = state.gen;
|
|
if ('graphics' in state) SETTINGS.graphics.select.value = state.graphics;
|
|
if ('scale' in state) SETTINGS.scale.input.checked = state.scale;
|
|
if ('oversize' in state) SETTINGS.oversize.input.checked = state.oversize;
|
|
if ('active' in state) null; // TODO
|
|
for (const s in SIDES) {
|
|
if (!state[s]) continue;
|
|
if ('pokemon' in state[s]) SIDES[s].pokemon.select.value = state[s].pokemon
|
|
if ('female' in state[s]) SIDES[s].female.input.checked = state[s].female;
|
|
if ('shiny' in state[s]) SIDES[s].shiny.input.checked = state[s].shiny;
|
|
if ('dynamax' in state[s]) SIDES[s].dynamax.input.checked = state[s].dynamax;
|
|
}
|
|
onChange();
|
|
}
|
|
|
|
function onChange() {
|
|
const gen = Number(SETTINGS.gen.select.value);
|
|
|
|
SETTINGS.oversize.input.disabled = !SETTINGS.graphics.select.value.startsWith('gen5');
|
|
SETTINGS.active.inputs[1].disabled = gen < 3;
|
|
SETTINGS.active.inputs[2].disabled = gen > 6 || gen < 5;
|
|
for (const side in SIDES) {
|
|
const species = Dex.species.get(SIDES[side].pokemon.select.value);
|
|
if (gen === 1) {
|
|
SIDES[side].female.input.disabled = true;
|
|
} else if (species.gender) {
|
|
SIDES[side].female.input.checked = SIDES[side].female.input.checked = species.gender === 'F';
|
|
SIDES[side].female.input.disabled = true;
|
|
} else {
|
|
SIDES[side].female.input.disabled = false;
|
|
}
|
|
SIDES[side].shiny.input.disabled = gen === 1;
|
|
SIDES[side].dynamax.input.disabled = gen !== 8;
|
|
}
|
|
|
|
render();
|
|
}
|
|
|
|
function changeBackdrop(backdrop, gen) {
|
|
if (BACKDROP && BACKDROP.gen === gen) {
|
|
backdrop.style = BACKDROP.style;
|
|
return;
|
|
}
|
|
BACKDROP = gen;
|
|
|
|
const BattleBackdropsThree = [
|
|
'bg-gen3.png',
|
|
'bg-gen3-cave.png',
|
|
'bg-gen3-ocean.png',
|
|
'bg-gen3-sand.png',
|
|
'bg-gen3-forest.png',
|
|
'bg-gen3-arena.png',
|
|
];
|
|
const BattleBackdropsFour = [
|
|
'bg-gen4.png',
|
|
'bg-gen4-cave.png',
|
|
'bg-gen4-snow.png',
|
|
'bg-gen4-indoors.png',
|
|
'bg-gen4-water.png',
|
|
];
|
|
const BattleBackdropsFive = [
|
|
'bg-beach.png',
|
|
'bg-beachshore.png',
|
|
'bg-desert.png',
|
|
'bg-meadow.png',
|
|
'bg-thunderplains.png',
|
|
'bg-city.png',
|
|
'bg-earthycave.png',
|
|
'bg-mountain.png',
|
|
'bg-volcanocave.png',
|
|
'bg-dampcave.png',
|
|
'bg-forest.png',
|
|
'bg-river.png',
|
|
'bg-deepsea.png',
|
|
'bg-icecave.png',
|
|
'bg-route.png',
|
|
];
|
|
const BattleBackdrops = [
|
|
'bg-aquacordetown.jpg',
|
|
'bg-beach.jpg',
|
|
'bg-city.jpg',
|
|
'bg-dampcave.jpg',
|
|
'bg-darkbeach.jpg',
|
|
'bg-darkcity.jpg',
|
|
'bg-darkmeadow.jpg',
|
|
'bg-deepsea.jpg',
|
|
'bg-desert.jpg',
|
|
'bg-earthycave.jpg',
|
|
'bg-elite4drake.jpg',
|
|
'bg-forest.jpg',
|
|
'bg-icecave.jpg',
|
|
'bg-leaderwallace.jpg',
|
|
'bg-library.jpg',
|
|
'bg-meadow.jpg',
|
|
'bg-orasdesert.jpg',
|
|
'bg-orassea.jpg',
|
|
'bg-skypillar.jpg',
|
|
];
|
|
|
|
let bg;
|
|
if (gen <= 1) bg = 'fx/bg-gen1.png';
|
|
else if (gen <= 2) bg = 'fx/bg-gen2.png';
|
|
else if (gen <= 3) bg = 'fx/' + sample(BattleBackdropsThree);
|
|
else if (gen <= 4) bg = 'fx/' + sample(BattleBackdropsFour);
|
|
else if (gen <= 5) bg = 'fx/' + sample(BattleBackdropsFive);
|
|
else bg = 'sprites/gen6bgs/' + sample(BattleBackdrops);
|
|
|
|
const style = `background-image:url(${Dex.resourcePrefix}${bg});display:block;opacity:0.8`;
|
|
backdrop.style = style;
|
|
BACKDROP = {gen, style};
|
|
}
|
|
|
|
function locToPos(loc, obj) {
|
|
loc = {
|
|
x: 0,
|
|
y: 0,
|
|
z: 0,
|
|
scale: 1,
|
|
opacity: 1,
|
|
...loc,
|
|
};
|
|
if (!loc.xscale && loc.xscale !== 0) loc.xscale = loc.scale;
|
|
if (!loc.yscale && loc.yscale !== 0) loc.yscale = loc.scale;
|
|
|
|
let left = 210;
|
|
let top = 245;
|
|
let scale = (obj.gen === 5
|
|
? 2.0 - ((loc.z) / 200)
|
|
: 1.5 - 0.5 * ((loc.z) / 200));
|
|
if (scale < .1) scale = .1;
|
|
|
|
left += (410 - 190) * ((loc.z) / 200);
|
|
top += (135 - 245) * ((loc.z) / 200);
|
|
left += Math.floor(loc.x * scale);
|
|
top -= Math.floor(loc.y * scale /* - loc.x * scale / 4 */);
|
|
let width = Math.floor(obj.w * scale * loc.xscale);
|
|
let height = Math.floor(obj.h * scale * loc.yscale);
|
|
let hoffset = Math.floor((obj.h - (obj.y || 0) * 2) * scale * loc.yscale);
|
|
left -= Math.floor(width / 2);
|
|
top -= Math.floor(hoffset / 2);
|
|
|
|
let pos = {
|
|
left,
|
|
top,
|
|
width,
|
|
height,
|
|
opacity: loc.opacity,
|
|
};
|
|
if (loc.display) pos.display = loc.display;
|
|
return pos;
|
|
}
|
|
|
|
function calculatePos(slot, z, gen, activeCount, pixelated, isBackSprite) {
|
|
const moreActive = activeCount - 1;
|
|
let statbarOffset = 0;
|
|
let x = 0, y = 0;
|
|
if (gen <= 4 && moreActive) {
|
|
x = (slot - 0.52) * (isBackSprite ? -1 : 1) * -55;
|
|
y = (isBackSprite ? -1 : 1) + 1;
|
|
if (!isBackSprite) statbarOffset = 30 * slot;
|
|
if (isBackSprite) statbarOffset = -28 * slot;
|
|
} else {
|
|
switch (moreActive) {
|
|
case 0:
|
|
x = 0;
|
|
break;
|
|
case 1:
|
|
if (pixelated) {
|
|
x = (slot * -100 + 18) * (isBackSprite ? -1 : 1);
|
|
} else {
|
|
x = (slot * -75 + 18) * (isBackSprite ? -1 : 1);
|
|
}
|
|
break;
|
|
case 2:
|
|
x = (slot * -70 + 20) * (isBackSprite ? -1 : 1);
|
|
break;
|
|
}
|
|
y = (slot * 10) * (isBackSprite ? -1 : 1);
|
|
if (!isBackSprite) statbarOffset = 17 * slot;
|
|
if (!isBackSprite && !moreActive && pixelated) statbarOffset = 15;
|
|
if (isBackSprite) statbarOffset = -7 * slot;
|
|
if (!isBackSprite && moreActive === 2) statbarOffset = 14 * slot - 10;
|
|
}
|
|
if (gen <= 2) {
|
|
statbarOffset += isBackSprite ? 1 : 20;
|
|
} else if (gen <= 3) {
|
|
statbarOffset += isBackSprite ? 5 : 30;
|
|
} else if (gen !== 5) {
|
|
statbarOffset += isBackSprite ? 20 : 30;
|
|
}
|
|
|
|
let pos = locToPos({x, y , z}, {w: 0, h: 96});
|
|
pos.top += 40;
|
|
|
|
const statbarLeft = pos.left - 80;
|
|
const statbarTop = pos.top - 73 - statbarOffset;
|
|
|
|
return {x, y, z, left: statbarLeft, top: statbarTop};
|
|
}
|
|
|
|
function leftof(side, offset) {
|
|
return (side === 'p1' ? -1 : 1) * offset;
|
|
}
|
|
|
|
function getHPColor(pokemon) {
|
|
let ratio = pokemon.hp / pokemon.maxhp;
|
|
if (ratio > 0.5) return 'g';
|
|
if (ratio > 0.2) return 'y';
|
|
return 'r';
|
|
}
|
|
|
|
function hpWidth(hp, maxhp, maxwidth) {
|
|
// special case for low health...
|
|
if (this.hp === 1 && this.maxhp > 45) return 1;
|
|
let percentage = Math.ceil(100 * hp / maxhp);
|
|
if ((percentage === 100) && (hp < maxhp)) {
|
|
percentage = 99;
|
|
}
|
|
return percentage * maxwidth / 100;
|
|
}
|
|
|
|
function displayHP(div, hp, maxhp, c) {
|
|
let hpcolor = getHPColor({hp, maxhp});
|
|
let w = hpWidth(hp, maxhp, 150);
|
|
div.style = `width:${w}px;border-right-width:${w ? 1 : 0}px;`;
|
|
if (hpcolor === 'y') div.classList.add(`${c}-yellow`);
|
|
else if (hpcolor === 'r') div.classList.add(`${c}-yellow`, `${c}-red`);
|
|
}
|
|
|
|
function renderStatbar(side, pokemon, gender) {
|
|
const statbar = e('div');
|
|
statbar.classList.add('statbar', (side === 'p2' ? 'lstatbar' : 'rstatbar'));
|
|
statbar.style = 'display: block';
|
|
|
|
const strong = e('strong', pokemon);
|
|
if (gender === 'M' || gender === 'F') {
|
|
const img = e('img', {class: 'pixelated'});
|
|
img.width = 7;
|
|
img.height = 10;
|
|
img.src = `${Dex.fxPrefix}gender-${gender.toLowerCase()}.png`;
|
|
strong.textContent += ' ';
|
|
strong.appendChild(img);
|
|
}
|
|
|
|
let symbol = '';
|
|
if (pokemon.indexOf('-Mega') >= 0) symbol = 'mega';
|
|
else if (pokemon === 'Kyogre-Primal') symbol = 'alpha';
|
|
else if (pokemon === 'Groudon-Primal') symbol = 'omega';
|
|
if (symbol) {
|
|
const img = e('img', {class: 'pixelated'});
|
|
img.src = `${Dex.resourcePrefix}sprites/misc/${symbol}.png`;
|
|
img.style = 'vertical-align:text-bottom;';
|
|
strong.textContent += ' ';
|
|
strong.appendChild(img);
|
|
}
|
|
|
|
const maxhp = 100;
|
|
const hp = Math.floor(Math.random() * (maxhp - 1)) + 0;
|
|
const prevhp = Math.floor(Math.random() * (maxhp - hp + 1)) + hp;
|
|
|
|
const hpbar = e('div', {class: 'hpbar'});
|
|
const hptext = e('div', {class: 'hptext', content: `${hpWidth(hp, maxhp, 100)}%`});
|
|
hpbar.appendChild(hptext);
|
|
hpbar.appendChild(e('div', {class: 'hptextborder'}));
|
|
const prevhpDiv = e('div', {class: 'prevhp'});
|
|
const hpDiv = e('div', {class: 'hp'});
|
|
|
|
displayHP(hpDiv, hp, maxhp, 'hp');
|
|
displayHP(prevhpDiv, prevhp, maxhp, 'prevhp');
|
|
|
|
prevhpDiv.appendChild(hpDiv);
|
|
hpbar.appendChild(prevhpDiv);
|
|
hpbar.appendChild(e('div', {class: 'status'}));
|
|
|
|
statbar.appendChild(strong);
|
|
statbar.appendChild(hpbar);
|
|
return statbar;
|
|
}
|
|
|
|
function getGender(gen, pokemon, female) {
|
|
if (gen === 1) return undefined;
|
|
const species = Dex.species.get(pokemon);
|
|
if (species.gender) return species.gender;
|
|
return female ? 'F' : 'M';
|
|
}
|
|
|
|
function render() {
|
|
const gen = Number(SETTINGS.gen.select.value);
|
|
const graphicsGen = GRAPHICS[SETTINGS.graphics.select.value];
|
|
|
|
if (SCENE) ROOT.removeChild(SCENE);
|
|
if (STATE) ROOT.removeChild(STATE);
|
|
|
|
SCENE = e('div', {class: 'battle'});
|
|
const innerbattle = e('div', {class: 'innerbattle'});
|
|
const backdrop = e('div', {class: 'backdrop'});
|
|
const main = e('div');
|
|
changeBackdrop(backdrop, gen);
|
|
|
|
innerbattle.appendChild(backdrop);
|
|
innerbattle.appendChild(e('div', {class: 'leftbar'}));
|
|
innerbattle.appendChild(e('div', {class: 'rightbar'}));
|
|
innerbattle.appendChild(main);
|
|
|
|
const statbarsDiv = e('div');
|
|
const spritesDiv = e('div');
|
|
for (const side of ['p2', 'p1']) {
|
|
const pokemon = SIDES[side].pokemon.select.value;
|
|
const gender = getGender(gen, pokemon, SIDES[side].female.input.checked);
|
|
const shiny = SIDES[side].shiny.input.checked;
|
|
|
|
SIDES[side].picon.style = Dex.getPokemonIcon(
|
|
{id: pokemon, gender},
|
|
side === 'p2' // facingLeft
|
|
);
|
|
SIDES[side].dex.style = getTeambuilderSprite({
|
|
species: pokemon,
|
|
shiny,
|
|
}, graphicsGen);
|
|
|
|
const {statbars, sprites} = renderSide(gen, side, pokemon, shiny, gender);
|
|
for (const statbar of statbars) statbarsDiv.appendChild(statbar);
|
|
for (const sprite of sprites) spritesDiv.appendChild(sprite);
|
|
}
|
|
main.appendChild(spritesDiv);
|
|
main.appendChild(statbarsDiv);
|
|
|
|
SCENE.style = 'position:relative;margin:50px auto;'
|
|
SCENE.appendChild(innerbattle);
|
|
|
|
STATE = e('textarea');
|
|
STATE.value = JSON.stringify(toState());
|
|
STATE.addEventListener('change', () => fromState(JSON.parse(STATE.value)));
|
|
|
|
ROOT.appendChild(SCENE);
|
|
ROOT.appendChild(STATE);
|
|
}
|
|
|
|
function renderSide(gen, side, pokemon, shiny, gender) {
|
|
const graphics = SETTINGS.graphics.select.value;
|
|
const graphicsGen = GRAPHICS[SETTINGS.graphics.select.value];
|
|
const active = SETTINGS.active.inputs.findIndex(input => input.checked) + 1;
|
|
const siden = +(side === 'p2');
|
|
const noScale = !SETTINGS.scale.input.checked;
|
|
const dynamax = SIDES[side].dynamax.input.checked;
|
|
const isBackSprite = side === 'p1';
|
|
|
|
const prefs = {
|
|
noanim: !graphics.endsWith('ani'),
|
|
nopastgens: graphicsGen > 5,
|
|
};
|
|
|
|
const statbars = [];
|
|
const sprites = [];
|
|
for (let slot = 0; slot < active; slot++) {
|
|
withPrefs(prefs, () => {
|
|
const data = Dex.getSpriteData(pokemon, siden, { gen, noScale, shiny, gender, dynamax });
|
|
const img = e('img');
|
|
img.src = data.url;
|
|
img.style.position = 'absolute';
|
|
if (data.pixelated) img.classList.add('pixelated');
|
|
|
|
const {x, y, z, left, top} =
|
|
calculatePos(slot, (siden ? 200 : 0), gen, active, data.pixelated, isBackSprite);
|
|
|
|
const css = locToPos({x, y, z, display: 'block'}, data);
|
|
for (let k in css) img.style[k] = k === 'opacity' ? css[k] : `${css[k]}px`;
|
|
|
|
const statbar = renderStatbar(side, pokemon, gender);
|
|
statbar.style.left = `${left}px`;
|
|
statbar.style.top = `${top}px`;
|
|
|
|
statbars.push(statbar);
|
|
sprites.push(img);
|
|
});
|
|
}
|
|
|
|
// God only knows why this particular z-ordering is desirable...
|
|
if (siden) sprites.reverse();
|
|
if (active === 2) {
|
|
statbars.reverse();
|
|
} else if (active === 3) {
|
|
if (!siden) {
|
|
statbars.reverse();
|
|
[sprites[1], sprites[2]] = [sprites[2], sprites[1]];
|
|
}
|
|
}
|
|
|
|
return {statbars, sprites};
|
|
}
|
|
|
|
function getTeambuilderSprite(pokemon, gen) {
|
|
if (!pokemon) return '';
|
|
const data = Dex.getTeambuilderSpriteData(pokemon, gen);
|
|
const shiny = (data.shiny ? '-shiny' : '');
|
|
return 'background-image:url(' + Dex.resourcePrefix + data.spriteDir + shiny + '/' + data.spriteid + '.png);background-position:' + (data.x) + 'px ' + (data.y - 15) + 'px;background-repeat:no-repeat';
|
|
}
|
|
|
|
function withPrefs(prefs, fn) {
|
|
const saved = Dex.prefs;
|
|
Dex.prefs = s => prefs[s];
|
|
fn();
|
|
Dex.prefs = saved;
|
|
}
|
|
|
|
function sample(arr) {
|
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
}
|
|
|
|
function randomSelections() {
|
|
const gen = sample([1, 2, 3, 4, 5, 6, 7, 8]);
|
|
const graphics = sample(Object.keys(GRAPHICS));
|
|
const oversize = graphics.startsWith('gen5') ? sample([true, false]) : false;
|
|
const scale = sample([true, false]);
|
|
const sides = { p1: {}, p2: {} };
|
|
for (side in sides) {
|
|
const [name, species] = sample(SPECIES);
|
|
sides[side].pokemon = name;
|
|
if (gen == 1) {
|
|
sides[side].female = null;
|
|
} else if (species.gender) {
|
|
sides[side].female = species.gender === 'F' ? true : false;
|
|
} else {
|
|
sides[side].female = sample([true, false]);
|
|
}
|
|
sides[side].shiny = gen > 1 ? sample([true, false]) : false;
|
|
sides[side].dynamax = gen === 8 ? sample([true, false]) : false;
|
|
}
|
|
return {
|
|
gen,
|
|
graphics,
|
|
scale,
|
|
oversize,
|
|
...sides
|
|
};
|
|
}
|
|
|
|
function renderRandom() {
|
|
if (ROOT.firstChild) ROOT.removeChild(ROOT.firstChild);
|
|
const div = e('div');
|
|
|
|
const selected = randomSelections();
|
|
SETTINGS = createSettingsControls(selected);
|
|
SIDES = {
|
|
p1: createSideControls('Player 1', selected.p1),
|
|
p2: createSideControls('Player 2', selected.p2),
|
|
};
|
|
|
|
div.appendChild(SETTINGS.fieldset);
|
|
div.appendChild(SIDES.p1.fieldset);
|
|
div.appendChild(SIDES.p2.fieldset);
|
|
|
|
const randomize = e('button', 'Randomize');
|
|
randomize.addEventListener('click', () => renderRandom());
|
|
randomize.style = 'padding: 10px; margin: 45px 25px';
|
|
div.appendChild(randomize);
|
|
|
|
root.appendChild(div);
|
|
onChange();
|
|
}
|
|
|
|
//#endregion
|
|
//#region MAIN
|
|
|
|
renderRandom();
|
|
|
|
//#endregion
|
|
</script>
|
|
</body>
|
|
</html> |