Skip to content

Instantly share code, notes, and snippets.

@malte-laukoetter
Created July 18, 2018 11:42
Show Gist options
  • Save malte-laukoetter/82cc07eb162c22aa4b8ef758bfdad15c to your computer and use it in GitHub Desktop.
Save malte-laukoetter/82cc07eb162c22aa4b8ef758bfdad15c to your computer and use it in GitHub Desktop.
import { GameType, Player, PlayerGameInfo, GameTypes, GntmPlayerGameInfo } from "../node_modules/hive-api";
declare let config: any
declare let logging: any
declare let embed: any
//Stats comparison formatting function
//This is what makes sure the stats are
//laid out as evenly as possible
function statsFormatting(variable, playerB) {
//Because maximum nickname length is 16 characters,
//that's what each column is limited to
var needed = (17 - variable.toString().length);
//spaceString is basically how many spaces the variable
//needs to reach the 16 character limit
var spaceString = "";
for(var i = 0; i < needed; i++) {
spaceString += " ";
}
//playerB checks whether the spaces have to
//be applied on the right or left of the variable
if(!playerB) {
return "<" + variable + spaceString;
} else {
return spaceString + variable + ">";
}
}
//This is just a nifty little functions that shows which
//of the stats is better, formatting it for the code block
function theText(varA,varB,middleText,more) {
var output = "";
var statA = "> ";
var statB = " <";
//the 'more' variable is there due to some stats being
//better the lower they are (deaths for instance)
if (more) {
if (varA>varB) {
statA = ">⍣";
} else if (varB>varA) {
statB = "⍣<";
}
} else {
if (varA<varB) {
statA = ">⍣";
} else if (varB<varA) {
statB = "⍣<";
}
}
output = statsFormatting(varA,false) + statA + middleText + statB + statsFormatting(varB,true)
return output;
}
/**
* applys the infoGetter to both players PlayerGameInfos
* @param infoGetter function that takes a PlayerGameInfo and transforms it into a number
* @param gameInfoPlayer1 PlayerGameInfo of first player
* @param gameInfoPlayer2 PlayerGameInfo of second player
* @param middleText see theText
* @param more see theText
*/
function compareText<T extends PlayerGameInfo>(infoGetter: ((gameInfo: T) => number), gameInfoPlayer1: T, gameInfoPlayer2: T, middleText: string, more: boolean) {
return theText(infoGetter(gameInfoPlayer1), infoGetter(gameInfoPlayer2), middleText, more)
}
//checkDM will delete the bots message if it's not sent via a DM.
//Default interval is 30000 miliseconds
function checkDM(msg, DM) {
if (DM != "dm" && config.settings.messageRemovalDelay > 0) {
msg.delete(config.settings.messageRemovalDelay);
}
};
/**
* generates the compare message, handels the errors and sends the message to the chat
* @param gameType gameType of this compare
* @param player1 first player
* @param player2 player to compare the first player with
* @param message the discord message that requested this compare
*/
async function generateCompare<T extends PlayerGameInfo>(gameType: GameType, player1: Player, player2: Player, message) {
if (message.channel.type != "dm" && config.settings.commandRemoval) {
message.delete();
}
try {
const gameInfoPlayer1 = await player1.gameInfo(gameType) as T
const gameInfoPlayer2 = await player2.gameInfo(gameType) as T
if (gameInfoPlayer1 && gameInfoPlayer2) {
const msg = await message.reply(generateCompareMessage(gameType, player1, gameInfoPlayer1, player2, gameInfoPlayer2))
checkDM(msg, message.channel.type);
} else {
message.reply("", {
embed: embed("Error", "An error occurred. Maybe you misspelled second player's name?", "red")
}).then(msg => checkDM(msg, message.channel.type));
}
} catch (error) {
logging.legacyLog("URGENT HTTP ERROR")
}
}
/**
* pads the string alternatly at the beginning and the end
* @param length target length of the string
* @param str string to pad
* @param startFront should the first add be in the front of the string?
*/
function padBoth(length: number, str: string, startFront: boolean = false){
if(str.length < length){
if(startFront){
str = ' ' + str
} else {
str = str + ' '
}
return padBoth(length, str, !startFront)
}
return str
}
/**
* creates the message for the compare
*/
function generateCompareMessage<T extends PlayerGameInfo>(gameType: GameType, playerA: Player, playerAInfo: T, playerB: Player, playerBInfo: T){
const titleTextTargetLength = gameTypeStatsToText.get(gameType).map(({title}) => title.length).sort((a,b) => b - a).pop()
return `**Comparison of ${gameType.name} of ${playerA.name} and ${playerB.name}**
\`\`\`md
${statsFormatting(playerA.name, false)}> * ${padBoth(titleTextTargetLength, 'Category')} * <${statsFormatting(playerB.name, false)}
${
gameTypeStatsToText.get(gameType)
.map(({title, infoGetter}) => theText(infoGetter(playerAInfo), infoGetter(playerBInfo), `* ${padBoth(titleTextTargetLength, title)} *`, true))
.join('\n')
}\`\`\``
}
/**
* a list of stats that can be generated from a PlayerGameInfo
*
* the title is the text used to describe the stat
* and the infoGetter takes a PlayerGameInfo and converts it to the number that should be shown
*/
type GameTypeStatsList<T extends PlayerGameInfo> = {title: string, infoGetter: (gameInfo: T) => number}[]
/**
* maps the different GameTypes to there GameTypeStatsList
*/
const gameTypeStatsToText: Map<GameType, GameTypeStatsList<any>> = new Map();
gameTypeStatsToText.set(GameTypes.GNTM, [
{
title: 'Points',
infoGetter: info => info.points
}, {
title: 'Victories',
infoGetter: info => info.victories
}, {
title: 'Games Played',
infoGetter: info => info.gamesPlayed
}, {
title: 'W/L Ratio',
infoGetter: info => Math.round(100 * info.victories / (info.gamesPlayed - info.victories)) / 100
}, {
title: 'PPG',
infoGetter: info => Math.round(info.points / info.gamesPlayed)
}, {
title: 'Kills',
infoGetter: info => info.kills
}, {
title: 'Deaths',
infoGetter: info => info.deaths
}, {
title: 'K/D',
infoGetter: info => Math.round(100 * info.kills / info.deaths) / 100
}, {
title: 'Gold Earned',
infoGetter: info => info.goldEarned
}, {
title: 'Beasts Slain',
infoGetter: info => info.beastsSlain
}, {
title: 'Shutdowns',
infoGetter: info => info.shutdowns
}
] as GameTypeStatsList<GntmPlayerGameInfo>)
/* Information on the command as a whole
Just as with -stats, this command is mostly Hardcoded.
The reasoning is similar: I believe it's better to code
in whatever stats you think are worth comparing, rather
than rely on same lists for each of so different games
But if you believe something has to be added, I suggest
messaging me. You can try adding the code in yourself,
however messaging me may make the updated comparison
available to everyone with the next update.
*/
module.exports = {
/* Command Information
===============================================================
If you'd wish to change the description of a command,
or where it can be executed, here is where you do that.
By default all commands have "All" channels allowed,
to limit it put channel ID's within "" and separate by commas.
Example:
allowedChannels: ["321251232131","1579213910451"],
Please not that description and usage is only visible in -help,
and actual command name has to be changed via commands.js
===============================================================
*/
description: "Compares statistics of two players\nFor list of available Main Game Codes, type \"-compare list\" \nFor list of available Arcade Game Codes, type \"-compare arcade\"",
usage: "-compare {Game Code} {Player1} {Player2}",
allowedInDM: true,
allowedChannels: ["All"],
call: function(message, args){
if (args[0]==undefined || args[1]==undefined || args[2]==undefined) {
if (message.channel.type != "dm" && config.settings.commandRemoval) {message.delete(config.settings.messageRemovalDelay);}
message.reply("The proper usage for the command is: " +
"\n-compare {Game Code} {Player1} {Player2}"+
"\nFor list of available Main Game Codes, type `-compare list`").then(msg => checkDM(msg, msg.channel.type, 2));
}else{
const gameType = GameTypes.list.find(type => type.id.toLowerCase() === args[0].toLowerCase())
if(gameType && gameTypeStatsToText.has(gameType)){
generateCompare(gameType, new Player(args[0]), new Player(args[1]), message)
} else {
if (message.channel.type != "dm" && config.settings.commandRemoval) { message.delete(); }
message.reply("I'm sorry, but I do not recognize that gamemode. Here is a list of supported games:",
{
embed: embed("HiveMC Gamemode Shortcut Help",
"Available Gamemodes: \n" +
[... gameTypeStatsToText.keys()].map(gameType => {
` • ${gameType.name} - ${gameType.id}`
}) +
"For Arcade game codes use `-compare Arcade`\n" +
"\nUsage: `-compare {Game Code} {Player1} {Player2}`", "blue")
}).then(msg => checkDM(msg, message.channel.type));
}
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment