2023-09-15 15:17:07 -06:00
|
|
|
import * as moth from "./moth.mjs"
|
|
|
|
import * as common from "./common.mjs"
|
2020-02-29 17:12:35 -07:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
const server = new moth.Server(".")
|
|
|
|
const ReplayDuration = 3 * common.Second
|
|
|
|
const MaxFrameRate = 24
|
|
|
|
/** Don't let any team's score exceed this percentage width */
|
|
|
|
const MaxScoreWidth = 95
|
2023-09-13 18:52:52 -06:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
/**
|
|
|
|
* Returns a promise that resolves after timeout.
|
|
|
|
*
|
|
|
|
* @param {Number} timeout How long to sleep (milliseconds)
|
|
|
|
* @returns {Promise}
|
|
|
|
*/
|
|
|
|
function sleep(timeout) {
|
|
|
|
return new Promise(resolve => setTimeout(resolve, timeout));
|
|
|
|
}
|
2023-09-13 18:52:52 -06:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
/**
|
|
|
|
* Pull new points log, and update the scoreboard.
|
|
|
|
*
|
|
|
|
* The update is animated, because I think that looks cool.
|
|
|
|
*/
|
|
|
|
async function update() {
|
|
|
|
let state = await server.GetState()
|
|
|
|
let rankingsElement = document.querySelector("#rankings")
|
|
|
|
let logSize = state.PointsLog.length
|
2023-09-13 18:52:52 -06:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
// Figure out the timing so that we can replay the scoreboard in about
|
|
|
|
// ReplayDuration, but no more than 24 frames per second.
|
|
|
|
let frameModulo = 1
|
|
|
|
let delay = 0
|
|
|
|
while (delay < (common.Second / MaxFrameRate)) {
|
|
|
|
frameModulo += 1
|
|
|
|
delay = ReplayDuration / (logSize / frameModulo)
|
2023-09-13 18:52:52 -06:00
|
|
|
}
|
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
let frame = 0
|
|
|
|
for (let scores of state.ScoreHistory()) {
|
|
|
|
frame += 1
|
|
|
|
if ((frame < state.PointsLog.length) && (frame % frameModulo)) {
|
|
|
|
continue
|
|
|
|
}
|
2023-09-13 18:52:52 -06:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
while (rankingsElement.firstChild) rankingsElement.firstChild.remove()
|
2023-09-13 18:52:52 -06:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
let sortedTeamIDs = [...scores.TeamIDs]
|
|
|
|
sortedTeamIDs.sort((a, b) => scores.CyFiScore(a) - scores.CyFiScore(b))
|
|
|
|
sortedTeamIDs.reverse()
|
2020-02-29 17:12:35 -07:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
let topScore = scores.CyFiScore(sortedTeamIDs[0])
|
|
|
|
for (let teamID of sortedTeamIDs) {
|
|
|
|
let teamName = state.TeamNames[teamID]
|
2020-02-29 17:12:35 -07:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
let row = rankingsElement.appendChild(document.createElement("div"))
|
2020-02-29 17:12:35 -07:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
let heading = row.appendChild(document.createElement("span"))
|
|
|
|
heading.textContent = teamName
|
|
|
|
heading.classList.add("teamname")
|
|
|
|
|
|
|
|
let categoryNumber = 0
|
|
|
|
for (let category of scores.Categories) {
|
|
|
|
let score = scores.CyFiCategoryScore(category, teamID)
|
|
|
|
if (!score) {
|
|
|
|
continue
|
|
|
|
}
|
2023-09-13 18:52:52 -06:00
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
let block = row.appendChild(document.createElement("span"))
|
|
|
|
let points = scores.GetPoints(category, teamID)
|
|
|
|
let width = MaxScoreWidth * score / topScore
|
|
|
|
|
|
|
|
block.textContent = category
|
|
|
|
block.title = `${points} points`
|
|
|
|
block.style.width = `${width}%`
|
|
|
|
block.classList.add(`cat${categoryNumber}`)
|
|
|
|
categoryNumber += 1
|
|
|
|
}
|
2020-02-29 17:12:35 -07:00
|
|
|
}
|
2023-09-15 15:17:07 -06:00
|
|
|
await sleep(delay)
|
2020-02-29 17:12:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-15 15:17:07 -06:00
|
|
|
function init() {
|
|
|
|
setInterval(update, common.Minute)
|
|
|
|
update()
|
2020-02-29 17:12:35 -07:00
|
|
|
}
|
2023-09-15 15:17:07 -06:00
|
|
|
|
|
|
|
common.WhenDOMLoaded(init)
|