@dada78641/bwrepinfo
Version:
Parses StarCraft: Remastered replay files and returns human-readable information
140 lines (128 loc) • 4.2 kB
JavaScript
// bwbaseInfo <https://github.com/msikma/hydrabot>
// © MIT license
import {gameTypeDefinitions} from 'sctoolsdata'
import {sortRaces} from 'sctoolsdata/lib/races/index.js'
import {formatChatToCodeBlock} from './chat.js'
import {formatDynamicTimestamp, formatFilesize, formatDateClockEmoji} from '../util/format.js'
/**
* Returns a race emoji for a given player.
*/
function getRaceEmoji(race) {
switch (race) {
case 'terran':
return ':terran:'
case 'protoss':
return ':protoss:'
case 'zerg':
return ':zerg:'
default:
return ':question:'
}
}
/**
* Returns a shortened version of the game type string.
*
* This changes a number of the longer game names to an abbreviated version.
* For example, "Top vs Bottom" is reduced to "TvB".
*/
function getGameTypeString(typeSlug) {
const gameType = Object.values(gameTypeDefinitions).find(type => type.slug === typeSlug)
return gameType.name
.replace(/Top vs Bottom/i, 'TvB')
.replace(/Free For All/i, 'FFA')
.replace(/Capture The Flag/i, 'CTF')
.replace(/Use Map Settings/i, 'UMS')
}
/**
* Returns the matchup for this game by the list of players.
*
* If there are only two players, the race letters will be sorted.
*/
function getGameMatchup(players) {
const raceLetters = players.map(player => player.race.slice(0, 1).toUpperCase())
return (raceLetters.length === 2 ? raceLetters.sort(sortRaces) : raceLetters).join('v')
}
/**
* Returns a title representing this game.
*
* This will generate a specific format for standard 1v1 games,
* and a more generic format for all other cases.
*/
function getGameTitle(baseInfo) {
const mapName = baseInfo.map.name
const gameType = getGameTypeString(baseInfo.game.type)
const gameHost = baseInfo.host
const playerAmount = baseInfo.players.length
// If there are just two players, this is a 1v1.
if (playerAmount === 2) {
const matchup = getGameMatchup(baseInfo.players)
return `${matchup} ${getPlayerName(baseInfo.players[0])} v. ${getPlayerName(baseInfo.players[1])} @ ${mapName}`
}
// In all other cases, return a generic name.
return `${playerAmount} player ${gameType} hosted by ${gameHost} @ ${mapName}`
}
/**
* Returns a string representing a player.
*
* This includes their name and an emoji representing their race.
*/
function getPlayerName(playerObj, apm = null, isCpu = false) {
return `${getRaceEmoji(playerObj.race)} ${playerObj.name}${apm ? ` (${apm} apm)` : ''}${isCpu ? ` (CPU)` : ''}`
}
/**
* Returns a string of race emojis representing all players in a game.
*/
function getPlayerRaces(players) {
return players.map(player => getRaceEmoji(player.race)).join('')
}
/**
* Returns a list of players.
*/
function getPlayerList(players) {
return players.map(player => {
return {
name: player.name,
apm: player.apm,
eapm: player.eapm,
race: player.race,
isCpu: player.ID === 255,
nameFormatted: getPlayerName(player, player.apm, player.ID === 255)
}
})
}
/**
* Returns formatted "rich" info about a replay, to easily embed in a Discord message.
*/
export function generateRichInfo(replay, baseInfo, bufferSize, options = {}) {
return {
title: getGameTitle(baseInfo),
game: {
type: getGameTypeString(baseInfo.game.type),
matchup: getGameMatchup(baseInfo.players),
title: baseInfo.game.title
},
players: {
amount: baseInfo.players.length,
races: getPlayerRaces(baseInfo.players),
list: getPlayerList(baseInfo.players)
},
time: {
startTime: formatDynamicTimestamp(baseInfo.game.startTime, 'F'),
startTimeRel: formatDynamicTimestamp(baseInfo.game.startTime, 'R'),
startTimeEmoji: formatDateClockEmoji(baseInfo.game.startTime),
duration: baseInfo.game.duration
},
map: {
name: baseInfo.map.name,
originalName: baseInfo.map.originalName
},
file: {
bytes: bufferSize,
size: formatFilesize(bufferSize)
},
messages: formatChatToCodeBlock(baseInfo.messages, baseInfo.players, options.useSpoilerMessages, options.maxChatLines),
meta: {
is1v1: baseInfo.players.length === 2
}
}
}