Lots of QOL + Icon Kit improvements

- added an optional col3 to icon kit
- added an options menu to the icon kit
- added a random button to the icon kit
- added a proper popup for the icon kit's 'steal icon' button
- redid the buttons on the icon kit
- tripled icon cache duration
- rewrote and minified the XOR class
- idk some other fun stuff
This commit is contained in:
GDColon 2020-09-22 18:37:21 -04:00
parent 4fa705de44
commit 351f1c0ad8
20 changed files with 335 additions and 138 deletions

View file

@ -20,7 +20,7 @@ module.exports = async (app, req, res) => {
sheet.loadInfo().then(async () => {
let tab = sheet.sheetsById[1555821000]
await tab.loadCells('A2:C2')
let topPlayers = tab.getCell(1, type == "demons" ? 2 : type == "usercoins" ? 1 : 0).value
let topPlayers = tab.getCell(1, type == "demons" ? 2 : type == "coins" ? 1 : 0).value
let idArray = topPlayers.split(",")

View file

@ -23,7 +23,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
}
}, async function (err, resp, body) {
if (err || !body || body == '-1') {
if (err || !body || body == '-1' || body.startsWith("<!")) {
if (!api && levelID < 0) return res.redirect('/')
if (!api) return res.redirect('search/' + req.params.id)
else return res.send("-1")

View file

@ -6,7 +6,6 @@ const icons = require('../icons/gameSheet.json');
const colors = require('../misc/colors.json');
const forms = require('../icons/forms.json')
const offsets = require('../icons/offsets.json');
const { finished } = require('stream');
function recolor(img, col) {
return img.scan(0, 0, img.bitmap.width, img.bitmap.height, function (x, y, idx) {
@ -44,7 +43,8 @@ module.exports = async (app, req, res) => {
if (!glowOffset.some(x => x != 0)) glowOffset = []
let topless = form == "bird" && req.query.topless
let sizeParam = req.query.size && !isNaN(req.query.size)
let autoSize = req.query.size == "auto"
let sizeParam = autoSize || (req.query.size && !isNaN(req.query.size))
if (outline == "0") outline = false;
if (iconID && iconID.toString().length == 1) iconID = "0" + iconID;
@ -69,14 +69,20 @@ module.exports = async (app, req, res) => {
if (!fs.existsSync(fromIcons(icon))) return res.sendFile(path.join(__dirname, '../assets/unknownIcon.png'))
}
let ex = fromIcons(extra)
let hasExtra = fs.existsSync(ex)
if (!colors[col1]) col1 = 0
if (!colors[col2]) col2 = 3
let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "topless" : ""}-${iconID}-${col1}-${col2}-${outline ? 1 : 0}`
let col3 = req.query.col3
if (col3 && (!hasExtra || !colors[col3] || col3 == "12")) col3 = null
let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "top" : ""}-${iconID}-${col1}-${col2}-${col3 || "x"}-${outline ? 1 : 0}`
if (!sizeParam && !glowOffset.length && cache[iconCode]) {
clearTimeout(cache[iconCode].timeoutID);
cache[iconCode].timeoutID = setTimeout(function() {delete cache[iconCode]}, 600000);
cache[iconCode].timeoutID = setTimeout(function() {delete cache[iconCode]}, 1800000);
return res.end(cache[iconCode].value);
}
@ -89,6 +95,7 @@ module.exports = async (app, req, res) => {
let robotOffset1, robotOffset2, robotOffset3, robotOffset1b, robotOffset2b, robotOffset3b;
let robotGlow1, robotGlow2, robotGlow3
let ufoTop, ufoOffset, ufoCoords, ufoSprite
let extrabit, offset2, size2;
if (isSpecial) {
const legs = [1,2,3].map(function(val) {return genImageName(`0${val+1}`)});
@ -108,23 +115,22 @@ module.exports = async (app, req, res) => {
if (!glowOffset.length) glowOffset = offsets[form][+iconID] || []
}
res.contentType('image/png');
let extrabit, offset2, size2;
if (fs.existsSync(fromIcons(extra))) {
extrabit = icons[extra]
offset2 = extrabit.spriteOffset.map(minusOrigOffset);
size2 = extrabit.spriteSize;
extra = new Jimp(fromIcons(extra));
useExtra = true
}
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 (col3) await Jimp.read(eb).then(e => { extra = recolor(e, col3) })
useExtra = true
}
Jimp.read(fromIcons(icon)).then(async function (ic) {
let iconSize = [ic.bitmap.width, ic.bitmap.height]
@ -279,18 +285,19 @@ module.exports = async (app, req, res) => {
if (form == "swing") img.resize(120, 111)
if (img.bitmap.height == 300) ic.autocrop(1, false)
if (sizeParam) {
let imgSize = Math.round(req.query.size)
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 (img.bitmap.width > img.bitmap.height) img.contain(img.bitmap.width, img.bitmap.width, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
else if (img.bitmap.width < img.bitmap.height) img.contain(img.bitmap.height, img.bitmap.height, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
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 && !glowOffset.length) {
cache[iconCode] = {
value: buffer,
timeoutID: setTimeout(function() {delete cache[iconCode]}, 600000)
timeoutID: setTimeout(function() {delete cache[iconCode]}, 1800000)
}
}
return res.end(buffer, 'base64')
@ -351,6 +358,7 @@ module.exports = async (app, req, res) => {
let result = []
if (app.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon()
res.contentType('image/png');
request.post(app.endpoint + 'getGJUsers20.php', {
form: {

View file

@ -2,6 +2,8 @@ const request = require('request')
const fs = require('fs')
const Level = require('../classes/Level.js')
function xor(str, key) { return Buffer.from(String.fromCodePoint(...str.split('').map((char, i) => char.charCodeAt(0) ^ key.charCodeAt(i % key.length)))).toString('base64') }
module.exports = async (app, req, res, api, analyze) => {
if (app.offline) {
@ -28,7 +30,7 @@ module.exports = async (app, req, res, api, analyze) => {
}
}, async function (err, resp, body) {
if (err || !body || body == '-1') {
if (err || !body || body == '-1' || body.startsWith("<!")) {
if (!api) return res.redirect('search/' + req.params.id)
else return res.send("-1")
}
@ -41,7 +43,6 @@ module.exports = async (app, req, res, api, analyze) => {
let levelInfo = app.parseResponse(preRes[0])
let level = new Level(levelInfo, author)
if (song[2]) {
level.songName = song[2] || "Unknown"
level.songAuthor = song[4] || "Unknown"

View file

@ -9,7 +9,7 @@ module.exports = async (app, req, res, api) => {
if (!req.body.accountID) return res.status(400).send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password provided!")
let subject = new Buffer(req.body.subject ? (req.body.color ? "☆" : "") + (req.body.subject.slice(0, 50)) : (req.body.color ? "☆" : "") + "No subject").toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
let subject = Buffer.from(req.body.subject ? (req.body.color ? "☆" : "") + (req.body.subject.slice(0, 50)) : (req.body.color ? "☆" : "") + "No subject").toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
let body = xor.encrypt(req.body.message.slice(0, 300), 14251)
let params = {

View file

@ -31,7 +31,7 @@ module.exports = async (app, req, res) => {
percent: 0
}
params.comment = new Buffer(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
params.gjp = xor.encrypt(req.body.password, 37526)
params.levelID = req.body.levelID.toString()
params.accountID = req.body.accountID.toString()

View file

@ -20,7 +20,7 @@ module.exports = async (app, req, res) => {
cType: '1'
}
params.comment = new Buffer(req.body.comment.slice(0, 190) + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
params.comment = Buffer.from(req.body.comment.slice(0, 190) + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
params.gjp = xor.encrypt(req.body.password, 37526)
params.accountID = req.body.accountID.toString()
params.userName = req.body.username

View file

@ -8,11 +8,11 @@ module.exports = async (app, req, res, api, getLevels) => {
request.post(app.endpoint + 'getGJUsers20.php', {
form: {
str: getLevels || req.params.id,
secret: app.secret
secret: app.secret,
}
}, function (err1, res1, b1) {
let searchResult = (err1 || b1 == '-1' || !b1) ? req.params.id : app.parseResponse(b1)[16]
let searchResult = (req.query.hasOwnProperty("account") || err1 || b1 == '-1' || b1.startsWith("<!") || !b1) ? req.params.id : app.parseResponse(b1)[16]
request.post(app.endpoint + 'getGJUserInfo20.php', {
form: {

View file

@ -74,7 +74,7 @@ module.exports = async (app, req, res) => {
request.post(app.endpoint + 'getGJLevels21.php', {
form : filters}, async function(err, resp, body) {
if (err || !body || body == '-1') return res.send("-1")
if (err || !body || body == '-1' || body.startsWith("<!")) return res.send("-1")
let splitBody = body.split('#')
let preRes = splitBody[0].split('|', 10)
let authorList = {}

View file

@ -18,26 +18,45 @@ body {
}
p {
font-family: "Roboto";
font-family: aller, helvetica, arial;
font-size: 18px;
}
h2 {
font-family: "Roboto";
h1 {
font-weight: normal;
font-size: 20px;
margin: 0 0;
font-size: 60px;
font-family: Pusab;
color: white;
letter-spacing: 0.02em;
overflow: hidden;
white-space: nowrap;
text-shadow: -2.2px -2.2px 0px #000, 2.2px -2.2px 0px #000, -2.2px 2.2px 0px #000, 2.2px 2.2px 0px #000, 5px 5px 0px rgba(0,0,0,0.4);
}
h3 {
font-family: "Roboto";
font-weight: bold;
font-size: 20px;
h2 {
font-weight: normal;
margin: 0 0;
font-size: 35px;
font-family: Pusab;
width: fit-content;
color: white;
letter-spacing: 0.02em;
text-shadow: -1.5px -1.5px 0px #000, 1.5px -1.5px 0px #000, -1.5px 1.5px 0px #000, 1.5px 1.5px 0px #000;
}
.hidden {
display: none;
}
.inline, .help {
display: inline-block;
}
.help {
cursor: help;
}
.small {
font-size: 16px
}
@ -62,12 +81,6 @@ h3 {
border: none;
}
input[type=checkbox] {
margin-left: 7px;
transform: scale(1.2) translateY(1px);
cursor: pointer;
}
input:focus, select:focus, textarea:focus, button:focus {
outline: none;
}
@ -79,6 +92,12 @@ input:focus, select:focus, textarea:focus, button:focus {
border-style: 1px inset black;
}
input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, input:-webkit-autofill:active {
box-shadow: 0 0 0px 1000px #764F1A inset !important;
-webkit-box-shadow: 0 0 0px 1000px #764F1A inset !important;
-webkit-text-fill-color: white !important;
}
textarea {
border-radius: 5px;
padding: 7 7 7 7;
@ -115,6 +134,13 @@ input:focus, select:focus, textarea:focus, button:focus {
text-align: center;
}
.supercenter {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.iconimage {
height: 60px;
}
@ -262,12 +288,16 @@ input:focus, select:focus, textarea:focus, button:focus {
border-image: url('https://gdcolon.com/assets/gj_select.png') 10 stretch !important;
}
.iconTabButton, .glowToggle {
.iconTabButton, .glowToggle, .copyForm {
margin: 0 5 0 5;
transition: transform .1s ease-in-out;
}
.iconTabButton:active, .glowToggle:active {
.copyForm {
margin: 18 10 0 10 !important;
}
.iconTabButton:active, .glowToggle:active, .copyForm:active {
transform:scale(1.05)
}
@ -297,7 +327,7 @@ input:focus, select:focus, textarea:focus, button:focus {
margin-top: -5;
}
#col1, #col2 {
#colors div {
display: flex;
}
@ -316,7 +346,11 @@ input:focus, select:focus, textarea:focus, button:focus {
}
.menuButton:hover {
transform:scale(1.1)
transform:scale(1.07)
}
.menuButton:active {
transform:scale(1.15)
}
#iconprogressbar {
@ -335,6 +369,78 @@ input:focus, select:focus, textarea:focus, button:focus {
background: transparent;
}
.popup {
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0; left: 0; right: 0; bottom: 0;
background-color: rgba(0,0,0,0.4);
z-index: 2;
}
.brownBox {
border: 17px solid transparent;
border-radius: 25px;
background-color: #995533;
border-image: url('./../assets/brownbox.png') 10% round;
}
.xButton {
position: absolute;
top: -35px;
left: -40px;
text-align: left;
}
input[type=text] {
font-weight: normal;
margin: 0 0;
font-size: 45px;
font-family: Pusab;
color: white;
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;
border: 0 solid transparent;
border-radius: 15px;
background-color: rgba(0, 0, 0, 0.3);
white-space: nowrap;
}
input[type=text]::placeholder {
color: #6F98D8;
font-size: 40px;
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=checkbox] {
display: none;
}
.gdcheckbox {
vertical-align: middle;
display: inline-block;
background-image: url(../assets/check-off.png);
background-repeat: no-repeat;
background-size: contain;
padding-right: 40px;
height: 42px;
text-align: left;
margin: 0 15 0 35;
}
input[type=checkbox]:checked + label.gdcheckbox {
background-image: url(../assets/check-on.png);
}
.bounce {
animation: boxAnimator 0.25s;
}
.gold {
color: rgb(255, 200, 0);
}
.blue {
color: blue;
}
@ -344,7 +450,7 @@ input:focus, select:focus, textarea:focus, button:focus {
}
.inline {
display: inline;
display: inline-block;
}
.spin {
@ -354,6 +460,21 @@ input:focus, select:focus, textarea:focus, button:focus {
mix-blend-mode: luminosity;
}
@keyframes boxAnimator {
0% {
transform: scale(0) translate(-50%, -50%);
transform-origin:left top
}
75% {
transform: scale(1.075) translate(-50%, -50%);
transform-origin:left top
}
100% {
transform: scale(1) translate(-50%, -50%);
transform-origin:left top
}
}
@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

BIN
assets/empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -1,62 +1,5 @@
//////////////////////////////////////////////////////////////////////////////////////////
// //
// ******** ********** ******* ** ******** **** ** //
// **////// /////**/// **/////** /** /**///// /**/** /** //
// /** /** ** //**/** /** /**//** /** //
// /********* /** /** /**/** /******* /** //** /** //
// ////////** /** /** /**/** /**//// /** //**/** //
// /** /** //** ** /** /** /** //**** //
// ******** /** //******* /********/********/** //*** //
// //////// // /////// //////// //////// // /// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//Stolen from https://github.com/fakemancat/geometry-dash-api/blob/master/classes/XOR.js because I am stupid.
module.exports = class XOR {
// Base64 functions
b64from(text) {
return Buffer.from(text, 'base64').toString('utf-8');
}
b64to(text) {
return Buffer.from(text).toString('base64');
}
// Xor functions
chr(ascii) {
return String.fromCodePoint(ascii);
}
text2ascii(input) {
return String(input).split('').map(letter => letter.charCodeAt());
}
cipher(data, key) {
key = this.text2ascii(key);
data = this.text2ascii(data);
let keysize = key.length;
let input_size = data.length;
let cipher = '';
for (let i = 0; i < input_size; i++) {
cipher += this.chr(data[i] ^ key[i % keysize]);
}
return cipher;
}
encrypt(password, key = 37526) {
let encode = this.cipher(password, key);
encode = Buffer.from(encode).toString('base64');
encode = encode
.replace(/\//g, '_')
.replace(/\+/g, '-');
return encode;
}
decrypt(gjp, key = 37526) {
let decode = gjp
.replace(/_/g, '/')
.replace(/-/g, '+');
decode = Buffer.from(decode, 'base64').toString();
decode = this.cipher(decode, key);
return decode;
}
};
xor(str, key) { return String.fromCodePoint(...str.split('').map((char, i) => char.charCodeAt(0) ^ key.toString().charCodeAt(i % key.toString().length))) }
encrypt(str, key = 37526) { return Buffer.from(this.xor(str, key)).toString('base64').replace(/\//g, '_').replace(/\+/g, '-'); }
decrypt(str, key = 37526) { return this.xor(Buffer.from(str.replace(/\//g, '_').replace(/\+/g, '-'), 'base64').toString(), key) }
}

View file

@ -185,9 +185,9 @@
<p>Unlike the Geometry Dash API, both username and ID can be used to fetch a user profile.</p>
<br>
<p class="reveal" onclick="$('#params-profile').slideToggle(100)"><b>Parameters (0)</b></p>
<p class="reveal" onclick="$('#params-profile').slideToggle(100)"><b>Parameters (1)</b></p>
<div class="subdiv" id="params-profile">
<p>No parameters for this one!</p>
<p>account: Forces the account ID to be used for fetching (normally Player ID is tried first)</p>
</div>
<br>
@ -714,16 +714,17 @@
<p style="font-size:15px; margin-top:-7px">This one isn't really part of the API, but dammit, my website my rules</p>
<br>
<p class="reveal" onclick="$('#params-icons').slideToggle(100)"><b>Parameters (7)</b></p>
<p class="reveal" onclick="$('#params-icons').slideToggle(100)"><b>Parameters (9)</b></p>
<div class="subdiv" id="params-icons">
<p><b>Parameters can be used to modify parts of a fetched user's icon</b></p>
<p>IDs generally correspond to their order of appearance in GD</p>
<p>form: The form of the icon (cube/ship/ball/ufo/wave/robot/spider/cursed)</p>
<p>form: The form of the icon (cube/ship/ball/ufo/wave/robot/spider/swing/cursed)</p>
<p>icon: The ID of the icon to use</p>
<p>col1: The ID of the primary color to use</p>
<p>col2: The ID of the secondary color to use</p>
<p>col3: Optional color ID to overwrite the 'white' layer used by some detailed icons</p>
<p>glow: If the icon should have a glow/outline (0 = off, anything else = on)</p>
<p>size: The size (in pixels) the icon should be, in case you don't want the default. Will always be square</p>
<p>size: The size in pixels that the icon should be (always square), in case you don't want the default. "Auto" also works.</p>
<p>topless: Removes the glass 'dome' from generated UFOs (legacy)</p>
<p>noUser: Disables fetching the icon from the GD servers. Slightly faster, but comes at the cost of having to build icons from the ground up using the parameters listed above. It completely ignores the entered username and always returns the default icon</p>
</div>

View file

@ -13,14 +13,41 @@
</head>
<body class="iconscroll" style="background-image: linear-gradient(rgb(139, 139, 139), rgb(100, 100, 100));" onbeforeunload="saveUrl()">
<div class="center hidden"><br>
<div class="popup" id="steal">
<div class="brownbox bounce center supercenter" style="height: 350px; width: 700px">
<h1 class="center gold" style="margin-top: 10px">Copy Icon</h1>
<input type="text" name="gdbrowser" id="playerName" autocomplete="off" placeholder="Username" maxlength="32" style="height: 58px; width: 90%; text-align: center; margin-top: 25px; margin-bottom: 5px;">
<div id="copyForms"></div>
<img src="../assets/ok.png" height=55px; class="postButton gdButton center" style="margin-top: 30px" id="fetchUser">
<img class="gdButton xButton" src="../assets/close.png" width="70px" onclick="$('#steal').hide()">
</div>
</div>
<div class="popup" id="settings">
<div class="brownbox bounce center supercenter" style="height: 350px; width: 720px">
<h1 class="center gold" style="margin-top: 10px; margin-bottom: 20px;">Settings</h1>
<div class="inline"><h2 class="help" help="Removes the clear dome on top of UFOs"><input type="checkbox" class="iconsetting" id="box-ufo"><label for="box-ufo" class="gdcheckbox gdButton"></label>No UFO Dome</h2></div>
<div class="inline"><h2 class="help" help="Forces a square aspect ratio on generated icons"><input type="checkbox" class="iconsetting" id="box-square"><label for="box-square" class="gdcheckbox gdButton"></label>Square Image</h2></div>
<br>
<div class="inline" style="margin-top: 12px"><h2 class="help" help="Adds a third color option for normally non-colorable details"><input type="checkbox" class="iconsetting" id="box-col3"><label for="box-col3" class="gdcheckbox gdButton"></label>Color 3</h2></div>
<br>
<p class="white" id="helpText" style="font-size: 24px; margin-bottom: 0;">(Hover over a setting for information)</p>
<img src="../assets/ok.png" height=55px; class="postButton gdButton center" style="margin-top: 30px" onclick="$('#settings').hide()">
<img class="gdButton xButton" src="../assets/close.png" width="70px" onclick="$('#settings').hide()">
</div>
</div>
<img id="iconkitlogo" src="../assets/iconkit.png" height=50px; style="margin: 7px 0;"><br><br>
<img id="loading" src="../assets/loading.png" class="spin" height=95px; style="margin: auto auto 25px auto">
<img id="result" src="../icon/icon" download="cube_1.png">
<hr id="gdfloor">
<div id="menuButtons" style="height: 65px; margin: 0 0 15 0;">
<button class="blankButton menuButton" id="downloadIcon" title="Download icon"><a id="downloadLink" download="cube_1.png" href="../icon/icon"><img src="../iconkitbuttons/download.png" width=50px></a></button>
<button class="blankButton menuButton" id="generateIcon" title="Generate icon"><img src="../iconkitbuttons/play.png" width=65px></button>
<button class="blankButton menuButton" id="fetchUser" title="Get player icon"><img src="../iconkitbuttons/player.png" width=50px></button>
<button class="blankButton menuButton" id="customColors" title="Settings" onclick="$('#settings').show()"><img src="../iconkitbuttons/cog.png" width=55px></button>
<button class="blankButton menuButton" id="downloadIcon" title="Download icon"><a id="downloadLink" download="cube_1.png" href="../icon/icon"><img src="../iconkitbuttons/download.png" width=55px></a></button>
<button class="blankButton menuButton" id="generateIcon" title="Generate icon"><img src="../iconkitbuttons/play.png" width=70px></button>
<button class="blankButton menuButton" id="getUserIcon" title="Get player icon"><img src="../iconkitbuttons/player.png" width=55px></button>
<button class="blankButton menuButton" id="randomIcon" title="Random Icon"><img src="../iconkitbuttons/shuffle.png" width=55px></button>
</div>
<div id="iconTabs"></div><br>
<div id="iconKitParent" class="iconKit">
@ -31,9 +58,9 @@
<div id="colors" class="iconKit">
<div id="col1"></div>
<div id="col2"></div>
<div style="margin-top: 12px" id="col3"></div>
</div><br>
<p style="color: rgb(20, 20, 20); margin-top: 3;">All sprites/assets belong to <a target=_blank href="http://robtopgames.com">RobTop Games</a>.</p>
<p><a target=_blank href="https://gdbrowser.com/api#icons">API Reference</a></p>
<p style="color: rgb(20, 20, 20); margin-top: 3px">Created by <a target=_blank href="https://gdcolon.com">GD Colon</a> • All sprites/assets belong to <a target=_blank href="http://robtopgames.com">RobTop Games</a><a target=_blank href="https://gdbrowser.com/api#icons">API Reference</a></p>
<div style="position:absolute; top: 2%; left: -1.95%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton" style="pointer-events: all" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
@ -52,22 +79,36 @@ $('.hidden').show();
let mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
let forms = ['cube', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider', 'swing']
let currentForm = 'cube'
let formCopy = 'cube'
let beenThereDoneThat = false
let selectedIcon = 1
let selectedForm = 'cube'
let selectedCol1 = 0
let selectedCol2 = 3
let selectedCol3 = null
let enableGlow = 0
let imagesLoaded = 0
let totalLoaded = 0
function capitalize(str) {return str[0].toUpperCase() + str.substr(1)}
if (mobile) $('#logo').attr('width', '80%');
forms.forEach(form => {$("#iconTabs").append(`<button form="${form}" class="blankButton iconTabButton"><img src="../iconkitbuttons/${form}_off.png" width=50px></button>`)})
let iconSettings = localStorage.iconkit.split(",")
iconSettings.forEach(x => {
$(`#box-${x}`).prop('checked', true)
})
function capitalize(str) { return str[0].toUpperCase() + str.substr(1) }
function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1) ) + min }
function row3() { if (iconSettings.includes("col3")) $('#col3').show(); else $('#col3').hide() }
row3()
forms.forEach(form => {
$("#iconTabs").append(`<button form="${form}" class="blankButton iconTabButton"><img src="../iconkitbuttons/${form}_off.png" width=50px></button>`)
$("#copyForms").append(`<button form="${form}" class="blankButton copyForm"><img src="../iconkitbuttons/${form}_off.png" width=50px></button>`)
})
$("#iconTabs").append(`<button title="Glow" class="blankButton glowToggle" id="glowbtn"><img id="glow" src="../iconkitbuttons/streak_off.png" width=50px></button>`)
forms.forEach(form => {$("#iconKitParent").append(`<div id="${form}s"></div>`)})
@ -101,15 +142,19 @@ fetch('./api/icons').then(res => {
$('[num="1"][form="cube"]').addClass('iconSelected');
})
filterIcon('color').forEach(function (i, p) {
$('#col1').append(`<button col=${p} class="blankButton color1" title="Color ${p}" id="col1-${p}"><img src="../gdicon/color_${p}.png" width=50px></button>`)
$('#col2').append(`<button col=${p} class="blankButton color2" title="Color ${p}" id="col2-${p}"><img src="../gdicon/color_${p}.png" width=50px></button>`)})
filterIcon('color').forEach(function (n, p) {
for (i=1; i<=3; i++) {
$(`#col${i}`).append(`<button col=${p} class="blankButton color${i}" title="Color ${p}" id="col${i}-${p}"><img src="../gdicon/color_${p}.png" width=50px></button>`)
}
})
$('#col1').append("<span style='min-width: 10px'></span>")
$('#col2').append("<span style='min-width: 10px'></span>")
$('#col3').append("<span style='min-width: 10px'></span>")
$('.color1[col="0"]').addClass('iconSelected');
$('.color2[col="3"]').addClass('iconSelected');
$('.color3[col="12"]').addClass('iconSelected');
$(document).on('click', '.iconTabButton', function () {
@ -135,6 +180,28 @@ fetch('./api/icons').then(res => {
})
$('#iconTabs').find('.iconTabButton').first().children().first().attr('src', $('.iconTabButton').first().children().first().attr('src').replace('_off', '_on'))
$("#randomIcon").click(function() {
selectedForm = forms[Math.floor(Math.random() * forms.length)]
selectedIcon = randInt(0, iconArray.filter(x => x.startsWith(selectedForm)).length)
selectedCol1 = randInt(0, iconArray.filter(x => x.startsWith("color")).length) - 1
selectedCol2 = randInt(0, iconArray.filter(x => x.startsWith("color")).length) - 1
selectedCol3 = null
enableGlow = randInt(0, 2) == 1 ? 1 : 0 // 1 in 3 chance of glow
$('#glow').attr('src', '../iconkitbuttons/streak_off.png')
$(`.iconTabButton[form=${selectedForm}]`).trigger('click')
$(`#${selectedForm}-${selectedIcon}`).trigger('click')
$(`#col1-${selectedCol1}`).trigger('click')
$(`#col2-${selectedCol2}`).trigger('click')
$(`#col3-15`).trigger('click')
if (enableGlow == 1) $("#glow").attr('src', $("#glow").attr('src').replace('_off', '_on'))
else $("#glow").attr('src', $("#glow").attr('src').replace('_on', '_off'))
$("#generateIcon").trigger('click')
})
})
$(document).on('click', '.glowToggle', function () {
@ -151,6 +218,13 @@ fetch('./api/icons').then(res => {
})
$(document).on('click', '.copyForm', function () {
$('.copyForm').each(function(x, y) {$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))})
formCopy = $(this).attr('form')
let src = $(this).children().first().attr('src')
$(this).children().first().attr('src', src.replace('_off', '_on'))
})
$(document).on('click', '.iconButton', function () {
$(".iconButton").removeClass("iconSelected");
$(this).addClass('iconSelected');
@ -171,11 +245,21 @@ fetch('./api/icons').then(res => {
selectedCol2 = $(this).attr('col');
})
$(document).on('click', '.color3', function () {
$(".color3").removeClass("iconSelected");
$(this).addClass('iconSelected');
selectedCol3 = $(this).attr('col');
if (selectedCol3 == 12) selectedCol3 = null
})
$("#generateIcon").click(function () {
$("#loading").show()
$("#result").hide()
$("#result").attr('src', `../icon/icon?icon=${selectedIcon}&form=${selectedForm}&col1=${selectedCol1}&col2=${selectedCol2}&glow=${enableGlow}`).attr('download', `${selectedForm}_${selectedIcon}.png`)
$("#downloadLink").attr('href', `../icon/icon?icon=${selectedIcon}&form=${selectedForm}&col1=${selectedCol1}&col2=${selectedCol2}&glow=${enableGlow}`).attr('download', `${selectedForm}_${selectedIcon}.png`)
let noDome = selectedForm == "ufo" && iconSettings.includes("ufo")
let square = iconSettings.includes("square")
let finalURL = `../icon/icon?icon=${selectedIcon}&form=${selectedForm}${noDome ? "&topless=1" : ""}&col1=${selectedCol1}&col2=${selectedCol2}${iconSettings.includes("col3") && selectedCol3 ? `&col3=${selectedCol3}` : ""}${enableGlow == 1 ? "&glow=1" : ""}${square ? "&size=auto" : ""}`
$("#result").attr('src', finalURL).attr('download', `${selectedForm}_${selectedIcon}.png`)
$("#downloadLink").attr('href', finalURL).attr('download', `${selectedForm}_${selectedIcon}.png`)
})
$('#result').on('load', function() {
@ -185,25 +269,34 @@ fetch('./api/icons').then(res => {
else $("#gdfloor").css('margin-top', '0px')
})
$("#fetchUser").click(function () {
let user = prompt(`Enter a player's username to get their ${currentForm == "ufo" ? "UFO" : currentForm}!\n(Form will depend on selected tab)\n`)
if (!user) return;
$("#getUserIcon").click(function() {
$(`.copyForm[form=${currentForm}]`).trigger('click')
$('#steal').show();
$('#playerName').focus()
})
let iconURL = `../icon/${user}?form=${currentForm}`
$("#fetchUser").click(function () {
let user = $("#playerName").val()
if (!user || !user.length) return $("#steal").hide()
let iconURL = `../icon/${user}?form=${formCopy}`
$(`.iconTabButton[form=${formCopy}]`).trigger('click')
$("#steal").hide()
$("#loading").show()
$("#result").hide()
$("#result").attr('src', iconURL).attr('download', `${user}_${currentForm}.png`)
$("#downloadLink").attr('href', iconURL).attr('download', `${user}_${currentForm}.png`)
$("#result").attr('src', iconURL).attr('download', `${user}_${formCopy}.png`)
$("#downloadLink").attr('href', iconURL).attr('download', `${user}_${formCopy}.png`)
$('#glow').attr('src', '../iconkitbuttons/streak_off.png')
enableGlow = 0
fetch('../api/profile/' + user).then(res => res.json())
.then(info => {
$(`#${currentForm}-${info.icon}`).trigger('click')
$(`#${formCopy}-${info[formCopy == "cube" ? "icon" : formCopy] || 1}`).trigger('click')
$(`#col1-${info.col1}`).trigger('click')
$(`#col2-${info.col2}`).trigger('click')
if (info.glow)$('#glowbtn').trigger('click')
$(`#col3-12`).trigger('click')
if (info.glow) $('#glowbtn').trigger('click')
})
})
@ -222,13 +315,43 @@ function backButton() {
window.location.href = "../../../../../"
}
let hoverText = $('#helpText').html()
$(".help").hover(function() {
$(this).css('color', 'rgba(200, 255, 255)')
$('#helpText').html($(this).attr('help'))
}, function() {
$(this).css('color', 'white')
$('#helpText').html(hoverText)
})
$(document).on('change', '.iconsetting', function (e) {
let checkedSettings = []
$('.iconsetting:checkbox:checked').each((i, x) => { checkedSettings.push(x.id.split('-')[1]) })
iconSettings = checkedSettings
localStorage.iconkit = checkedSettings.join(",")
row3()
})
$(document).keydown(function(k) {
if (k.keyCode == 27) { //esc
if (k.keyCode == 13) {
if ($("#steal").is(":visible")) $("#fetchUser").trigger('click')
else $("#generateIcon").trigger('click')
}
if (k.keyCode == 27) { //esc
if ($(".popup").is(":visible")) return $('.popup').hide()
k.preventDefault()
$('#backButton').trigger('click')
}
});
$(document).on('click', '.brownbox', function (e) {
e.stopPropagation();
})
$(document).on('click', '.popup', function () {
$('.popup').hide()
})
function saveUrl() {
sessionStorage.setItem('prevUrl', window.location.href);
}