Achievements Browser!!!
|
@ -20,9 +20,10 @@ module.exports = async (app, req, res) => {
|
|||
|
||||
request.post(app.endpoint + 'getGJLevelScores211.php', params, async function(err, resp, body) {
|
||||
|
||||
if (err || body == '-1' || !body) return res.send("-1")
|
||||
if (err || body == -1 || !body) return res.send("-1")
|
||||
scores = body.split('|').map(x => app.parseResponse(x))
|
||||
if (!(scores.filter(x => x[1]).length)) return res.send("-1")
|
||||
else app.trackSuccess()
|
||||
|
||||
scores.forEach(x => {
|
||||
let keys = Object.keys(x)
|
||||
|
|
|
@ -11,15 +11,12 @@ module.exports = async (app, req, res) => {
|
|||
else amount = count;
|
||||
}
|
||||
|
||||
let params = req.gdParams({
|
||||
count: amount,
|
||||
type: "top",
|
||||
})
|
||||
let params = {count: amount, type: "top"}
|
||||
|
||||
if (["creators", "creator", "cp"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) type = "creators"
|
||||
else if (["week", "weekly"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) type = "weekly" // i think GDPS'es use this
|
||||
if (["creators", "creator", "cp"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) params.type = "creators"
|
||||
else if (["week", "weekly"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) params.type = "weekly" // i think GDPS'es use this
|
||||
|
||||
request.post(app.endpoint + 'getGJScores20.php', params, async function(err, resp, body) {
|
||||
request.post(app.endpoint + 'getGJScores20.php', req.gdParams(params), async function(err, resp, body) {
|
||||
|
||||
if (err || body == '-1' || !body) return res.send("-1")
|
||||
scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
|
||||
|
|
|
@ -15,7 +15,8 @@ module.exports = async (app, req, res) => {
|
|||
|
||||
request.post(app.endpoint + 'getGJUserInfo20.php', params, async function (err, resp, body) {
|
||||
|
||||
if (err || body == '-1' || body == '-2' || !body) return res.status(400).send("Error counting messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. (this is not an issue with gdbrowser)")
|
||||
if (err || body == '-1' || body == '-2' || !body) return res.status(400).send(`Error counting messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else app.trackSuccess()
|
||||
let count = app.parseResponse(body)[38]
|
||||
if (!count) return res.status(400).send("Error fetching unread messages!")
|
||||
else res.status(200).send(count)
|
||||
|
|
|
@ -18,9 +18,9 @@ module.exports = async (app, req, res, api) => {
|
|||
|
||||
request.post(app.endpoint + 'deleteGJMessages20.php', req.gdParams(params), async function (err, resp, body) {
|
||||
|
||||
if (body != 1) return res.status(400).send("The Geometry Dash servers refused to delete the message! Make sure your username and password are entered correctly.")
|
||||
if (body != 1) return res.status(400).send(`The Geometry Dash servers refused to delete the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else res.status(200).send(`${deleted == 1 ? "1 message" : `${deleted} messages`} deleted!`)
|
||||
|
||||
app.trackSuccess()
|
||||
})
|
||||
|
||||
}
|
|
@ -15,7 +15,8 @@ module.exports = async (app, req, res, api) => {
|
|||
|
||||
request.post(app.endpoint + 'downloadGJMessage20.php', params, async function (err, resp, body) {
|
||||
|
||||
if (err || body == '-1' || !body) return res.status(400).send("Error fetching message!")
|
||||
if (err || body == '-1' || !body) return res.status(400).send(`Error fetching message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else app.trackSuccess()
|
||||
|
||||
let x = app.parseResponse(body)
|
||||
let msg = {}
|
||||
|
|
|
@ -17,7 +17,8 @@ module.exports = async (app, req, res, api) => {
|
|||
|
||||
request.post(app.endpoint + 'getGJMessages20.php', params, async function (err, resp, body) {
|
||||
|
||||
if (err || body == '-1' || body == '-2' || !body) return res.status(400).send("Error fetching messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. (this is not an issue with gdbrowser)")
|
||||
if (err || body == '-1' || body == '-2' || !body) return res.status(400).send(`Error fetching messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else app.trackSuccess()
|
||||
|
||||
let messages = body.split("|").map(msg => app.parseResponse(msg))
|
||||
let messageArray = []
|
||||
|
|
|
@ -20,10 +20,9 @@ module.exports = async (app, req, res, api) => {
|
|||
})
|
||||
|
||||
request.post(app.endpoint + 'uploadGJMessage20.php', params, async function (err, resp, body) {
|
||||
|
||||
if (body != 1) return res.status(400).send("The Geometry Dash servers refused to send the message! Make sure your username and password are entered correctly.")
|
||||
if (body != 1) return res.status(400).send(`The Geometry Dash servers refused to send the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else res.status(200).send('Message sent!')
|
||||
|
||||
app.trackSuccess()
|
||||
})
|
||||
|
||||
}
|
|
@ -34,7 +34,8 @@ module.exports = async (app, req, res) => {
|
|||
|
||||
request.post(app.endpoint + 'likeGJItem211.php', req.gdParams(params), function (err, resp, body) {
|
||||
if (err) return res.status(400).send("The Geometry Dash servers returned an error! Perhaps they're down for maintenance")
|
||||
if (!body || body == "-1") return res.status(400).send("The Geometry Dash servers rejected your vote! Make sure your username and password are entered correctly.")
|
||||
if (!body || body == "-1") return res.status(400).send(`The Geometry Dash servers rejected your vote! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else app.trackSuccess()
|
||||
res.status(200).send((params.like == 1 ? 'Successfully liked!' : 'Successfully disliked!') + " (this will only take effect if this is your first time doing so)")
|
||||
})
|
||||
}
|
|
@ -42,12 +42,14 @@ module.exports = async (app, req, res) => {
|
|||
|
||||
request.post(app.endpoint + 'uploadGJComment21.php', req.gdParams(params), function (err, resp, body) {
|
||||
if (err) return res.status(400).send("The Geometry Dash servers returned an error! Perhaps they're down for maintenance")
|
||||
if (!body || body == "-1") return res.status(400).send("The Geometry Dash servers rejected your comment! Try again later, or make sure your username and password are entered correctly.")
|
||||
if (!body || body == "-1") return res.status(400).send(`The Geometry Dash servers rejected your comment! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
if (body.startsWith("temp")) {
|
||||
let banStuff = body.split("_")
|
||||
return res.status(400).send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
|
||||
}
|
||||
|
||||
res.status(200).send(`Comment posted to level ${params.levelID} with ID ${body}`)
|
||||
app.trackSuccess()
|
||||
rateLimit[req.body.username] = Date.now();
|
||||
setTimeout(() => {delete rateLimit[req.body.username]; }, cooldown);
|
||||
})
|
||||
|
|
|
@ -27,7 +27,8 @@ module.exports = async (app, req, res) => {
|
|||
|
||||
request.post(app.endpoint + 'uploadGJAccComment20.php', req.gdParams(params), function (err, resp, body) {
|
||||
if (err) return res.status(400).send("The Geometry Dash servers returned an error! Perhaps they're down for maintenance")
|
||||
if (!body || body == "-1") return res.status(400).send("The Geometry Dash servers rejected your profile post! Try again later, or make sure your username and password are entered correctly.")
|
||||
else if (!body || body == "-1") return res.status(400).send(`The Geometry Dash servers rejected your profile post! Try again later, or make sure your username and password are entered correctly. Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince()} ago.`)
|
||||
else app.trackSuccess()
|
||||
res.status(200).send(`Comment posted to ${params.userName} with ID ${body}`)
|
||||
})
|
||||
}
|
BIN
assets/achievements.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/achievements/attempts.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/achievements/chamberOfTime.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/achievements/coins.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
assets/achievements/color1_off.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/achievements/color1_on.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/achievements/color2_off.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
assets/achievements/color2_on.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/achievements/creator.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/achievements/customLevels.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/achievements/demons.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
assets/achievements/diamonds.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/achievements/gd.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/achievements/gdm.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
assets/achievements/gdr.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/achievements/gds.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
assets/achievements/gdw.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
assets/achievements/hd/attempts.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/achievements/hd/chamberOfTime.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
assets/achievements/hd/coins.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/achievements/hd/creator.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
assets/achievements/hd/customLevels.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
assets/achievements/hd/demon.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/achievements/hd/diamonds.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
assets/achievements/hd/jumps.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/achievements/hd/levels.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
assets/achievements/hd/mappacks.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
assets/achievements/hd/raing.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/achievements/hd/robtop.png
Normal file
After Width: | Height: | Size: 9 KiB |
BIN
assets/achievements/hd/secrets.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/achievements/hd/shardBonus.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/achievements/hd/shardFire.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/achievements/hd/shardIce.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/achievements/hd/shardLava.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/achievements/hd/shardPoison.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
assets/achievements/hd/shardShadow.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
assets/achievements/hd/social.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/achievements/hd/stars.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
assets/achievements/hd/ultimate.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
assets/achievements/hd/usercoins.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/achievements/hd/vaultOfSecrets.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
assets/achievements/jumps.png
Normal file
After Width: | Height: | Size: 6 KiB |
BIN
assets/achievements/level.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
assets/achievements/mappacks.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
assets/achievements/misc_off.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/achievements/misc_on.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
assets/achievements/rating.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/achievements/robtop.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/achievements/secret.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
assets/achievements/shardBonus.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
assets/achievements/shardFire.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/achievements/shardIce.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/achievements/shardLava.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/achievements/shardPoison.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
assets/achievements/shardShadow.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/achievements/social.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/achievements/stars.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/achievements/ultimate.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/achievements/usercoins.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
assets/achievements/vaultOfSecrets.png
Normal file
After Width: | Height: | Size: 10 KiB |
|
@ -1101,6 +1101,59 @@ input::-webkit-inner-spin-button {
|
|||
outline-offset: -0.4vh;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex img {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.colorCircle {
|
||||
border: 3.75px solid black;
|
||||
border-radius: 420px;
|
||||
}
|
||||
|
||||
.typeFilter {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 0.75% 0.75%;
|
||||
border-radius: 0.5vh;
|
||||
}
|
||||
|
||||
.typeFilter.achDeselected, .gameFilter.achDeselected {
|
||||
filter: brightness(30%) saturate(50%);
|
||||
}
|
||||
|
||||
.rewardFilter.achDeselected {
|
||||
filter: brightness(60%);
|
||||
}
|
||||
|
||||
.typeFilter.achDeselected {
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.labelHover {
|
||||
color: yellow;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.achSelect {
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
user-select: none;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.achSelect:active {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
.hidey {
|
||||
opacity: 0%;
|
||||
pointer-events: none;
|
||||
transition-duration: 0s !important;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #ff0000 !important;
|
||||
}
|
||||
|
|
BIN
assets/iconkitbuttons/deathEffect_off.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/iconkitbuttons/deathEffect_on.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/iconkitbuttons/trail_off.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/iconkitbuttons/trail_on.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/trails/1.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/trails/2.png
Normal file
After Width: | Height: | Size: 7 KiB |
BIN
assets/trails/3.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/trails/4.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/trails/5.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
assets/trails/6.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/trails/7.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
192
html/achievements.html
Normal file
|
@ -0,0 +1,192 @@
|
|||
<head>
|
||||
<title>Achievements</title>
|
||||
<meta charset="utf-8">
|
||||
<link href="../assets/css/browser.css" type="text/css" rel="stylesheet">
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
|
||||
<link rel="icon" href="../assets/trophies/1.png">
|
||||
<meta id="meta-title" property="og:title" content="Achievements">
|
||||
<meta id="meta-desc" property="og:description" content="View the list of achievements in Geometry Dash!">
|
||||
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/trophies/1.png">
|
||||
</head>
|
||||
|
||||
<body class="levelBG" onbeforeunload="saveUrl()">
|
||||
|
||||
<div id="everything" style="overflow: auto;">
|
||||
|
||||
<div id="filters" class="popup">
|
||||
<div class="brownBox bounce center supercenter" style="width: 110vh; height: 75%; padding-top: 0.3%; padding-bottom: 3.5%">
|
||||
<img class="gdButton" src="../assets/close.png" width="10%" style="position: absolute; top: -7.5%; left: -5.5vh" onclick="$('#filters').hide()">
|
||||
<h2 class="small" style="margin-bottom: 0%">Filters</h2><br>
|
||||
|
||||
<div id="rewardLabel">
|
||||
<img filter="rewardFilter" class="achSelect selectAll" style="margin: 0% 2% 1.9% 0%" class="gdButton inline" src="../assets/select-all.png" height="5%">
|
||||
<h1 class="smaller inline" style="margin-bottom: 1.5%">Reward</h1>
|
||||
<img filter="rewardFilter" class="achSelect selectNone" style="margin: 0% 0% 1.9% 2%" class="gdButton inline" src="../assets/select-none.png" height="5%">
|
||||
</div>
|
||||
<div id="forms"></div>
|
||||
|
||||
<div id="typeLabel">
|
||||
<img filter="typeFilter" class="achSelect selectAll" style="margin: 0% 2% 1.4% 0%" class="gdButton inline" src="../assets/select-all.png" height="5%">
|
||||
<h1 class="smaller inline" style="margin-top: 3%; margin-bottom: 1%">Requirement</h1>
|
||||
<img filter="typeFilter" class="achSelect selectNone" style="margin: 0% 0% 1.4% 2%" class="gdButton inline" src="../assets/select-none.png" height="5%">
|
||||
</div>
|
||||
<div id="types"></div>
|
||||
|
||||
<div id="gameLabel">
|
||||
<img filter="gameFilter" class="achSelect selectAll" style="margin: 0% 2% 1.8% 0%" class="gdButton inline" src="../assets/select-all.png" height="5%">
|
||||
<h1 class="smaller inline" style="margin-top: 1%; margin-bottom: 1.3%">Game</h1>
|
||||
<img filter="gameFilter" class="achSelect selectNone" style="margin: 0% 0% 1.8% 2%" class="gdButton inline" src="../assets/select-none.png" height="5%">
|
||||
</div>
|
||||
<div id="games">
|
||||
<img class="gdButton achFilter gameFilter" src="../assets/achievements/gd.png" height="12%" style="margin: 0% 1%" title="Geometry Dash" filter="gd">
|
||||
<img class="gdButton achFilter gameFilter" src="../assets/achievements/gdm.png" height="12%" style="margin: 0% 1%" title="Geometry Dash Meltdown" filter="meltdown">
|
||||
<img class="gdButton achFilter gameFilter" src="../assets/achievements/gdw.png" height="12%" style="margin: 0% 1%" title="Geometry Dash World" filter="world">
|
||||
</div>
|
||||
<img id="submitFilters" class="gdButton" style="margin: 2.5% 0.7%" src="../assets/btn-submit.png" height="10%">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
|
||||
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
|
||||
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
|
||||
</div>
|
||||
|
||||
<div id="searchBox" class="supercenter dragscroll" style="width: 128vh">
|
||||
<div style="height: 4.5%"></div>
|
||||
</div>
|
||||
|
||||
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></div>
|
||||
|
||||
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
|
||||
<h1 class="pre" id="header">Achievements</h1>
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
|
||||
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; top: 2%; right: 1.5%; width: 9%; text-align: right">
|
||||
<img class="gdButton" style="margin-bottom: 15%" src="../assets/plus.png" width="60%" onclick="$('#filters').show()">
|
||||
<img id="check" class="gdButton" style="margin-bottom: 15%" src="../assets/check-off.png" width="60%"">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
<script async type="text/javascript" src="../sizecheck.js"></script>
|
||||
<script type="text/javascript" src="../dragscroll.js"></script>
|
||||
<script>
|
||||
|
||||
let disabledFilters = {reward: [], type: [], game: []}
|
||||
let forms = ["cube", "ship", "ball", "ufo", "wave", "robot", "spider"]
|
||||
let gameColors = {meltdown: "rgb(255, 100, 0)", world: "rgb(0, 225, 255)"}
|
||||
let achievements = []
|
||||
let colors = {}
|
||||
let completed = false
|
||||
|
||||
let formStr = ["Icon", "Ship", "Ball", "UFO", "Wave", "Robot", "Spider", "Trail", "Death Effect", "Primary Color", "Secondary Color", "Misc"]
|
||||
forms.concat(["trail", "deathEffect", "color1", "color2", "misc"]).forEach((x, y) => {
|
||||
$('#forms').append(`<img class="gdButton achFilter rewardFilter" filter="${x}" title="${formStr[y]}" src="../assets/${y > 8 ? "achievements" : "iconkitbuttons"}/${x}_on.png" height=9.5%" style="margin: 0% 0.75%">`)
|
||||
})
|
||||
|
||||
function append(reset=true) {
|
||||
$('#searchBox').html(`<div style="height: 4.5%"></div>`)
|
||||
achievements.forEach(x => {
|
||||
|
||||
let iconImg = `"`;
|
||||
if (forms.includes(x.rewardType)) iconImg = `../assets/previewicons/${x.rewardType}-${x.rewardID < 10 ? "0" : ""}${x.rewardID}.png" width="90%" title="${x.rewardType}_${x.rewardID < 10 ? "0" : ""}${x.rewardID}"`
|
||||
else if (x.rewardType.startsWith("color")) {
|
||||
let col = colors[x.rewardID]
|
||||
let colType = x.rewardType.slice(5)
|
||||
iconImg = `../assets/col${colType}.png" class="colorCircle" width="80%" title="${colType == 1 ? "Primary" : "Secondary"} Color ${x.rewardID}" style="background-color: rgb(${col.r}, ${col.g}, ${col.b})"`
|
||||
}
|
||||
else if (x.rewardType == "deathEffect") iconImg = `../assets/deatheffects/${x.rewardID}.png" width="85%" title="Death Effect ${x.rewardID}"`
|
||||
else if (x.rewardType == "trail") iconImg = `../assets/trails/${x.rewardID}.png" width="85%" title="Trail ${x.rewardID}"`
|
||||
else if (x.rewardType == "misc") iconImg = `../assets/coin.png" width="85%"`
|
||||
|
||||
$('#searchBox').append(`<div class="flex searchresult leaderboardSlot" style="height: 18%; width: 92%; padding-left: 3%; padding-top: 0%">
|
||||
<div class="flex" style="width: 8%; margin-right: 2%"><img src="${iconImg}></div>
|
||||
<div>
|
||||
<h2 title="geometry.ach.${x.id}" smaller inline" style="font-size: 4.5vh; margin-top: 1vh; color: ${gameColors[x.game] || "default"}">${x.name}</h2>
|
||||
<p style="margin-top: 2vh; color:${completed ? "yellow" : "white"}">${completed ? x.achievedDescription : x.description}</p>
|
||||
</div>
|
||||
</div>`)
|
||||
})
|
||||
$('#searchBox').append('<div style="height: 4.5%"></div>')
|
||||
if (reset) $('#searchBox').scrollTop(0)
|
||||
}
|
||||
|
||||
fetch('./api/achievements').then(res => res.json().then(ach => {
|
||||
|
||||
Object.keys(ach.types).forEach(x => {
|
||||
$('#types').append(`<img class="gdButton achFilter typeFilter" filter="${ach.types[x][1].join(" ")}" src="../assets/achievements/${x}.png" title="${ach.types[x][0]}" height="8.5%" style="margin: 0.6% 0.4%">`)
|
||||
})
|
||||
|
||||
achievements = ach.achievements
|
||||
colors = ach.colors
|
||||
append()
|
||||
|
||||
function label(labelName) {
|
||||
let labelFilter = `.${labelName}Filter`
|
||||
let labelID = `#${labelName}Label h1`
|
||||
let labelButtons = `#${labelName}Label img`
|
||||
$(labelFilter).hover(function() {
|
||||
$(labelButtons).addClass('hidey')
|
||||
$(labelID).attr('text', $(labelID)
|
||||
.text()).text($(this).attr('title')).addClass("labelHover")
|
||||
}, function() {
|
||||
$(labelButtons).removeClass('hidey')
|
||||
$(labelID).text($(labelID).attr('text')).removeClass("labelHover")
|
||||
})
|
||||
|
||||
$(labelFilter).click(function() {
|
||||
let filters = $(this).attr('filter').split(" ")
|
||||
if (!$(this).hasClass('achDeselected')) {
|
||||
$(this).addClass('achDeselected')
|
||||
filters.forEach(x => disabledFilters[labelName].push(x))
|
||||
if (labelName == "reward") $(this).attr('src', $(this).attr('src').replace("_on", "_off"))
|
||||
}
|
||||
else {
|
||||
$(this).removeClass('achDeselected')
|
||||
filters.forEach(x => disabledFilters[labelName] = disabledFilters[labelName].filter(y => y != x))
|
||||
if (labelName == "reward") $(this).attr('src', $(this).attr('src').replace("_off", "_on"))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
label("reward")
|
||||
label("type")
|
||||
label("game")
|
||||
|
||||
$(document).on('click', '.selectNone', function() { $(`.${$(this).attr('filter')}:not(.achDeselected)`).trigger('click'); $('#selectNone').hide(); $('#selectAll').show() })
|
||||
$(document).on('click', '.selectAll', function() { $(`.${$(this).attr('filter')}.achDeselected`).trigger('click'); $('#selectNone').show(); $('#selectAll').hide() })
|
||||
|
||||
$('#submitFilters').click(function() {
|
||||
|
||||
$('.popup').hide()
|
||||
|
||||
if (!$('.rewardFilter:not(.achDeselected)').length) $('#rewardLabel .selectAll').trigger('click')
|
||||
if (!$('.typeFilter:not(.achDeselected)').length) $('#typeLabel .selectAll').trigger('click')
|
||||
if (!$('.gameFilter:not(.achDeselected)').length) $('#gameLabel .selectAll').trigger('click')
|
||||
|
||||
achievements = ach.achievements
|
||||
.filter(x => !disabledFilters.reward.includes(x.rewardType))
|
||||
.filter(x => !disabledFilters.type.some(y => x.id.startsWith(y)))
|
||||
.filter(x => !disabledFilters.game.includes(x.game))
|
||||
append()
|
||||
})
|
||||
|
||||
$('#check').click(function() {
|
||||
completed = !completed
|
||||
if (completed) $('#check').attr('src', '../assets/check-on.png')
|
||||
else $('#check').attr('src', '../assets/check-off.png')
|
||||
append(false)
|
||||
})
|
||||
}))
|
||||
|
||||
</script>
|
|
@ -28,6 +28,10 @@
|
|||
<img id="creditsButton" class="gdButton" src="../assets/credits.png" width="60%" onclick="loadCredits()">
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; bottom: 2.5%; right: 1.5%; text-align: right; width: 14%;">
|
||||
<a href="../achievements"><img class="gdButton" src="../assets/achievements.png" width="40%"></a>
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; top: -1.5%; right: 10%; text-align: right; width: 10%;">
|
||||
<a href="../iconkit"><img class="iconRope" src="../assets/iconrope.png" width="40%"></a>
|
||||
</div>
|
||||
|
|
|
@ -83,10 +83,8 @@
|
|||
<p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">
|
||||
Are you sure you want to <span style="color: #F05C5C">delete</span> this message?
|
||||
</p>
|
||||
<img src="../assets/btn-cancel.png" height=29%; class="gdButton center"
|
||||
onclick="$('#confirmDelete').hide()">
|
||||
<img src="../assets/btn-delete.png" height=29%; id="deleteCurrentMessage"
|
||||
class="gdButton center sideSpaceB">
|
||||
<img src="../assets/btn-cancel.png" height=29%; class="gdButton center" onclick="$('#confirmDelete').hide()">
|
||||
<img src="../assets/btn-delete.png" height=29%; id="deleteCurrentMessage" class="gdButton center sideSpaceB">
|
||||
</div>
|
||||
|
||||
<div id="deleting" style="display: none">
|
||||
|
@ -94,6 +92,13 @@
|
|||
<p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">Deleting message...</p>
|
||||
<img src="../assets/loading.png" class="spin noSelect" style="height: 35%; margin-top: -2%">
|
||||
</div>
|
||||
|
||||
<div id="delete-error" style="display: none;">
|
||||
<img class="gdButton" src="../assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('#delete-error').hide(); $('#confirmDelete').hide(); $('#preDelete').show()">
|
||||
<img src="../assets/exclamation.png" style="height: 40%">
|
||||
<p class="bigger" style="margin-bottom: 0%; margin-top: 2.5%">Something went wrong!</p>
|
||||
<p id="delError" style="font-size: 2.4vh; margin-top: 1%"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -117,6 +122,13 @@
|
|||
class="selectedAmount"></span>...</p>
|
||||
<img src="../assets/loading.png" class="spin noSelect" style="height: 35%; margin-top: -2%">
|
||||
</div>
|
||||
|
||||
<div id="bd-error" style="display: none;">
|
||||
<img class="gdButton" src="../assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('#bd-error').hide(); $('#bulkDelete').hide(); $('#preBulkDelete').show()">
|
||||
<img src="../assets/exclamation.png" style="height: 40%">
|
||||
<p class="bigger" style="margin-bottom: 0%; margin-top: 2.5%">Something went wrong!</p>
|
||||
<p id="bdError" style="font-size: 2.4vh; margin-top: 1%"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -135,8 +147,7 @@
|
|||
</div>
|
||||
<div id="messageOptions">
|
||||
<img class="gdButton" style="width: 8%" title="Reply" src="../assets/reply.png" id="replyButton">
|
||||
<img class="gdButton" style="width: 8%" title="Delete" src="../assets/trash.png"
|
||||
onclick="$('#confirmDelete').show()">
|
||||
<img class="gdButton" style="width: 8%" title="Delete" src="../assets/trash.png" id="deleteButton" onclick="$('#confirmDelete').show()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -280,7 +291,7 @@
|
|||
appendMessages()
|
||||
$('#messages').show()
|
||||
|
||||
let targetUser = window.location.search.match(/\?sendTo=(.+)/)
|
||||
let targetUser = window.location.search.match(/(\?|&)sendTo=(.+)/)
|
||||
if (targetUser) {
|
||||
targetUser = decodeURIComponent(targetUser[1])
|
||||
fetch(`../api/profile/${targetUser}`).then(res => res.json()).then(res => {
|
||||
|
@ -317,7 +328,7 @@
|
|||
.done(msgs => {
|
||||
messages = msgs
|
||||
appendMessages()
|
||||
})
|
||||
}).fail(e => { $('#msgList').html("") })
|
||||
}
|
||||
|
||||
$(document).on('click', '.hitbox', function (x) {
|
||||
|
@ -343,8 +354,12 @@
|
|||
authorID = $(this).attr('authorID')
|
||||
let subject = $(this).find('h3:first')
|
||||
subject.html(subject.html().replace('<span style="color:#00E600">!</span>', "")) //lazy way to mark as read
|
||||
|
||||
$('#theMfMessage').attr('style', '')
|
||||
$('#messageSubject').attr('style', `color: ${$(this).attr('browserColor') ? 'rgb(120, 200, 255)' : "white"}`).text(subject.text())
|
||||
$('#messageAuthor').html($(this).find('.gdButton').html())
|
||||
$('#replyButton').hide()
|
||||
$('#deleteButton').hide()
|
||||
$('#messageBody').text('').hide()
|
||||
$('#messageLoad').show()
|
||||
$('#selectedMessage').show()
|
||||
|
@ -356,6 +371,8 @@
|
|||
if (cache[messageID]) {
|
||||
$('#messageBody').attr('style', `color: ${cache[messageID][1] ? 'rgb(255, 140, 255)' : "white"}`).text(cache[messageID][0]).show()
|
||||
$('#messageLoad').hide()
|
||||
$('#replyButton').show()
|
||||
$('#deleteButton').show()
|
||||
}
|
||||
|
||||
else $.post("../messages/" + messageID, { password, accountID })
|
||||
|
@ -365,6 +382,8 @@
|
|||
function loadMsg() {
|
||||
$('#messageBody').attr('style', `color: ${msg.browserColor ? 'rgb(255, 140, 255)' : "white"}`).text(msg.content).show()
|
||||
$('#messageLoad').hide()
|
||||
$('#replyButton').show()
|
||||
$('#deleteButton').show()
|
||||
}
|
||||
|
||||
if (!messageStatus[msg.accountID]) fetch(`../api/profile/${msg.author}`).then(res => res.json()).then(res => {
|
||||
|
@ -375,6 +394,13 @@
|
|||
loadMsg()
|
||||
}
|
||||
})
|
||||
.fail(e => {
|
||||
$('#messageAuthor').html(' ')
|
||||
$('#messageSubject').html(' ')
|
||||
$('#messageLoad').hide()
|
||||
$('#messageBody').html(e.responseText).show()
|
||||
$('#theMfMessage').attr('style', 'background-color: rgba(0, 0, 0, 0)')
|
||||
})
|
||||
})
|
||||
|
||||
$('#deleteCurrentMessage').click(function () {
|
||||
|
@ -392,6 +418,11 @@
|
|||
$('#preDelete').show()
|
||||
$('#deleting').hide()
|
||||
})
|
||||
.fail(e => {
|
||||
$('#deleting').hide()
|
||||
$('#delete-error').show()
|
||||
$('#delError').html(e.responseText)
|
||||
})
|
||||
})
|
||||
|
||||
$('#purge').click(function () {
|
||||
|
@ -418,8 +449,13 @@
|
|||
}
|
||||
allowEsc = true
|
||||
$('#bulkDelete').hide()
|
||||
$('#preBulkDelete').show()
|
||||
$('#bulkDeleting').hide()
|
||||
$('#preBulkDelete').show()
|
||||
})
|
||||
.fail(e => {
|
||||
$('#bulkDeleting').hide()
|
||||
$('#bd-error').show()
|
||||
$('#bdError').html(e.responseText)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
const plist = require('plist');
|
||||
const { readFileSync, writeFileSync } = require('fs');
|
||||
const data = plist.parse(readFileSync('./GJ_GameSheet02-uhd.plist', 'utf8'));
|
||||
for (let key in data.frames) {
|
||||
let fileData = data.frames[key];
|
||||
for (let innerKey in fileData) {
|
||||
if (typeof fileData[innerKey]) {
|
||||
if (innerKey != "spriteSize" && innerKey != "spriteOffset") delete fileData[innerKey] // remove useless stuff
|
||||
else fileData[innerKey] = JSON.parse(fileData[innerKey].replace(/{/g, '[').replace(/}/g, ']'));
|
||||
}
|
||||
}
|
||||
}
|
||||
writeFileSync('./gameSheet.json', JSON.stringify(data.frames, null, 2).replace(/\[\n.+?(-?\d+),\n.+?(-?\d+)\n.+]/g, "[$1, $2]")); // regex to make it easier to read
|
20
index.js
|
@ -9,6 +9,7 @@ app.offline = false // set to true to go into "offline" mode (in case of ip ban
|
|||
app.config = require('./settings') // tweak settings in this file if you're using a GDPS
|
||||
app.endpoint = app.config.endpoint // default is boomlings.com/database/
|
||||
app.accountCache = {} // account IDs are cached here to shave off requests to getgjusers
|
||||
app.lastSuccess = null // timestamp of the last time a gjp request was accepted my the servers
|
||||
|
||||
let rlMessage = "Rate limited ¯\\_(ツ)_/¯<br><br>Please do not spam my servers with a crazy amount of requests. It slows things down on my end and stresses RobTop's servers just as much." +
|
||||
" If you really want to send a zillion requests for whatever reason, please download the GDBrowser repository locally - or even just send the request directly to the GD servers.<br><br>" +
|
||||
|
@ -33,6 +34,8 @@ const RL2 = rateLimit({
|
|||
let api = true;
|
||||
let gdIcons = fs.readdirSync('./assets/previewicons')
|
||||
let sampleIcons = require('./misc/sampleIcons.json')
|
||||
let achievements = require('./misc/achievements.json')
|
||||
let achievementTypes = require('./misc/achievementTypes.json')
|
||||
let colorList = require('./icons/colors.json')
|
||||
let forms = { "player": "cube", "bird": "ufo", "dart": "wave" }
|
||||
let assetPage = fs.readFileSync('./html/assets.html', 'utf8')
|
||||
|
@ -57,6 +60,19 @@ app.use(function(req, res, next) {
|
|||
let directories = [""]
|
||||
fs.readdirSync('./api').filter(x => !x.includes(".")).forEach(x => directories.push(x))
|
||||
|
||||
app.trackSuccess = function() {
|
||||
// made this a function in case i wanna do more stuff in the future
|
||||
app.lastSuccess = Date.now()
|
||||
}
|
||||
|
||||
app.timeSince = function(time=app.lastSuccess) {
|
||||
if (!time) return "[unknown]"
|
||||
let secsPassed = Math.floor((Date.now() - time) / 1000)
|
||||
let minsPassed = Math.floor(secsPassed / 60)
|
||||
secsPassed -= 60 * minsPassed;
|
||||
return `${minsPassed}m ${secsPassed}s`
|
||||
}
|
||||
|
||||
app.run = {}
|
||||
directories.forEach(d => {
|
||||
fs.readdirSync('./api/' + d).forEach(x => {if (x.includes('.')) app.run[x.split('.')[0]] = require('./api/' + d + "/" + x) })
|
||||
|
@ -103,7 +119,7 @@ app.get("/assets/:dir*?", function(req, res) {
|
|||
|
||||
if (dir.includes('.') || !req.path.endsWith("/")) {
|
||||
if (!req.params[0]) main = ""
|
||||
if (req.params.dir == "deatheffects") return res.sendFile(__dirname + "/assets/deatheffects/0.png")
|
||||
if (req.params.dir == "deatheffects" || req.params.dir == "trails") return res.sendFile(__dirname + "/assets/deatheffects/0.png")
|
||||
return res.send(`<p style="font-size: 20px; font-family: aller, helvetica, arial">Looks like this file doesn't exist ¯\\_(ツ)_/¯<br><a href='/assets/${main}'>View directory listing for <b>/assets/${main}</b></a></p>`)
|
||||
}
|
||||
|
||||
|
@ -140,6 +156,7 @@ app.get("/", function(req, res) {
|
|||
else res.sendFile(__dirname + "/html/home.html")
|
||||
})
|
||||
|
||||
app.get("/achievements", function(req, res) { res.sendFile(__dirname + "/html/achievements.html") })
|
||||
app.get("/analyze/:id", async function(req, res) { res.sendFile(__dirname + "/html/analyze.html") })
|
||||
app.get("/api", function(req, res) { res.sendFile(__dirname + "/html/api.html") })
|
||||
app.get("/boomlings", function(req, res) { res.sendFile(__dirname + "/html/boomlings.html") })
|
||||
|
@ -193,6 +210,7 @@ app.get("/:id", function(req, res) { app.run.level(app, req, res) })
|
|||
// MISC
|
||||
|
||||
app.get("/icon/:text", function(req, res) { app.run.icon(app, req, res) })
|
||||
app.get("/api/achievements", function(req, res) { res.send({achievements, types: achievementTypes, colors: colorList }) })
|
||||
app.get('/api/icons', function(req, res) {
|
||||
let sample = [JSON.stringify(sampleIcons[Math.floor(Math.random() * sampleIcons.length)].slice(1))]
|
||||
res.send({icons: gdIcons, colors: colorList, colorOrder, whiteIcons, sample});
|
||||
|
|
26
misc/achievementTypes.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"stars": ["Stars", ["stars"]],
|
||||
"coins": ["Secret Coins", ["coins"]],
|
||||
"usercoins": ["User Coins", ["usercoins"]],
|
||||
"diamonds": ["Diamonds", ["diamonds"]],
|
||||
"demons": ["Demons", ["demon"]],
|
||||
"shardFire": ["Fire Shards", ["shardFire"]],
|
||||
"shardIce": ["Ice Shards", ["shardIce"]],
|
||||
"shardLava": ["Lava Shards", ["shardLava"]],
|
||||
"shardPoison": ["Poison Shards", ["shardPoison"]],
|
||||
"shardShadow": ["Shadow Shards", ["shardShadow"]],
|
||||
"shardBonus": ["Bonus Shards", ["shardBonus"]],
|
||||
"level": ["Official Levels", ["level", "mdlevel", "world.level", "steam"]],
|
||||
"customLevels": ["Online Levels", ["custom"]],
|
||||
"creator": ["Creator", ["creator", "submit"]],
|
||||
"ultimate": ["3 Coins", ["demoncoin", "mdcoin"]],
|
||||
"mappacks": ["Map Packs", ["mappacks"]],
|
||||
"attempts": ["Attempts", ["attempt", "special"]],
|
||||
"jumps": ["Jumping", ["jump"]],
|
||||
"rating": ["Rating", ["like", "rateDiff"]],
|
||||
"social": ["Social", ["followCreator", "friends"]],
|
||||
"robtop": ["Support", ["rate", "mdrate", "twitter", "facebook", "youtube", "moreGames", "lite"]],
|
||||
"secret": ["Secrets + Vault", ["secret"]],
|
||||
"vaultOfSecrets": ["Vault of Secrets", ["v2.secret"]],
|
||||
"chamberOfTime": ["Chamber of Time", ["v3.secret"]]
|
||||
}
|
2576
misc/achievements.json
Normal file
68
misc/plistToJSON.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
let path = "C:/Program Files (x86)/Steam/steamapps/common/Geometry Dash/Resources/"
|
||||
|
||||
let files = ["AchievementsDesc", "AchievementsDescMD"]
|
||||
let values = ["achievedDescription", "title", "icon", "unachievedDescription"]
|
||||
let innerKey = null
|
||||
let jsonValues = false
|
||||
let exportName = "achievements"
|
||||
|
||||
// let files = ["GJ_GameSheet02-uhd"]
|
||||
// let values = ["spriteSize", "spriteOffset"]
|
||||
// let innerKey = "frames"
|
||||
// let jsonValues = true
|
||||
|
||||
const plist = require('plist');
|
||||
const fs = require('fs');
|
||||
|
||||
let final = {}
|
||||
|
||||
files.forEach(file => {
|
||||
let data = plist.parse(fs.readFileSync(path + file + '.plist', 'utf8'));
|
||||
if (innerKey) data = data[innerKey]
|
||||
|
||||
console.log(`Converting ${file}.plist...`)
|
||||
|
||||
for (let key in data) {
|
||||
let fileData = data[key];
|
||||
for (let innerKey in fileData) {
|
||||
if (fileData[innerKey] && values.includes(innerKey)) {
|
||||
if (!final[key]) final[key] = {}
|
||||
if (jsonValues) final[key][innerKey] = JSON.parse(fileData[innerKey].replace(/{/g, '[').replace(/}/g, ']')); // for icon sheet
|
||||
else final[key][innerKey] = fileData[innerKey]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
console.log("Finishing up...")
|
||||
|
||||
if (exportName == "achievements") { // hardcoded shit starts here!
|
||||
|
||||
let achString = "geometry.ach."
|
||||
let rewardTypes = {color: "color1", icon: "cube", bird: "ufo", dart: "wave", special: "trail", death: "deathEffect"}
|
||||
let games = {"md": "meltdown", "world.": "world"}
|
||||
|
||||
let achArray = []
|
||||
for (let k in final) {
|
||||
let reward = final[k].icon ? final[k].icon.split("_") : []
|
||||
let achObj = {
|
||||
id: k.slice(achString.length),
|
||||
game: "gd",
|
||||
name: final[k].title,
|
||||
rewardType: rewardTypes[reward[0]] || reward[0] || "misc",
|
||||
rewardID: +reward[1] || -1,
|
||||
description: final[k].unachievedDescription,
|
||||
achievedDescription: final[k].achievedDescription
|
||||
}
|
||||
Object.keys(games).forEach(x => {
|
||||
if (k.startsWith(achString + x)) achObj.game = games[x]
|
||||
})
|
||||
achArray.push(achObj)
|
||||
}
|
||||
data = achArray.filter(x => !x.id.startsWith("lite"))
|
||||
data = data.filter(x => x.game == "gd").concat(data.filter(x => x.game != "gd"))
|
||||
}
|
||||
|
||||
fs.writeFileSync(exportName + '.json', JSON.stringify(data, null, 2).replace(/\[\n.+?(-?\d+),\n.+?(-?\d+)\n.+]/g, "[$1, $2]")); // regex to make it easier to read
|
||||
|
||||
console.log("Successfully converted!")
|