Closed CodeTappert closed 4 months ago
Ideas/a better implementation is welcome. Also i would like to hear from the dev team if something like this is even wanted
Also do not forget Stellar type!
Added stellar type (updates the code in the main post too)
Added stellar type (updates the code in the main post too)
But i see how the yellow part could make the text hard to read... Maybe we should add a border around the text
Again made a change to the size and now the text has a minimal black border for redability. The icons now are 64x28 and thus (as far as i can tell) double the size of the ones currently used but in the same aspect ratio so it should be pretty easy to swap them out
So i improved it again.
This code now runs locally and not in a browser and creates png files for every type:
const fs = require('fs');
const { createCanvas, registerFont} = require('canvas');
registerFont('./pokemon-emerald-pro.ttf', { family: 'Pokemon Emerald Pro' });
// Function to adjust the brightness of a hex color
function adjustBrightness(color, percent) {
let r = parseInt(color.slice(1, 3), 16);
let g = parseInt(color.slice(3, 5), 16);
let b = parseInt(color.slice(5, 7), 16);
r = Math.min(255, Math.max(0, r + Math.round(r * percent / 100)));
g = Math.min(255, Math.max(0, g + Math.round(g * percent / 100)));
b = Math.min(255, Math.max(0, b + Math.round(b * percent / 100)));
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, '0')}`;
}
// Function to create a basic image with rounded corners and text
function createImage(width, height, backgroundColor, text, originalType,language = 'en', font = '20px Verdana') {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
setupRoundedCorners(ctx, width, height);
drawBackground(ctx, width, height, backgroundColor);
drawText(ctx, width, height, text, language);
saveImage(canvas, language,originalType);
}
// Function to create a rainbow image with specified text
function createRainbowImage(width, height, text, originalType, language = 'en', font = '20px Verdana') {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
setupRoundedCorners(ctx, width, height);
drawRainbowBackground(ctx, width, height);
drawText(ctx, width, height, text, language);
saveImage(canvas, language,originalType);
}
// Set up rounded corners
function setupRoundedCorners(ctx, width, height) {
const radius = 4;
ctx.beginPath();
ctx.moveTo(radius, 0);
ctx.lineTo(width - radius, 0);
ctx.quadraticCurveTo(width, 0, width, radius);
ctx.lineTo(width, height - radius);
ctx.quadraticCurveTo(width, height, width - radius, height);
ctx.lineTo(radius, height);
ctx.quadraticCurveTo(0, height, 0, height - radius);
ctx.lineTo(0, radius);
ctx.quadraticCurveTo(0, 0, radius, 0);
ctx.closePath();
ctx.clip();
}
// Draw background with a lighter top and black bottom row
function drawBackground(ctx, width, height, backgroundColor) {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = adjustBrightness(backgroundColor, 20);
ctx.fillRect(0, 0, width, 1);
ctx.fillStyle = 'black';
ctx.fillRect(0, height - 1, width, 1);
}
// Draw rainbow gradient background
function drawRainbowBackground(ctx, width, height) {
const gradient = ctx.createLinearGradient(0, 0, width, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.15, 'orange');
gradient.addColorStop(0.3, 'yellow');
gradient.addColorStop(0.45, 'green');
gradient.addColorStop(0.6, 'blue');
gradient.addColorStop(0.75, 'indigo');
gradient.addColorStop(1, 'violet');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
ctx.fillRect(0, 0, width, 1);
ctx.fillStyle = 'black';
ctx.fillRect(0, height - 1, width, 1);
}
// Draw text centered with border
function drawText(ctx, width, height, text,language) {
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
let fontSize = 45;
let metrics;
const padding = 2;
do {
if (language === 'zhCN') {
ctx.font = `${fontSize}px Microsoft YaHei:`;
} else {
ctx.font = `${fontSize}px Pokemon Emerald Pro`;
}
metrics = ctx.measureText(text);
fontSize -= 1;
} while (metrics.width > width - 2 * padding || fontSize > height - 2 * padding);
ctx.strokeText(text, width / 2, height / 2);
ctx.fillText(text, width / 2, height / 2);
}
// Save image to a file
function saveImage(canvas, language,typename) {
const buffer = canvas.toBuffer('image/png');
if (!fs.existsSync('types')) {
fs.mkdirSync('types');
}
if (!fs.existsSync(`types/${language}`)) {
fs.mkdirSync(`types/${language}`);
}
fs.writeFileSync(`types/${language}/${typename}.png`, buffer);
}
// Generate example images
// Normal type
createImage(128,56, '#A8A878', 'Normal','normal','en');
createImage(128,56, '#A8A878', 'Normal','normal','de');
createImage(128,56, '#A8A878', 'Normal','normal','fr');
createImage(128,56, '#A8A878', 'Normal','normal','es');
createImage(128,56, '#A8A878', 'Normale','normal','it');
createImage(128,56, '#A8A878', '一般','normal','zhCN');
// Fire type
createImage(128,56, '#F08030', 'Fire','fire','en');
createImage(128,56, '#F08030', 'Feuer','fire','de');
createImage(128,56, '#F08030', 'Feu','fire','fr');
createImage(128,56, '#F08030', 'Fuego','fire','es');
createImage(128,56, '#F08030', 'Fuoco','fire','it');
createImage(128,56, '#F08030', '火','fire','zhCN');
// Water type
createImage(128,56, '#6890F0', 'Water','water','en');
createImage(128,56, '#6890F0', 'Wasser','water','de');
createImage(128,56, '#6890F0', 'Eau','water','fr');
createImage(128,56, '#6890F0', 'Agua','water','es');
createImage(128,56, '#6890F0', 'Acqua','water','it');
createImage(128,56, '#6890F0', '水','water','zhCN');
// Electric type
createImage(128,56, '#F8D030', 'Electric','electric','en');
createImage(128,56, '#F8D030', 'Elektro','electric','de');
createImage(128,56, '#F8D030', 'Électrik','electric','fr');
createImage(128,56, '#F8D030', 'Eléctrico','electric','es');
createImage(128,56, '#F8D030', 'Elettro','electric','it');
createImage(128,56, '#F8D030', '电','electric','zhCN');
// Grass type
createImage(128,56, '#78C850', 'Grass','grass','en');
createImage(128,56, '#78C850', 'Pflanze','grass','de');
createImage(128,56, '#78C850', 'Plante','grass','fr');
createImage(128,56, '#78C850', 'Planta','grass','es');
createImage(128,56, '#78C850', 'Erba','grass','it');
createImage(128,56, '#78C850', '草','grass','zhCN');
// Ice type
createImage(128,56, '#98D8D8', 'Ice','ice','en');
createImage(128,56, '#98D8D8', 'Eis','ice','de');
createImage(128,56, '#98D8D8', 'Glace','ice','fr');
createImage(128,56, '#98D8D8', 'Hielo','ice','es');
createImage(128,56, '#98D8D8', 'Ghiaccio','ice','it');
createImage(128,56, '#98D8D8', '冰','ice','zhCN');
// Fighting type
createImage(128,56, '#C03028', 'Fighting','fighting','en');
createImage(128,56, '#C03028', 'Kampf','fighting','de');
createImage(128,56, '#C03028', 'Combat','fighting','fr');
createImage(128,56, '#C03028', 'Lucha','fighting','es');
createImage(128,56, '#C03028', 'Lotta','fighting','it');
createImage(128,56, '#C03028', '斗','fighting','zhCN');
// Poison type
createImage(128,56, '#A040A0', 'Poison','poison','en');
createImage(128,56, '#A040A0', 'Gift','poison','de');
createImage(128,56, '#A040A0', 'Poison','poison','fr');
createImage(128,56, '#A040A0', 'Veneno','poison','es');
createImage(128,56, '#A040A0', 'Veleno','poison','it');
createImage(128,56, '#A040A0', '毒','poison','zhCN');
// Ground type
createImage(128,56, '#E0C068', 'Ground','ground','en');
createImage(128,56, '#E0C068', 'Boden','ground','de');
createImage(128,56, '#E0C068', 'Sol','ground','fr');
createImage(128,56, '#E0C068', 'Tierra','ground','es');
createImage(128,56, '#E0C068', 'Terra','ground','it');
createImage(128,56, '#E0C068', '地面','ground','zhCN');
// Flying type
createImage(128,56, '#A890F0', 'Flying','flying','en');
createImage(128,56, '#A890F0', 'Flug','flying','de');
createImage(128,56, '#A890F0', 'Vol','flying','fr');
createImage(128,56, '#A890F0', 'Volador','flying','es');
createImage(128,56, '#A890F0', 'Volante','flying','it');
createImage(128,56, '#A890F0', '飞行','flying','zhCN');
// Psychic type
createImage(128,56, '#F85888', 'Psychic','psychic','en');
createImage(128,56, '#F85888', 'Psycho','psychic','de');
createImage(128,56, '#F85888', 'Psy','psychic','fr');
createImage(128,56, '#F85888', 'Psíquico','psychic','es');
createImage(128,56, '#F85888', 'Psico','psychic','it');
createImage(128,56, '#F85888', '超能','psychic','zhCN');
// Bug type
createImage(128,56, '#A8B820', 'Bug','bug','en');
createImage(128,56, '#A8B820', 'Käfer','bug','de');
createImage(128,56, '#A8B820', 'Insecte','bug','fr');
createImage(128,56, '#A8B820', 'Bicho','bug','es');
createImage(128,56, '#A8B820', 'Coleottero','bug','it');
createImage(128,56, '#A8B820', '虫','bug','zhCN');
// Rock type
createImage(128,56, '#B8A038', 'Rock','rock','en');
createImage(128,56, '#B8A038', 'Gestein','rock','de');
createImage(128,56, '#B8A038', 'Roche','rock','fr');
createImage(128,56, '#B8A038', 'Roca','rock','es');
createImage(128,56, '#B8A038', 'Roccia','rock','it');
createImage(128,56, '#B8A038', '岩石','rock','zhCN');
// Ghost type
createImage(128,56, '#705898', 'Ghost','ghost','en');
createImage(128,56, '#705898', 'Geist','ghost','de');
createImage(128,56, '#705898', 'Spectre','ghost','fr');
createImage(128,56, '#705898', 'Fantasma','ghost','es');
createImage(128,56, '#705898', 'Fantasma','ghost','it');
createImage(128,56, '#705898', '幽灵','ghost','zhCN');
// Dragon type
createImage(128,56, '#7038F8', 'Dragon','dragon','en');
createImage(128,56, '#7038F8', 'Drache','dragon','de');
createImage(128,56, '#7038F8', 'Dragon','dragon','fr');
createImage(128,56, '#7038F8', 'Dragón','dragon','es');
createImage(128,56, '#7038F8', 'Drago','dragon','it');
createImage(128,56, '#7038F8', '龙','dragon','zhCN');
// Dark type
createImage(128,56, '#705848', 'Dark','dark','en');
createImage(128,56, '#705848', 'Unlicht','dark','de');
createImage(128,56, '#705848', 'Ténèbres','dark','fr');
createImage(128,56, '#705848', 'Siniestro','dark','es');
createImage(128,56, '#705848', 'Buio','dark','it');
createImage(128,56, '#705848', '暗','dark','zhCN');
// Steel type
createImage(128,56, '#B8B8D0', 'Steel','steel','en');
createImage(128,56, '#B8B8D0', 'Stahl','steel','de');
createImage(128,56, '#B8B8D0', 'Acier','steel','fr');
createImage(128,56, '#B8B8D0', 'Acero','steel','es');
createImage(128,56, '#B8B8D0', 'Acciaio','steel','it');
createImage(128,56, '#B8B8D0', '钢','steel','zhCN');
// Fairy type
createImage(128,56, '#EE99AC', 'Fairy','fairy','en');
createImage(128,56, '#EE99AC', 'Fee','fairy','de');
createImage(128,56, '#EE99AC', 'Fée','fairy','fr');
createImage(128,56, '#EE99AC', 'Hada','fairy','es');
createImage(128,56, '#EE99AC', 'Folletto','fairy','it');
createImage(128,56, '#EE99AC', '妖精','fairy','zhCN');
// Stellar type
createRainbowImage(128,56, 'Stellar','stellar','en');
createRainbowImage(128,56, 'Stellar','stellar','de');
createRainbowImage(128,56, 'Stellaire','stellar','fr');
createRainbowImage(128,56, 'Astral','stellar','es');
createRainbowImage(128,56, 'Astrale','stellar','it');
createRainbowImage(128,56, '星晶','stellar','zhCN');
I think it is still easier to use image files in the game like it is currently thus i created this creator. This can easily can be expanded for new languages
Currently this uses a default windows font for chinese. and the pokemon emerald font for everything else.
Example screenshot for the german files:
Here a zip of files it created. types.zip
There seems to be some problems with the chinese text... I will look for a solution
There seems to be some problems with the chinese text... I will look for a solution
Fixed and updated in the last code i posted here
Will be talked about (and maybe used) in #789 But this here will be closed
I noticed that the type symbols (with text) are all currently in english.
Of course creating them all by hand for every language would be a lot of work so i want to make a suggestion that we create them on the fly.
I created some code that does this (rudementary). Of course a real implementation needs to be a lot better. For example my text is still kind of blurry.
The following code is out of date! Please look at comment: https://github.com/pagefaultgames/pokerogue/issues/755#issuecomment-2106297059
Here is the code: (also it is no typescript just javascript)
Here is a image of what it created (100% scale):