您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows NT Season Points earned and Skipped Characters (Nitros) Used on Race Result screen.
- // ==UserScript==
- // @name Nitro Type - Race Result Enhancements
- // @version 0.4.4
- // @description Shows NT Season Points earned and Skipped Characters (Nitros) Used on Race Result screen.
- // @author Toonidy
- // @match *://*.nitrotype.com/race
- // @match *://*.nitrotype.com/race/*
- // @icon https://i.ibb.co/YRs06pc/toonidy-userscript.png
- // @require https://greasyfork.org/scripts/443718-nitro-type-userscript-utils/code/Nitro%20Type%20Userscript%20Utils.js?version=1042360
- // @grant none
- // @license MIT
- // @namespace https://greasyfork.org/users/858426
- // ==/UserScript==
- /* global createLogger findReact */
- const logging = createLogger("Nitro Type Race Result Enhancements")
- /////////////
- // Utils //
- /////////////
- /** Calculate User's Race score. */
- const getUserRaceResult = (user) => {
- const { typed, nitros, skipped, startStamp, completeStamp, errors } = user.progress
- let endStamp = completeStamp || Date.now()
- const wpm = Math.round((typed - skipped) / 5 / ((endStamp - startStamp) / 6e4)),
- accuracy = ((1 - errors / (typed - skipped)) * 100).toFixed(2),
- points = Math.round((100 + wpm / 2) * (1 - errors / (typed - skipped)))
- return { accuracy, points, wpm, nitros, skipped }
- }
- /** Sort Handler to sort by rank position. */
- const sortRacersHandler = (e, t) => {
- // Source: https://www.nitrotype.com/dist/site/js/ra.js
- return e.disqualified && !t.disqualified
- ? 1
- : (t.disqualified && !e.disqualified) || (e.progress.completeStamp && !t.progress.completeStamp)
- ? -1
- : t.progress.completeStamp && !e.progress.completeStamp
- ? 1
- : e.progress.completeStamp && t.progress.completeStamp
- ? e.progress.completeStamp < t.progress.completeStamp
- ? -1
- : 1
- : e.progress.percentageFinished === t.progress.percentageFinished
- ? 0
- : e.progress.percentageFinished > t.progress.percentageFinished
- ? -1
- : 1
- }
- ///////////////////
- // Racing Page //
- ///////////////////
- const raceContainer = document.getElementById("raceContainer"),
- raceObj = raceContainer ? findReact(raceContainer) : null
- if (!raceContainer || !raceObj) {
- logging.error("Init")("Could not find the race track")
- return
- }
- const server = raceObj.server
- /** Mutation obverser to track whether results screen showed up. */
- const resultObserver = new MutationObserver((mutations, observer) => {
- for (const mutation of mutations) {
- for (const newNode of mutation.addedNodes) {
- if (newNode.classList?.contains("race-results")) {
- observer.disconnect()
- // Setup New Racer Stats Container
- const racers = raceObj.state.racers.slice().sort(sortRacersHandler)
- const dummyCell = document.createElement("div")
- dummyCell.classList.add("split-cell")
- let racerRankNewNodes = []
- const racerRankNodes = newNode.querySelectorAll(".gridTable-row")
- racerRankNodes.forEach((node, i) => {
- const r = racers[i]
- const listRow = node.querySelector(".gridTable-cell:nth-of-type(2) .split"),
- statRow = listRow.querySelector(".split-cell:nth-of-type(2)")
- // Add in the new stat fields
- const { points, skipped } = getUserRaceResult(r),
- accuracyNode = statRow.querySelector(".list .list-item:nth-of-type(2)"),
- suffixClass = accuracyNode?.querySelector("span")?.className || "tc-ts"
- const newStatRow = document.createElement("div")
- newStatRow.className = `${listRow.className} new-stat-row`
- newStatRow.append(dummyCell.cloneNode(), statRow)
- listRow.append(dummyCell.cloneNode())
- listRow.after(newStatRow)
- const skippedNode = document.createElement("div")
- skippedNode.classList.add("list-item", "skipped")
- skippedNode.innerHTML = `${r.robot ? "N/A" : skipped} <span class="${suffixClass}">Skipped</span>`
- const pointsNode = document.createElement("div")
- pointsNode.classList.add("list-item", "points")
- pointsNode.innerHTML = `${r.robot ? "N/A" : points} <span class="${suffixClass}">Points</span>`
- racerRankNewNodes[i] = [skippedNode, pointsNode]
- if (!accuracyNode) {
- if (!node.classList.contains("is-wampus") && !r.disqualified) {
- logging.warn(`Race Result")("Unable to setup new stats on row ${i}`)
- }
- return
- }
- accuracyNode.after(skippedNode, pointsNode)
- })
- /* Track new progress updates */
- server.on("update", (e) => {
- const racers = raceObj.state.racers.slice().sort(sortRacersHandler)
- racerRankNodes.forEach((node, i) => {
- const r = racers[i],
- { points, skipped } = getUserRaceResult(r),
- [skippedNode, pointsNode] = racerRankNewNodes[i],
- accuracyNode = node.querySelector(".new-stat-row .list .list-item:nth-of-type(2)")
- if (r.disqualified || node.classList.contains("is-wampus")) {
- skippedNode.remove()
- pointsNode.remove()
- return
- }
- skippedNode.childNodes[0].textContent = `${r.robot ? "N/A" : skipped} `
- pointsNode.childNodes[0].textContent = `${r.robot ? "N/A" : points} `
- if (!accuracyNode) {
- logging.warn(`Race Result")("Unable to insert new stats back into row ${i}`)
- return
- }
- accuracyNode.after(skippedNode, pointsNode)
- })
- })
- return
- }
- }
- }
- })
- resultObserver.observe(raceContainer, { childList: true, subtree: true })
- logging.info("Init")("Race Result listener has been setup")