CLIENT SIDE ICONS LETS FUCKING GOOOO
30
README.md
|
@ -1,3 +1,9 @@
|
||||||
|
hi, this is colon from the future.
|
||||||
|
|
||||||
|
what the FUCK was wrong with me back then???? seriously this is some of the worst code i've ever seen
|
||||||
|
|
||||||
|
welp, here's the readme. but you've been warned,,,
|
||||||
|
|
||||||
|
|
||||||
# GDBrowser
|
# GDBrowser
|
||||||
|
|
||||||
|
@ -84,9 +90,6 @@ This is where all the backend stuff happens! Yipee!
|
||||||
|
|
||||||
They're all fairly similar. Fetch something, parse the response, and serve it in a crisp and non-intimidating JSON. This is probably what you came for.
|
They're all fairly similar. Fetch something, parse the response, and serve it in a crisp and non-intimidating JSON. This is probably what you came for.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The odd one out is icon.js, which is for generating GD icons. The code here is horrendous, so apologies in advance. Improvements to it would be greatly appreciated! (and i will love you forever)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,20 +129,6 @@ XOR.js encrypts/decrypts stuff like GD passwords
|
||||||
|
|
||||||
The HTML files! Nothing too fancy, since it can all be seen directly from gdbrowser. Note that profile.html and level.html (and some parts of home.html) have [[VARIABLES]] (name, id, etc) replaced by the server when they're sent.
|
The HTML files! Nothing too fancy, since it can all be seen directly from gdbrowser. Note that profile.html and level.html (and some parts of home.html) have [[VARIABLES]] (name, id, etc) replaced by the server when they're sent.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
comingsoon.html was used while the site was still in development, I just left it in there as a nice little throwback
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Icons
|
|
||||||
It's GJ_Gamesheet02 but split into a much more intimidating cluster of a million files. These icons are put together and colored in the monstrosity that is icon.js
|
|
||||||
| name | description |
|
|
||||||
|:----:|:-----------:|
|
|
||||||
|`parseIconPlist.js`| Reads GJ_GameSheet02-uhd.plist and magically transforms it into gameSheet.json. Props to 101arrowz for helping make this |
|
|
||||||
| `colors.json` | List of the player colors in GD. Fairly straight forward |
|
|
||||||
| `forms.json` | List of the different icon forms, their ingame filenames, and their index in responses from the GD servers
|
|
||||||
| `offsets.json` | A bunch of hardcoded offsets. Desperate times call for desperate measures |
|
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
|
@ -169,15 +158,10 @@ Inevitable misc folder
|
||||||
| `achievementTypes.json` | An object containing different categories of achievements (stars, shards, vault, etc) and how to identify them |
|
| `achievementTypes.json` | An object containing different categories of achievements (stars, shards, vault, etc) and how to identify them |
|
||||||
| `credits.json` | Credits! (shown on the homepage) |
|
| `credits.json` | Credits! (shown on the homepage) |
|
||||||
| `dragscroll.js` | Used on several pages for drag scrolling |
|
| `dragscroll.js` | Used on several pages for drag scrolling |
|
||||||
|
| `global.js` | Excecuted on most pages. Used for the 'page isn't wide enough' message, back button, icons, and a few other things |
|
||||||
| `music.json` | An array of the official GD tracks (name, artist) |
|
| `music.json` | An array of the official GD tracks (name, artist) |
|
||||||
| `parseAchievementPlist.js` | A script that reads GD's achievement .plist files and converts it into achievements.json |
|
|
||||||
| `sampleIcons.json` | A pool of icons, one of which will randomly appear when visiting the icon kit. Syntax is [Name, ID, Col1, Col2, Glow] |
|
| `sampleIcons.json` | A pool of icons, one of which will randomly appear when visiting the icon kit. Syntax is [Name, ID, Col1, Col2, Glow] |
|
||||||
| `secretStuff.json` | GJP goes here, needed for level leaderboards. Not included in the repo for obvious reasons |
|
| `secretStuff.json` | GJP goes here, needed for level leaderboards. Not included in the repo for obvious reasons |
|
||||||
| `settings.js` | Tweak small settings here, mainly for local use or GDPS'es |
|
|
||||||
| `shops.js` | A hardcoded list of all the shop icons in GD |
|
|
||||||
| `sizecheck.js` | Excecuted on most pages. Used for the 'page isn't wide enough' message, back button, and a few other things |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
|
||||||
if (err2 && (foundID || authorData)) {
|
if (err2 && (foundID || authorData)) {
|
||||||
let authorInfo = foundID || authorData.split(":")
|
let authorInfo = foundID || authorData.split(":")
|
||||||
level.author = authorInfo[1] || "-"
|
level.author = authorInfo[1] || "-"
|
||||||
level.accountID = authorInfo[0].includes(",") ? "0" : authorInfo[0]
|
level.accountID = authorInfo[0] && authorInfo[0].includes(",") ? "0" : authorInfo[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (!err && b2 != '-1') {
|
else if (!err && b2 != '-1') {
|
||||||
|
|
312
api/icon.js
|
@ -1,312 +0,0 @@
|
||||||
const sharp = require('sharp');
|
|
||||||
const Canvas = require('canvas')
|
|
||||||
const psd = require('ag-psd')
|
|
||||||
const fs = require('fs');
|
|
||||||
const mainPath = `../icons/`
|
|
||||||
const icons = require('../misc/icons/gameSheet.json');
|
|
||||||
const colors = require('../misc/icons/colors.json');
|
|
||||||
const forms = require('../misc/icons/forms.json')
|
|
||||||
const offsets = require('../misc/icons/offsets.json')
|
|
||||||
const legOffsets = require('../misc/icons/legOffsets.json')
|
|
||||||
|
|
||||||
let canvasSize = 300
|
|
||||||
let halfCanvas = canvasSize/2
|
|
||||||
let TRANSPARENT = {r: 0, g: 0, b: 0, alpha: 0}
|
|
||||||
let cache = {}
|
|
||||||
|
|
||||||
let partNames = {
|
|
||||||
"1": "Primary",
|
|
||||||
"2": "Secondary",
|
|
||||||
"3": "UFO Dome",
|
|
||||||
"glow": "Glow",
|
|
||||||
"extra": "White",
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert hex to RGB
|
|
||||||
let hexRegex = /^[A-Fa-f0-9]{6}$/
|
|
||||||
function hexConvert(hex) { hex = hex.replace('#', ''); return {val: hex, r: '0x' + hex[0] + hex[1] | 0, g: '0x' + hex[2] + hex[3] | 0, b: '0x' + hex[4] + hex[5] | 0}; }
|
|
||||||
|
|
||||||
// get path name from icon form and ID
|
|
||||||
function getIconPath(icon, formName) {
|
|
||||||
return `${mainPath}${formName}_${icon < 10 ? "0" : ""}${icon}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// get color from param input
|
|
||||||
function getColor(colInput, defaultCol) {
|
|
||||||
colInput = String(colInput)
|
|
||||||
let foundColor = colors[colInput]
|
|
||||||
if (foundColor) {
|
|
||||||
foundColor.val = colInput
|
|
||||||
return foundColor
|
|
||||||
}
|
|
||||||
else if (colInput.match(hexRegex)) { // custom hex code
|
|
||||||
let hexCol = hexConvert(colInput)
|
|
||||||
colors[colInput.toLowerCase()] = hexCol
|
|
||||||
return hexCol
|
|
||||||
}
|
|
||||||
else if (!foundColor && defaultCol) {
|
|
||||||
let def = colors[defaultCol]
|
|
||||||
def.val = defaultCol
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async (app, req, res) => {
|
|
||||||
|
|
||||||
async function buildIcon(account=[], userCode) {
|
|
||||||
|
|
||||||
let form = forms[req.query.form] || forms["icon"]
|
|
||||||
|
|
||||||
let iconID = req.query.icon || account[form.index] || 1;
|
|
||||||
let col1 = getColor(req.query.col1 || account[10], "0")
|
|
||||||
let col2 = getColor(req.query.col2 || account[11], "3")
|
|
||||||
let colG = getColor(req.query.colG || req.query.colg)
|
|
||||||
let colW = getColor(req.query.colW || req.query.colw || req.query.col3)
|
|
||||||
|
|
||||||
let useGlow = req.query.glow || account[28] || false;
|
|
||||||
if (useGlow && ["false", "0"].includes(useGlow)) useGlow = false
|
|
||||||
if (col1.r == 0 && col1.g == 0 && col1.b == 0 ) useGlow = true
|
|
||||||
|
|
||||||
// bit of a hacky solution for glow color but whatev
|
|
||||||
let glowColor = colG || col2
|
|
||||||
if (glowColor.r == 0 && glowColor.g == 0 && glowColor.b == 0) glowColor = col1
|
|
||||||
if (glowColor.r == 0 && glowColor.g == 0 && glowColor.b == 0) glowColor = {r: 255, g: 255, b: 255}
|
|
||||||
|
|
||||||
let psdExport = req.query.psd || false
|
|
||||||
let topless = form.name == "UFO" ? req.query.topless || false : false
|
|
||||||
|
|
||||||
let customSize = req.query.size == "auto" ? "auto" : +req.query.size || null
|
|
||||||
|
|
||||||
let iconPath = getIconPath(iconID, form.form)
|
|
||||||
|
|
||||||
let iconCode = `${form.name}-${iconID}-${col1.val}-${col2.val}-${colG ? colG.val : "x"}-${colW ? colW.val : "x"}-${useGlow ? 1 : 0}`
|
|
||||||
let cachable = !topless && !customSize && !psdExport
|
|
||||||
if (cachable && cache[iconCode]) return res.end(cache[iconCode].buffer)
|
|
||||||
|
|
||||||
// default to 1 if icon ID does not exist
|
|
||||||
if (!fs.existsSync(getPartName(1).slice(1))) { // slice 1 from filename since fs reads paths differently
|
|
||||||
iconID = 1
|
|
||||||
iconPath = getIconPath(1, form.form)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get path of icon 'part' (1: primary, 2: secondary, 3: ufo top, extra: white, glow: glow, )
|
|
||||||
function getPartName(part, robotPart) {
|
|
||||||
let path = iconPath
|
|
||||||
if (form.legs) path += `_0${robotPart || 1}`
|
|
||||||
if (!part || part == "1") return `${path}_001.png`
|
|
||||||
else return `${path}_${part}_001.png`
|
|
||||||
}
|
|
||||||
|
|
||||||
// recolor white parts of icon to specified color
|
|
||||||
async function recolor(img, col) {
|
|
||||||
let rawData = await img.raw().toBuffer({resolveWithObject: true})
|
|
||||||
for (let i=0; i<rawData.data.length; i += 4) { // [R, G, B, A]
|
|
||||||
if (rawData.data[i + 3] > 0) {
|
|
||||||
rawData.data[i] = col.r / (255 / rawData.data[i]);
|
|
||||||
rawData.data[i + 1] = col.g / (255 / rawData.data[i + 1]);
|
|
||||||
rawData.data[i + 2] = col.b / (255 / rawData.data[i + 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sharp(rawData.data, {raw: {width: rawData.info.width, height: rawData.info.height, channels: 4, background: TRANSPARENT}}).png()
|
|
||||||
}
|
|
||||||
|
|
||||||
// color icon part and add to layer list
|
|
||||||
async function addLayer(part, color, legSection) {
|
|
||||||
|
|
||||||
let leg = legSection ? legSection.leg : null
|
|
||||||
|
|
||||||
let partName = getPartName(part, leg)
|
|
||||||
let offsetData = icons[partName.slice(mainPath.length)]
|
|
||||||
if (!offsetData) return
|
|
||||||
let { spriteSize, spriteOffset } = offsetData
|
|
||||||
|
|
||||||
let builtPart = sharp(partName.slice(1)) // slice 1 from filename since sharp also reads paths differently
|
|
||||||
if (color) builtPart = await recolor(builtPart, color)
|
|
||||||
|
|
||||||
let left = halfCanvas - Math.floor(spriteSize[0] / 2) + spriteOffset[0]
|
|
||||||
let top = halfCanvas - Math.floor(spriteSize[1] / 2) - spriteOffset[1]
|
|
||||||
|
|
||||||
if (legSection) {
|
|
||||||
left += Math.floor(legSection.xPos)
|
|
||||||
top -= Math.floor(legSection.yPos)
|
|
||||||
// if (legSection.darken) builtPart.tint({r: 100, g: 100, b: 100})
|
|
||||||
if (legSection.rotation) {
|
|
||||||
builtPart.rotate(legSection.rotation, {background: TRANSPARENT})
|
|
||||||
if (part == "glow") { left--; top--; }
|
|
||||||
}
|
|
||||||
if (legSection.yScale) builtPart.resize({width: spriteSize[0], height: Math.floor(spriteSize[1] * legSection.yScale), fit: "fill"})
|
|
||||||
if (legSection.xFlip) builtPart.flop()
|
|
||||||
}
|
|
||||||
|
|
||||||
let layerData = {
|
|
||||||
partName, spriteOffset, spriteSize, leg,
|
|
||||||
layerName: partNames[part],
|
|
||||||
behind: legSection && legSection.darken,
|
|
||||||
isGlow: part == "glow",
|
|
||||||
input: await builtPart.toBuffer(),
|
|
||||||
left, top
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legSection) {
|
|
||||||
if (!legLayers[legSection.leg]) legLayers[legSection.leg] = [layerData]
|
|
||||||
else legLayers[legSection.leg].push(layerData)
|
|
||||||
}
|
|
||||||
|
|
||||||
else layers.push(layerData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// build all layers of icon segment (col1, col2, glow, extra)
|
|
||||||
async function buildFullLayer(legSection) {
|
|
||||||
let hasExtra = fs.existsSync(getPartName("extra", legSection ? legSection.leg : null).slice(1))
|
|
||||||
|
|
||||||
if (form.form == "bird" && !topless) await addLayer(3, null, legSection) // ufo top
|
|
||||||
await addLayer(2, col2, legSection) // secondary color
|
|
||||||
if (useGlow) await addLayer("glow", glowColor, legSection) // glow
|
|
||||||
await addLayer(1, col1, legSection) // primary color
|
|
||||||
if (hasExtra) await addLayer("extra", colW, legSection) // extra
|
|
||||||
|
|
||||||
// if (legSection) {
|
|
||||||
// let foundLeg = legLayers[legSection.leg]
|
|
||||||
// foundLeg.forEach(x => layers.push(x))
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
let layers = []
|
|
||||||
let legLayers = []
|
|
||||||
let legData = form.legs ? legOffsets[form.form] || [] : []
|
|
||||||
let parentSize = icons[getPartName(1).slice(mainPath.length)].spriteSize
|
|
||||||
let canvas = sharp({create: {width: canvasSize, height: canvasSize, channels: 4, background: TRANSPARENT}})
|
|
||||||
|
|
||||||
// if (legData.length) {
|
|
||||||
// for (let i=0; i<legData.length; i++) {
|
|
||||||
// await buildFullLayer(legData[i])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
await buildFullLayer()
|
|
||||||
|
|
||||||
// if (legData.length) layers = legLayers.flat().filter(x => x).sort((a, b) => !!b.behind - !!a.behind).sort((a, b) => !!b.isGlow - !!a.isGlow)
|
|
||||||
|
|
||||||
canvas.composite(layers)
|
|
||||||
|
|
||||||
let rawData = await canvas.toBuffer({resolveWithObject: true})
|
|
||||||
let minX = canvasSize; let maxX = 0;
|
|
||||||
let minY = canvasSize; let maxY = 0;
|
|
||||||
for (let i=0; i<rawData.data.length; i += 4) { // [R, G, B, A]
|
|
||||||
let pixelIndex = i/4
|
|
||||||
let x = pixelIndex % canvasSize;
|
|
||||||
let y = Math.floor(pixelIndex / canvasSize);
|
|
||||||
let alpha = rawData.data[i + 3];
|
|
||||||
if (alpha > 0) {
|
|
||||||
if (x < minX) minX = x
|
|
||||||
if (x > maxX) maxX = x
|
|
||||||
if (y < minY) minY = y
|
|
||||||
if (y > maxY) maxY = y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to make a new sharp instance so everything is merged. bit hacky but it works
|
|
||||||
let dimensions = [maxX - minX, maxY - minY]
|
|
||||||
|
|
||||||
if (!psdExport) {
|
|
||||||
let finalIcon = sharp(rawData.data, {raw: {width: canvasSize, height: canvasSize, channels: 4}})
|
|
||||||
.extract({left: minX, top: minY, width: dimensions[0], height: dimensions[1]})
|
|
||||||
|
|
||||||
if (customSize) {
|
|
||||||
let isThicc = dimensions[0] > dimensions[1]
|
|
||||||
let squareSize = req.query.size == "auto" ? (isThicc ? dimensions[0] : dimensions[1]) : Math.floor(req.query.size)
|
|
||||||
if (squareSize < 32) squareSize = 32
|
|
||||||
if (squareSize > 256) squareSize = 256
|
|
||||||
|
|
||||||
// use longest side to make square
|
|
||||||
if (isThicc) finalIcon.resize({
|
|
||||||
width: dimensions[isThicc ? 0 : 1],
|
|
||||||
height: dimensions[isThicc ? 0 : 1],
|
|
||||||
fit: "contain",
|
|
||||||
background: TRANSPARENT
|
|
||||||
})
|
|
||||||
finalIcon.resize({width: squareSize, height: squareSize, fit: "contain", background: TRANSPARENT})
|
|
||||||
}
|
|
||||||
finalIcon.png().toBuffer().then(x => {
|
|
||||||
res.end(x) // send file
|
|
||||||
if (cachable) { // cache for a bit
|
|
||||||
cache[iconCode] = { buffer: x, timeoutID: setTimeout(function() {delete cache[iconCode]}, 10000000) } // cache file for 3 hours
|
|
||||||
if (userCode) cache[userCode] = { buffer: x, timeoutID: setTimeout(function() {delete cache[userCode]}, 300000) } // 5 min cache for player icons
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
let psdLayers = layers.map(x => {
|
|
||||||
let Image = Canvas.Image
|
|
||||||
let canvas = Canvas.createCanvas(...dimensions)
|
|
||||||
let ctx = canvas.getContext('2d');
|
|
||||||
const img = new Image()
|
|
||||||
img.onload = () => {
|
|
||||||
ctx.drawImage(img, 0 + x.left - minX, 0 + x.top - minY)
|
|
||||||
}
|
|
||||||
img.onerror = err => { throw err }
|
|
||||||
img.src = x.input
|
|
||||||
return {name: x.layerName, canvas, leg: x.leg}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (form.legs) {
|
|
||||||
let legLayers = []
|
|
||||||
for (let i=1; i<=form.legs + 1; i++) legLayers.push({name: i == 1 ? "Base" : `Leg ${i}`, opened: true, children: []})
|
|
||||||
psdLayers.forEach(x => {
|
|
||||||
legLayers[x.leg-1].children.push(x)
|
|
||||||
})
|
|
||||||
psdLayers = legLayers.reverse()
|
|
||||||
}
|
|
||||||
|
|
||||||
const photoshop = {
|
|
||||||
width: dimensions[0],
|
|
||||||
height: dimensions[1],
|
|
||||||
children: psdLayers
|
|
||||||
};
|
|
||||||
|
|
||||||
const buffer = psd.writePsdBuffer(photoshop);
|
|
||||||
return res.end(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ==================================== //
|
|
||||||
|
|
||||||
// OLD CODE IS BEING USED FOR ROBOTS AND SPIDERS
|
|
||||||
let formCheck = forms[req.query.form]
|
|
||||||
if (formCheck && formCheck.legs) return app.run.icon_old(app, req, res)
|
|
||||||
|
|
||||||
let username = req.params.text
|
|
||||||
let userCode;
|
|
||||||
res.contentType('image/png');
|
|
||||||
|
|
||||||
if (req.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon()
|
|
||||||
|
|
||||||
else if (app.config.cachePlayerIcons && !Object.keys(req.query).filter(x => !["form", "forceGD"].includes(x)).length) {
|
|
||||||
userCode = `${req.id}u-${username.toLowerCase()}-${forms[req.query.form] ? req.query.form : 'cube'}`
|
|
||||||
if (cache[userCode]) return res.end(cache[userCode].value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id)
|
|
||||||
let foundID = app.userCache(req.id, username)
|
|
||||||
let skipRequest = accountMode || foundID
|
|
||||||
let forceGD = req.query.hasOwnProperty("forceGD")
|
|
||||||
|
|
||||||
// skip request by causing fake error lmao
|
|
||||||
req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : req.gdParams({ str: username, forceGD }, !forceGD), function (err1, res1, body1) {
|
|
||||||
|
|
||||||
let result = foundID ? foundID[0] : (accountMode || err1) ? username : app.parseResponse(body1)[16];
|
|
||||||
|
|
||||||
req.gdRequest('getGJUserInfo20', req.gdParams({ targetAccountID: result, forceGD }, !forceGD), function (err2, res2, body2) {
|
|
||||||
|
|
||||||
if (err2) return buildIcon();
|
|
||||||
let iconData = app.parseResponse(body2)
|
|
||||||
if (!foundID && !forceGD) app.userCache(req.id, iconData[16], iconData[2], iconData[1])
|
|
||||||
return buildIcon(iconData, userCode);
|
|
||||||
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
392
api/icon_old.js
|
@ -1,392 +0,0 @@
|
||||||
// this file is a potential candidate for worst code on github
|
|
||||||
// i advise you to turn back now
|
|
||||||
// seriously, it's not too late
|
|
||||||
|
|
||||||
// update: there is now a new system being used for icons, however, spiders and robots do not work
|
|
||||||
// this old code is being used in the meantime
|
|
||||||
|
|
||||||
const Jimp = require('jimp');
|
|
||||||
const fs = require('fs');
|
|
||||||
const icons = require('../misc/icons/gameSheet.json');
|
|
||||||
const colors = require('../misc/icons/colors.json');
|
|
||||||
const forms = require('../misc/icons/forms.json')
|
|
||||||
const offsets = require('../misc/icons/offsets.json');
|
|
||||||
|
|
||||||
let hexRegex = /^[A-Fa-f0-9]{6}$/
|
|
||||||
function hexConvert(hex) { hex = hex.replace('#', ''); return {r: '0x' + hex[0] + hex[1] | 0, g: '0x' + hex[2] + hex[3] | 0, b: '0x' + hex[4] + hex[5] | 0}; }
|
|
||||||
function recolor(img, col) {
|
|
||||||
return img.scan(0, 0, img.bitmap.width, img.bitmap.height, function (x, y, idx) {
|
|
||||||
if (img.bitmap.data.slice(idx, idx+3).every(function(val) {return val >= 20 && val <= 255})) { // If it's not "black, i.e. we want to recolor it"
|
|
||||||
this.bitmap.data[idx] = colors[col].r / (255 / this.bitmap.data[idx]);
|
|
||||||
this.bitmap.data[idx + 1] = colors[col].g / (255 / this.bitmap.data[idx + 1]);
|
|
||||||
this.bitmap.data[idx + 2] = colors[col].b / (255 / this.bitmap.data[idx + 2]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caveat of genFileName is that if there are any falsey values in the arguments they are ignored.
|
|
||||||
This is usually a good thing though - avoid issues by not putting something like 0 instead of '0' */
|
|
||||||
function genFileName(...args) { return args.filter(function(val) {return val}).join('_') +'_001.png' }
|
|
||||||
function fromIcons(filename) { return `./icons/${filename}` }
|
|
||||||
let cache = {};
|
|
||||||
|
|
||||||
module.exports = async (app, req, res) => {
|
|
||||||
|
|
||||||
function buildIcon(account=[], usercode) {
|
|
||||||
|
|
||||||
let { form, ind } = forms[req.query.form] || {};
|
|
||||||
form = form || 'player';
|
|
||||||
ind = ind || 21;
|
|
||||||
|
|
||||||
let iconID = req.query.icon || account[ind] || 1;
|
|
||||||
let col1 = req.query.col1 || account[10] || 0;
|
|
||||||
let col2 = req.query.col2 || account[11] || 3;
|
|
||||||
let colG = req.query.colG || req.query.colg
|
|
||||||
let colW = req.query.colW || req.query.colw || req.query.col3
|
|
||||||
let outline = req.query.glow || account[28] || "0";
|
|
||||||
|
|
||||||
let topless = form == "bird" && req.query.topless
|
|
||||||
let drawLegs = !(req.query.noLegs > 0)
|
|
||||||
let autoSize = req.query.size == "auto"
|
|
||||||
let sizeParam = autoSize || (req.query.size && !isNaN(req.query.size))
|
|
||||||
if (outline == "0" || outline == "false") outline = false;
|
|
||||||
|
|
||||||
if (iconID && iconID.toString().length == 1) iconID = "0" + iconID;
|
|
||||||
|
|
||||||
function genImageName(...args) { return genFileName(form, iconID, ...args) }
|
|
||||||
|
|
||||||
let icon, glow, extra;
|
|
||||||
function setBaseIcons() {
|
|
||||||
icon = genImageName(isSpecial && '01');
|
|
||||||
glow = genImageName(isSpecial && '01', '2');
|
|
||||||
extra = genImageName(isSpecial && '01', 'extra');
|
|
||||||
}
|
|
||||||
let isSpecial = ['robot', 'spider'].includes(form);
|
|
||||||
setBaseIcons();
|
|
||||||
|
|
||||||
if (!fs.existsSync(fromIcons(icon)) || (isSpecial && !fs.existsSync(fromIcons(genImageName('02'))))) {
|
|
||||||
iconID = '01';
|
|
||||||
setBaseIcons();
|
|
||||||
}
|
|
||||||
|
|
||||||
let ex = fromIcons(extra)
|
|
||||||
let hasExtra = fs.existsSync(ex)
|
|
||||||
|
|
||||||
let cols = [col1, col2, colG, colW]
|
|
||||||
cols.forEach(col => {
|
|
||||||
if (!col) return
|
|
||||||
col = col.toString()
|
|
||||||
if (col.match(hexRegex)) colors[col.toLowerCase()] = hexConvert(col)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!colors[col1] || isNaN(colors[col1].r)) col1 = colors[+col1] ? +col1 : 0
|
|
||||||
if (!colors[col2] || isNaN(colors[col2].r)) col2 = colors[+col2] ? +col2 : 3
|
|
||||||
if (!colors[colG] || isNaN(colors[colG].r)) colG = colors[+colG] ? +colG : null
|
|
||||||
if (!colors[colW] || isNaN(colors[colW].r)) colW = colors[+colW] ? +colW : null
|
|
||||||
if (colW && (!hasExtra || colW == 12)) colW = null
|
|
||||||
|
|
||||||
if (col1 == 15 || col1 === "000000") outline = true;
|
|
||||||
|
|
||||||
let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "top" : ""}-${iconID}-${col1}-${col2}-${colG || "x"}-${colW || "x"}-${outline ? 1 : 0}`
|
|
||||||
|
|
||||||
if (!sizeParam && (!isSpecial || drawLegs) && cache[iconCode]) return res.end(cache[iconCode].value)
|
|
||||||
|
|
||||||
let useExtra = false
|
|
||||||
let originalOffset = icons[icon].spriteOffset;
|
|
||||||
let minusOrigOffset = function(x, y) { return x - originalOffset[y] }
|
|
||||||
let offset = icons[glow].spriteOffset.map(minusOrigOffset);
|
|
||||||
let robotLeg1, robotLeg2, robotLeg3, robotLeg3b, robotLeg2b, robotLeg1b, robotLeg1c;
|
|
||||||
let robotOffset1, robotOffset2, robotOffset3, robotOffset1b, robotOffset2b, robotOffset3b;
|
|
||||||
let robotGlow1, robotGlow2, robotGlow3, glowOffset
|
|
||||||
let ufoTop, ufoOffset, ufoCoords, ufoSprite
|
|
||||||
let extrabit, offset2, size2;
|
|
||||||
|
|
||||||
if (isSpecial) {
|
|
||||||
const legs = [1,2,3].map(function(val) {return genImageName(`0${val+1}`)});
|
|
||||||
const glows = [1,2,3].map(function(val) {return genImageName(`0${val+1}`, '2')});
|
|
||||||
robotOffset1 = icons[legs[0]].spriteOffset.map(minusOrigOffset).concat(icons[legs[0]].spriteSize);
|
|
||||||
robotOffset2 = icons[legs[1]].spriteOffset.map(minusOrigOffset).concat(icons[legs[1]].spriteSize);
|
|
||||||
robotOffset3 = icons[legs[2]].spriteOffset.map(minusOrigOffset).concat(icons[legs[2]].spriteSize);
|
|
||||||
|
|
||||||
robotOffset1b = icons[glows[0]].spriteOffset.map(minusOrigOffset).concat(icons[glows[0]].spriteSize);
|
|
||||||
robotOffset2b = icons[glows[1]].spriteOffset.map(minusOrigOffset).concat(icons[glows[1]].spriteSize);
|
|
||||||
robotOffset3b = icons[glows[2]].spriteOffset.map(minusOrigOffset).concat(icons[glows[2]].spriteSize);
|
|
||||||
|
|
||||||
robotLeg1 = new Jimp(fromIcons(legs[0])); robotGlow1 = new Jimp(fromIcons(glows[0]))
|
|
||||||
robotLeg2 = new Jimp(fromIcons(legs[1])); robotGlow2 = new Jimp(fromIcons(glows[1]))
|
|
||||||
robotLeg3 = new Jimp(fromIcons(legs[2])); robotGlow3 = new Jimp(fromIcons(glows[2]))
|
|
||||||
|
|
||||||
glowOffset = offsets[form][+iconID] || []
|
|
||||||
}
|
|
||||||
|
|
||||||
Jimp.read(fromIcons(glow)).then(async function (image) {
|
|
||||||
|
|
||||||
let size = [image.bitmap.width, image.bitmap.height]
|
|
||||||
let glow = recolor(image, col2)
|
|
||||||
let imgOff = isSpecial ? 100 : 0
|
|
||||||
|
|
||||||
let eb = fromIcons(extra)
|
|
||||||
if (fs.existsSync(eb)) {
|
|
||||||
extrabit = icons[extra]
|
|
||||||
offset2 = extrabit.spriteOffset.map(minusOrigOffset);
|
|
||||||
size2 = extrabit.spriteSize;
|
|
||||||
extra = new Jimp(eb);
|
|
||||||
if (colW) await Jimp.read(eb).then(e => { extra = recolor(e, colW) })
|
|
||||||
useExtra = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Jimp.read(fromIcons(icon)).then(async function (ic) {
|
|
||||||
|
|
||||||
let iconSize = [ic.bitmap.width, ic.bitmap.height]
|
|
||||||
recolor(ic, col1)
|
|
||||||
ic.composite(glow, (iconSize[0] / 2) - (size[0] / 2) + offset[0], (iconSize[1] / 2) - (size[1] / 2) - offset[1], { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
|
|
||||||
if (form == "bird" && !topless) {
|
|
||||||
ufoTop = genImageName('3')
|
|
||||||
ufoOffset = icons[ufoTop].spriteOffset.map(minusOrigOffset).concat(icons[ufoTop].spriteSize);
|
|
||||||
ufoCoords = [imgOff + (iconSize[0] / 2) - (ufoOffset[2] / 2) + ufoOffset[0], (iconSize[1] / 2) - (ufoOffset[3] / 2) - ufoOffset[1] + 300 - iconSize[1]]
|
|
||||||
ufoSprite = fromIcons(ufoTop)
|
|
||||||
ic.contain(iconSize[0], 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_BOTTOM)
|
|
||||||
// Only add dome if there's no glow, otherwise the dome will be outlined as well
|
|
||||||
if (!outline) ic.composite(await Jimp.read(ufoSprite), ufoCoords[0], ufoCoords[1], {mode: Jimp.BLEND_DESTINATION_OVER})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drawLegs && (form == "robot" || req.query.form == "cursed")) {
|
|
||||||
|
|
||||||
ic.contain(iconSize[0], 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP)
|
|
||||||
ic.contain(iconSize[0] + 200, 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP)
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotGlow1)).then(rob => {
|
|
||||||
rob.rotate(-45)
|
|
||||||
robotGlow1 = recolor(rob, col2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotGlow2)).then(rob => {
|
|
||||||
rob.rotate(45)
|
|
||||||
robotGlow2 = recolor(rob, col2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotGlow3)).then(rob => {
|
|
||||||
robotGlow3 = recolor(rob, col2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg1)).then(rob => {
|
|
||||||
rob.rotate(-45)
|
|
||||||
recolor(rob, col1)
|
|
||||||
rob.composite(robotGlow1, (robotOffset1[2] - robotOffset1b[2]) + (glowOffset[0] || 1), ((robotOffset1[3] - robotOffset1b[3]) / 2) + (glowOffset[1] || 0), { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
robotLeg1 = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg2)).then(rob => {
|
|
||||||
rob.rotate(45)
|
|
||||||
recolor(rob, col1)
|
|
||||||
rob.composite(robotGlow2, ((robotOffset2[2] - robotOffset2b[2]) / 4) + (glowOffset[4] || 0), ((robotOffset2[3] - robotOffset2b[3]) / 2) + (glowOffset[5] || 0), { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
robotLeg2 = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg2)).then(rob => {
|
|
||||||
robotLeg2b = rob.color([{ apply: 'darken', params: [20] }]).rotate(-5)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg3)).then(rob => {
|
|
||||||
recolor(rob, col1)
|
|
||||||
rob.composite(robotGlow3, ((robotOffset3[2] - robotOffset3b[2]) / 2) + (glowOffset[2] || 0), ((robotOffset3[3] - robotOffset3b[3]) / 2) + (glowOffset[3] || 0), { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
robotLeg3 = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg3)).then(rob => {
|
|
||||||
robotLeg3b = rob.color([{ apply: 'darken', params: [10] }])
|
|
||||||
})
|
|
||||||
|
|
||||||
ic.composite(robotLeg2b, 100 + (iconSize[0] / 2) - (robotOffset2[2]) + robotOffset2[0] - 31, (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 73)
|
|
||||||
ic.composite(robotLeg3b, 100 + (iconSize[0] / 2) - (robotOffset3[2]) + robotOffset3[0] + 20, (iconSize[1] / 2) - (robotOffset3[3]) - robotOffset3[1] + 78)
|
|
||||||
ic.composite(robotLeg2, 100 + (iconSize[0] / 2) - (robotOffset2[2]) + robotOffset2[0] - 20, (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 73)
|
|
||||||
ic.composite(robotLeg3, 100 + (iconSize[0] / 2) - (robotOffset3[2]) + robotOffset3[0] + 40, (iconSize[1] / 2) - (robotOffset3[3]) - robotOffset3[1] + 78)
|
|
||||||
ic.composite(robotLeg1, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] - 20, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 50)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (drawLegs && form == "spider") {
|
|
||||||
|
|
||||||
let spiderBody;
|
|
||||||
ic.contain(iconSize[0], 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP)
|
|
||||||
ic.contain(iconSize[0] + 200, 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP)
|
|
||||||
|
|
||||||
if (iconID == "07") {
|
|
||||||
robotOffset2[2] -= 10
|
|
||||||
robotOffset2[1] += 12
|
|
||||||
robotOffset1b[3] -= 105
|
|
||||||
robotOffset2b[3] -= 150
|
|
||||||
robotOffset2b[2] -= 60
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iconID == "16") {
|
|
||||||
robotOffset1b[3] -= 100
|
|
||||||
robotOffset2b[3] -= 200
|
|
||||||
robotOffset2b[2] -= 30
|
|
||||||
}
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotGlow1)).then(rob => {
|
|
||||||
if (robotGlow1.bitmap.width < 10) robotGlow1.opacity(0)
|
|
||||||
else robotGlow1 = recolor(rob, col2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotGlow2)).then(rob => {
|
|
||||||
robotGlow2 = recolor(rob, col2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotGlow3)).then(rob => {
|
|
||||||
robotGlow3 = recolor(rob, col2)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg1)).then(rob => {
|
|
||||||
recolor(rob, col1)
|
|
||||||
rob.composite(robotGlow1, ((robotOffset1[2] - robotOffset1b[2]) / 2) + (glowOffset[2] || 0), ((robotOffset1[3] - robotOffset1b[3]) / 4) + (glowOffset[3] || 0), { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
robotLeg1 = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg2)).then(rob => {
|
|
||||||
recolor(rob, col1)
|
|
||||||
rob.composite(robotGlow2, ((robotOffset2[2] - robotOffset2b[2]) / 6) + (glowOffset[0] || 0), ((robotOffset2[3] - robotOffset2b[3]) / 6) + (glowOffset[1] || 0), { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
rob.rotate(-40)
|
|
||||||
robotLeg2 = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg1)).then(rob => {
|
|
||||||
robotLeg1b = rob.color([{ apply: 'darken', params: [20] }])
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg1b)).then(rob => {
|
|
||||||
robotLeg1c = rob.mirror(true, false)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(robotLeg3)).then(rob => {
|
|
||||||
recolor(rob, col1)
|
|
||||||
rob.composite(robotGlow3, ((robotOffset3[2] - robotOffset3b[2]) / 2) + (glowOffset[4] || 0), ((robotOffset3[3] - robotOffset3b[3]) / 2) + (glowOffset[5] || 0), { mode: Jimp.BLEND_DESTINATION_OVER })
|
|
||||||
robotLeg3 = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
await Jimp.read(new Jimp(ic)).then(rob => {
|
|
||||||
spiderBody = rob
|
|
||||||
})
|
|
||||||
|
|
||||||
ic.composite(robotLeg3, 100 + (iconSize[0] / 2) - (robotOffset3[2]) + (robotOffset3[0]), (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 77)
|
|
||||||
ic.composite(robotLeg1b, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] + 35, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 70)
|
|
||||||
ic.composite(robotLeg1c, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] + 75, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 70)
|
|
||||||
// ^ BELOW
|
|
||||||
ic.composite(spiderBody, 0, 0)
|
|
||||||
// v ABOVE
|
|
||||||
ic.composite(robotLeg2, 100 + (iconSize[0] / 2) - (robotOffset2[2]) + robotOffset2[0] - 60, (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 75)
|
|
||||||
ic.composite(robotLeg1, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] + 7, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 70)
|
|
||||||
}
|
|
||||||
|
|
||||||
// every now and then jimp does a fucky wucky uwu and this line errors. seems to be an issue with the lib itself :v
|
|
||||||
try { if (useExtra) ic.composite(extra, imgOff + (iconSize[0] / 2) - (size2[0] / 2) + offset2[0], (iconSize[1] / 2) - (size2[1] / 2) - offset2[1] + (form == "bird" && !req.query.topless ? 300 - iconSize[1] : 0)) }
|
|
||||||
catch(e) {}
|
|
||||||
|
|
||||||
let finalSize = [ic.bitmap.width, ic.bitmap.height]
|
|
||||||
|
|
||||||
function finish(img) {
|
|
||||||
img.autocrop(0.01, false)
|
|
||||||
if (form == "swing") img.resize(120, 111)
|
|
||||||
if (img.bitmap.height == 300) ic.autocrop(1, false)
|
|
||||||
if (sizeParam) {
|
|
||||||
let thicc = img.bitmap.width > img.bitmap.height
|
|
||||||
let imgSize = req.query.size == "auto" ? (thicc ? img.bitmap.width : img.bitmap.height) : Math.round(req.query.size)
|
|
||||||
if (imgSize < 32) imgSize = 32
|
|
||||||
if (imgSize > 512) imgSize = 512
|
|
||||||
if (thicc) img.contain(img.bitmap.width, img.bitmap.width, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
|
||||||
else img.contain(img.bitmap.height, img.bitmap.height, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
|
||||||
img.resize(imgSize, Jimp.AUTO)
|
|
||||||
}
|
|
||||||
img.getBuffer(Jimp.AUTO, (err, buffer) => {
|
|
||||||
if (!sizeParam && drawLegs) {
|
|
||||||
cache[iconCode] = { value: buffer, timeoutID: setTimeout(function() {delete cache[iconCode]}, 10000000) } // 3 hour cache
|
|
||||||
if (usercode) cache[usercode] = { value: buffer, timeoutID: setTimeout(function() {delete cache[usercode]}, 300000) } // 5 min cache for player icons
|
|
||||||
}
|
|
||||||
return res.end(buffer, 'base64')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!outline) return finish(ic)
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
ic.getBuffer(Jimp.AUTO, function (err, buff) {
|
|
||||||
|
|
||||||
const Canvas = require('canvas')
|
|
||||||
, Image = Canvas.Image
|
|
||||||
, canvas = Canvas.createCanvas(finalSize[0] + 10, finalSize[1] + 10)
|
|
||||||
, ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
if (!colG) colG = (col2 == 15 || col2 == "000000" ? col1 : col2)
|
|
||||||
if (colG == 15 || colG == "000000") colG = 12
|
|
||||||
|
|
||||||
const img = new Image()
|
|
||||||
img.onload = () => {
|
|
||||||
var dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1], // offset array
|
|
||||||
s = 2, i = 0, x = canvas.width / 2 - finalSize[0] / 2, y = canvas.height / 2 - finalSize[1] / 2;
|
|
||||||
|
|
||||||
for (; i < dArr.length; i += 2) ctx.drawImage(img, x + dArr[i] * s, y + dArr[i + 1] * s);
|
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "source-in";
|
|
||||||
ctx.fillStyle = `rgba(${colors[colG].r}, ${colors[colG].g}, ${colors[colG].b}, 1})`;
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
ctx.globalCompositeOperation = "source-over";
|
|
||||||
ctx.imageSmoothingEnabled = false;
|
|
||||||
|
|
||||||
// Add UFO top last so it doesn't get glow'd
|
|
||||||
if (form == "bird" && !topless) {
|
|
||||||
const dome = new Image()
|
|
||||||
dome.src = ufoSprite
|
|
||||||
ctx.drawImage(dome, ufoCoords[0]+5, ufoCoords[1]+5)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.drawImage(img, x, y)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
img.onerror = err => { throw err }
|
|
||||||
img.src = buff
|
|
||||||
|
|
||||||
Jimp.read(canvas.toBuffer()).then(b => {
|
|
||||||
return finish(b)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let username = req.params.text
|
|
||||||
let userCode;
|
|
||||||
res.contentType('image/png');
|
|
||||||
|
|
||||||
if (req.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon()
|
|
||||||
|
|
||||||
else if (app.config.cachePlayerIcons && !Object.keys(req.query).filter(x => !["form", "forceGD"].includes(x)).length) {
|
|
||||||
userCode = `${req.id}u-${username.toLowerCase()}-${forms[req.query.form] ? req.query.form : 'cube'}`
|
|
||||||
if (cache[userCode]) return res.end(cache[userCode].value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id)
|
|
||||||
let foundID = app.userCache(req.id, username)
|
|
||||||
let skipRequest = accountMode || foundID
|
|
||||||
let forceGD = req.query.hasOwnProperty("forceGD")
|
|
||||||
|
|
||||||
// skip request by causing fake error lmao
|
|
||||||
req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : req.gdParams({ str: username, forceGD }, !forceGD), function (err1, res1, body1) {
|
|
||||||
|
|
||||||
let result = foundID ? foundID[0] : (accountMode || err1) ? username : app.parseResponse(body1)[16];
|
|
||||||
|
|
||||||
req.gdRequest('getGJUserInfo20', req.gdParams({ targetAccountID: result, forceGD }, !forceGD), function (err2, res2, body2) {
|
|
||||||
|
|
||||||
if (err2) return buildIcon();
|
|
||||||
let iconData = app.parseResponse(body2)
|
|
||||||
if (!foundID && !forceGD) app.userCache(req.id, iconData[16], iconData[2], iconData[1])
|
|
||||||
return buildIcon(iconData, userCode);
|
|
||||||
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
const colors = require('../../misc/icons/colors.json');
|
const colors = require('../../iconkit/sacredtexts/colors.json');
|
||||||
|
|
||||||
module.exports = async (app, req, res) => {
|
module.exports = async (app, req, res) => {
|
||||||
|
|
||||||
|
|
BIN
assets/colU.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
|
@ -726,10 +726,9 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
|
||||||
|
|
||||||
.commentPercent {
|
.commentPercent {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin: 0 0 0 1vh;
|
margin: 0 0 0 1.5vh;
|
||||||
font-size: 2vh;
|
font-size: 2vh;
|
||||||
color: rgba(0, 0, 0, 0.5);
|
color: rgba(0, 0, 0, 0.5);
|
||||||
transform: translateY(40%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentLikes {
|
.commentLikes {
|
||||||
|
@ -743,8 +742,54 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
|
||||||
transform: translateY(-19%)
|
transform: translateY(-19%)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.commentIcon {
|
||||||
|
margin-right: 1.25%;
|
||||||
|
width: 3.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentIcon img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentRow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creditsIcon {
|
||||||
|
height: 30%;
|
||||||
|
margin-bottom: 7%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creditsIcon img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.specialThanksIcon img {
|
||||||
|
width: 42%;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.leaderboardSlot {
|
.leaderboardSlot {
|
||||||
height: 25%;
|
height: 25%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding-left: 0%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboardName {
|
||||||
|
margin-right: 2.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboardStars {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.weeklyStuff {
|
.weeklyStuff {
|
||||||
|
@ -752,10 +797,38 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ranking {
|
.ranking {
|
||||||
transform:scale(0.82) translate(-20.7vh, -20vh);
|
display: flex;
|
||||||
position: absolute;
|
padding-left: 5%;
|
||||||
height: 10%;
|
padding-right: 2%;
|
||||||
width: 12.5%;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 88%;
|
||||||
|
width: 10%
|
||||||
|
}
|
||||||
|
|
||||||
|
.ranking h2 {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboardIcon {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboardIcon img {
|
||||||
|
height: 80%;
|
||||||
|
max-width: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leaderboardSide {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0.5%
|
||||||
}
|
}
|
||||||
|
|
||||||
#collectibles, .leaderboardStats {
|
#collectibles, .leaderboardStats {
|
||||||
|
@ -769,12 +842,16 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaderboardStats {
|
.leaderboardStats {
|
||||||
|
display: flex;
|
||||||
margin-top: 2%;
|
margin-top: 2%;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaderboardStats img {
|
.leaderboardStats img {
|
||||||
transform: translate(-20%, -7%);
|
|
||||||
width: 4.3%;
|
width: 4.3%;
|
||||||
|
margin-left: 1%;
|
||||||
|
margin-right: 2.5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#boomling {
|
#boomling {
|
||||||
|
|
|
@ -36,7 +36,7 @@ h1 {
|
||||||
text-shadow: 3px 3px 0.5px rgba(0, 0, 0, 0.3);
|
text-shadow: 3px 3px 0.5px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2, select, input[type=number] {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 0 0;
|
margin: 0 0;
|
||||||
font-size: 35px;
|
font-size: 35px;
|
||||||
|
@ -89,13 +89,6 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:not([type='checkbox']), select {
|
|
||||||
padding: 7 7 7 7;
|
|
||||||
font-size: 14px;
|
|
||||||
width: 15%;
|
|
||||||
border-style: 1px inset black;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, input:-webkit-autofill:active {
|
input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, input:-webkit-autofill:active {
|
||||||
box-shadow: 0 0 0px 1000px #764F1A inset !important;
|
box-shadow: 0 0 0px 1000px #764F1A inset !important;
|
||||||
-webkit-box-shadow: 0 0 0px 1000px #764F1A inset !important;
|
-webkit-box-shadow: 0 0 0px 1000px #764F1A inset !important;
|
||||||
|
@ -116,6 +109,18 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
font-family: "Roboto";
|
font-family: "Roboto";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#iconbox {
|
||||||
|
display: flex;
|
||||||
|
max-height: 175px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#result {
|
||||||
|
object-fit: contain;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
#textbox {
|
#textbox {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
|
@ -172,12 +177,6 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
.squareIcon {
|
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
|
||||||
padding: 5px 5px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#url {
|
#url {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
|
@ -238,7 +237,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
|
|
||||||
#generate {
|
#generate {
|
||||||
font-family: "Roboto";
|
font-family: "Roboto";
|
||||||
border: transparent;
|
border: rgba(0, 0, 0, 0);
|
||||||
background-color: #88FF33;
|
background-color: #88FF33;
|
||||||
box-shadow: 2px 2px 3px #66AA22;
|
box-shadow: 2px 2px 3px #66AA22;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
@ -259,7 +258,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
|
|
||||||
.miniButton {
|
.miniButton {
|
||||||
font-family: "Roboto";
|
font-family: "Roboto";
|
||||||
border: transparent;
|
border: rgba(0, 0, 0, 0);
|
||||||
background-color: #88FF33;
|
background-color: #88FF33;
|
||||||
box-shadow: 1.5px 1.5px 2px #66AA22;
|
box-shadow: 1.5px 1.5px 2px #66AA22;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
@ -282,16 +281,36 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 10px;
|
||||||
margin: 0 auto 0 auto;
|
margin: 0 auto 0 auto;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iconContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconContainer button {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconContainer button img {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
.iconKit button, .colTypes button {
|
.iconKit button, .colTypes button {
|
||||||
margin: 0 0 0 0;
|
margin: 0 0 0 0;
|
||||||
padding: 0 0 0 0;
|
padding: 0px 0px;
|
||||||
border: 5px solid transparent;
|
border: 5px solid rgba(0, 0, 0, 0); /* need this for border image to work */
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconSelected {
|
.iconSelected {
|
||||||
|
@ -301,6 +320,11 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
.iconTabButton, .glowToggle, .copyForm {
|
.iconTabButton, .glowToggle, .copyForm {
|
||||||
margin: 0 5 0 5;
|
margin: 0 5 0 5;
|
||||||
transition: transform .1s ease-in-out;
|
transition: transform .1s ease-in-out;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconTabButton:focus-visible, .glowToggle:focus-visible, .copyForm:focus-visible, .menuButton:focus-visible, .postButton:focus-visible {
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.colorPicker {
|
.colorPicker {
|
||||||
|
@ -342,6 +366,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
|
|
||||||
#colors {
|
#colors {
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
|
max-width: 90%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 10 10 10 10;
|
padding: 10 10 10 10;
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
@ -349,7 +374,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
|
|
||||||
#colors::-webkit-scrollbar, #iconKitParent::-webkit-scrollbar {
|
#colors::-webkit-scrollbar, #iconKitParent::-webkit-scrollbar {
|
||||||
width: 9px;
|
width: 9px;
|
||||||
height: 9px;
|
height: 10px;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,30 +383,35 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
#iconKitParent {
|
#iconKitParent {
|
||||||
|
min-height: 90px;
|
||||||
max-height: 210px;
|
max-height: 210px;
|
||||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
|
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#iconKitParent div {
|
|
||||||
margin-top: -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#colors div {
|
#colors div {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iconColor div {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
#gdfloor {
|
#gdfloor {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
margin-top: 0px;
|
margin-top: 1px;
|
||||||
border: 0;
|
border: 0;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
background-image: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
background-image: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
|
||||||
|
position: relative;
|
||||||
|
z-index: -10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menuButton {
|
.menuButton {
|
||||||
margin: 0 5 0 5;
|
margin: 0 5 0 5;
|
||||||
transition: transform .15s ease-in-out;
|
transition: transform .15s ease-in-out;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menuButton:hover {
|
.menuButton:hover {
|
||||||
|
@ -394,21 +424,23 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
|
|
||||||
#iconprogressbar {
|
#iconprogressbar {
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
height: 12px;
|
height: 7px;
|
||||||
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#iconloading {
|
#iconloading {
|
||||||
background: rgba(255, 255, 255, 0.5);
|
background: rgba(255, 255, 255, 0.5);
|
||||||
height: 12px;
|
height: 7px;
|
||||||
width: 0%;
|
width: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconScroll::-webkit-scrollbar {
|
body::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
width: 0;
|
width: 0;
|
||||||
background: transparent;
|
background: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconHover {
|
.iconHover, .iconButton:focus-visible, .iconColor:focus-visible {
|
||||||
transform: scale(1.075);
|
transform: scale(1.075);
|
||||||
background-color: rgb(255, 255, 255, 0.2);
|
background-color: rgb(255, 255, 255, 0.2);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
@ -425,7 +457,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.brownBox {
|
.brownBox {
|
||||||
border: 17px solid transparent;
|
border: 17px solid rgba(0, 0, 0, 0);
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
background-color: #995533;
|
background-color: #995533;
|
||||||
border-image: url('../../assets/brownbox.png') 10% round;
|
border-image: url('../../assets/brownbox.png') 10% round;
|
||||||
|
@ -446,7 +478,7 @@ input[type=text] {
|
||||||
color: white;
|
color: white;
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
text-shadow: -0.2vh -0.2vh 0vh #000, 0.2vh -0.2vh 0vh #000, -0.2vh 0.2vh 0vh #000, 0.2vh 0.2vh 0vh #000;
|
text-shadow: -0.2vh -0.2vh 0vh #000, 0.2vh -0.2vh 0vh #000, -0.2vh 0.2vh 0vh #000, 0.2vh 0.2vh 0vh #000;
|
||||||
border: 0 solid transparent;
|
border: none;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -458,6 +490,22 @@ input[type=text]::placeholder {
|
||||||
text-shadow: -0.15vh -0.15vh 0vh #000, 0.15vh -0.15vh 0vh #000, -0.15vh 0.15vh 0vh #000, 0.15vh 0.15vh 0vh #000;
|
text-shadow: -0.15vh -0.15vh 0vh #000, 0.15vh -0.15vh 0vh #000, -0.15vh 0.15vh 0vh #000, 0.15vh 0.15vh 0vh #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=number] {
|
||||||
|
border: none;
|
||||||
|
border-radius: 7px;
|
||||||
|
height: 50px;
|
||||||
|
width: 100px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
white-space: nowrap;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
input[type=checkbox] {
|
input[type=checkbox] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -478,12 +526,105 @@ input[type=checkbox]:checked + label.gdcheckbox {
|
||||||
background-image: url(../../assets/check-on.png);
|
background-image: url(../../assets/check-on.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#settingList {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settingList div {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settingList .gdCheckbox {
|
||||||
|
margin: 0px 0px 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animationSelector {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animationSelector select {
|
||||||
|
border-radius: 7px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
width: 300px;
|
||||||
|
height: 45px;
|
||||||
|
font-size: 32px;
|
||||||
|
padding-left: 7px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animationSelector option {
|
||||||
|
color: black;
|
||||||
|
font-size: 20px;
|
||||||
|
font-family: aller, helvetica, arial;
|
||||||
|
letter-spacing: unset;
|
||||||
|
-webkit-text-stroke-width: unset;
|
||||||
|
-webkit-text-stroke-color: unset;
|
||||||
|
text-shadow: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animationSelector h2 {
|
||||||
|
width: unset
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"] {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
appearance: none !important;
|
||||||
|
height: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
border: 1px solid;
|
||||||
|
cursor: pointer;
|
||||||
|
border-image: url(../../assets/slider_track.png);
|
||||||
|
border-image-slice: 21 22 22 21;
|
||||||
|
border-image-width: 30px 30px 30px 30px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-webkit-slider-thumb {
|
||||||
|
appearance: none !important;
|
||||||
|
height: 45px;
|
||||||
|
width: 45px;
|
||||||
|
background-image: url("../../assets/slider_thumb.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-webkit-slider-thumb:active { background-image: url("../../assets/slider_thumb_active.png"); }
|
||||||
|
|
||||||
|
#extraInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: left;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extraInfo div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: left;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#extraInfo p {
|
||||||
|
color: white;
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 0px 0px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.bounce {
|
.bounce {
|
||||||
animation: boxAnimator 0.25s;
|
animation: boxAnimator 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grayscale {
|
.greyedOut {
|
||||||
filter: grayscale(100%) brightness(0.7);
|
filter: grayscale(100%) brightness(0.7);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gold {
|
.gold {
|
||||||
|
@ -502,6 +643,10 @@ input[type=checkbox]:checked + label.gdcheckbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1100px) {
|
||||||
|
.hideIfSmall { display: none !important; }
|
||||||
|
}
|
||||||
|
|
||||||
.spin {
|
.spin {
|
||||||
-webkit-animation: spin 2s linear infinite;
|
-webkit-animation: spin 2s linear infinite;
|
||||||
-moz-animation: spin 2s linear infinite;
|
-moz-animation: spin 2s linear infinite;
|
||||||
|
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 36 KiB |
BIN
assets/iconkitbuttons/copied.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
assets/iconkitbuttons/copy.png
Normal file
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 8 KiB |
Before Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 101 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 102 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 101 B |
Before Width: | Height: | Size: 101 B |
Before Width: | Height: | Size: 101 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 103 B |
Before Width: | Height: | Size: 101 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 100 B |
Before Width: | Height: | Size: 1,020 B |
Before Width: | Height: | Size: 1.1 KiB |