您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays a mini map of the Race Track Version 2. 1st version is made by toonidy:) Subscribe to MV Typing on YouTube!
// ==UserScript== // @name Nitro Type - Racing Mini Map by MV Typing // @version 0.2.0 // @description Displays a mini map of the Race Track Version 2. 1st version is made by toonidy:) Subscribe to MV Typing on YouTube! // @author MV Typing // @match *://*.nitrotype.com/race // @match *://*.nitrotype.com/race/* // @icon https://i.ibb.co/YRs06pc/toonidy-userscript.png // @grant none // @require https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.5.4/browser/pixi.min.js // @require https://greasyfork.org/scripts/443718-nitro-type-userscript-utils/code/Nitro%20Type%20Userscript%20Utils.js?version=1042360 // @license MIT // @namespace https://greasyfork.org/users/858426 // ==/UserScript== /* globals PIXI findReact createLogger */ const config = { colors: { me: 0xf2887f, opponentPlayer: 0x9E00FF, opponentBot: 0xF3FF00, opponentWampus: 0xFF8000, nitro: 0xFF00D8, background: 0x696969, raceLane: 0xFF0000, startLine: 0x00F3FF, finishLine: 0x3200FF, }, trackLocally: true, // whether to track your player's position without server data moveDestination: { enabled: true, alpha: 0.5, // range 0.0 - 1.0 (the lower the value, the more transparent) }, } const logging = createLogger("Nitro Type Racing Mini Map"), raceContainer = document.querySelector("#raceContainer"), raceObj = raceContainer ? findReact(raceContainer) : null if (!raceContainer || !raceObj) { logging.error("Init")("Could not find the race track") return } if (!raceObj.props.user.loggedIn) { logging.error("Init")("This userscript is not available for Guest Racing") return } PIXI.utils.skipHello() const style = document.createElement("style") style.appendChild( document.createTextNode(` .nt-racing-mini-map-root canvas { display: block; }`)) document.head.appendChild(style) const racingMiniMap = new PIXI.Application({ width: 1024, height: 100, backgroundColor: config.colors.background }), container = document.createElement("div"); container.className = "nt-racing-mini-map-root" /////////////////////// // Prepare Objects // /////////////////////// const RACER_WIDTH = 28, CROSSING_LINE_WIDTH = 32, PADDING = 2, racers = Array(5).fill(null), server = raceObj.server, currentUserID = raceObj.props.user.userID // Draw mini racetrack const raceTrackBG = new PIXI.TilingSprite.from("/dist/site/images/backgrounds/bg-noise.png", { width: racingMiniMap.renderer.width, height: racingMiniMap.renderer.height} ), startLine = PIXI.Sprite.from(PIXI.Texture.WHITE), finishLine = PIXI.Sprite.from(PIXI.Texture.WHITE) startLine.x = CROSSING_LINE_WIDTH startLine.y = 0 startLine.width = 1 startLine.height = racingMiniMap.renderer.height startLine.tint = config.colors.startLine finishLine.x = racingMiniMap.renderer.width - CROSSING_LINE_WIDTH - 1 finishLine.y = 0 finishLine.width = 1 finishLine.height = racingMiniMap.renderer.height finishLine.tint = config.colors.finishLine raceTrackBG.addChild(startLine, finishLine) for (let i = 1; i < 5; i++) { const lane = PIXI.Sprite.from(PIXI.Texture.WHITE) lane.x = 0 lane.y = i * (racingMiniMap.renderer.height / 5) lane.width = racingMiniMap.renderer.width lane.height = 1 lane.tint = config.colors.raceLane raceTrackBG.addChild(lane) } racingMiniMap.stage.addChild(raceTrackBG) /* Mini Map movement animation update. */ function animateRacerTicker() { const r = this const lapse = Date.now() - r.lastUpdated if (r.sprite.x < r.toX) { const distance = r.toX - r.fromX r.sprite.x = r.fromX + Math.min(distance, distance * (lapse / r.moveMS)) if (r.ghostSprite && r.sprite.x === r.ghostSprite.x) { r.ghostSprite.renderable = false } } if (r.skipped > 0) { const nitroTargetWidth = r.nitroToX - r.nitroFromX if (r.nitroSprite.width < nitroTargetWidth) { r.nitroSprite.width = Math.min(nitroTargetWidth, r.sprite.x - r.nitroFromX) } else if (r.nitroSprite.width === nitroTargetWidth && r.nitroSprite.alpha > 0 && !r.nitroDisableFade) { if (r.nitroSprite.alpha === 1) { r.nitroStartFadeStamp = Date.now() - 1 } r.nitroSprite.alpha = Math.max(0, 1 - ((Date.now() - r.nitroStartFadeStamp) / 1e3)) } } if (r.completeStamp !== null && r.sprite.x === r.toX && r.nitroSprite.alpha === 0) { racingMiniMap.ticker.remove(animateRacerTicker, this) } } /* Handle adding in players on the mini map. */ server.on("joined", (e) => { const { lane, userID } = e let color = config.colors.opponentBot if (userID === currentUserID) { color = config.colors.me } else if (!e.robot) { color = config.colors.opponentPlayer } else if (e.profile.specialRobot === "wampus") { color = config.colors.opponentWampus } if (racers[lane]) { racers[lane].ghostSprite.tint = color racers[lane].sprite.tint = color racers[lane].sprite.x = 0 - RACER_WIDTH + PADDING racers[lane].lastUpdated = Date.now() racers[lane].fromX = racers[lane].sprite.x racers[lane].toX = PADDING racers[lane].sprite.renderable = true return } const r = PIXI.Sprite.from(PIXI.Texture.WHITE) r.x = 0 - RACER_WIDTH + PADDING r.y = PADDING + (lane > 0 ? 1 : 0) + (lane * (racingMiniMap.renderer.height / 5)) r.tint = color r.width = RACER_WIDTH r.height = 16 - (lane > 0 ? 1 : 0) const n = PIXI.Sprite.from(PIXI.Texture.WHITE) n.y = r.y + ((16 - (lane > 0 ? 1 : 0)) / 2) - 1 n.renderable = false n.tint = config.colors.nitro n.width = 1 n.height = 2 racers[lane] = { lane, sprite: r, userID: userID, ghostSprite: null, nitroSprite: n, lastUpdated: Date.now(), fromX: r.x, toX: PADDING, skipped: 0, nitroStartFadeStamp: null, nitroFromX: null, nitroToX: null, nitroDisableFade: false, moveMS: 500, completeStamp: null, } if (config.moveDestination.enabled) { const g = PIXI.Sprite.from(PIXI.Texture.WHITE) g.x = PADDING g.y = PADDING + (lane > 0 ? 1 : 0) + (lane * (racingMiniMap.renderer.height / 5)) g.tint = color g.alpha = config.moveDestination.alpha g.width = RACER_WIDTH g.height = 16 - (lane > 0 ? 1 : 0) g.renderable = false racers[lane].ghostSprite = g racingMiniMap.stage.addChild(g) } racingMiniMap.stage.addChild(n) racingMiniMap.stage.addChild(r) racingMiniMap.ticker.add(animateRacerTicker, racers[lane]) }) /* Handle any players leaving the race track. */ server.on("left", (e) => { const lane = racers.findIndex((r) => r?.userID === e) if (racers[lane]) { racers[lane].sprite.renderable = false racers[lane].ghostSprite.renderable = false racers[lane].nitroSprite.renderable = false } }) /* Handle race map progress position updates. */ server.on("update", (e) => { let moveFinishMS = 500 const payloadUpdateRacers = e.racers.slice().sort((a, b) => { if (a.progress.completeStamp === b.progress.completeStamp) { return 0 } if (a.progress.completeStamp === null) { return 1 } return a.progress.completeStamp > 0 && b.progress.completeStamp > 0 && a.progress.completeStamp > b.progress.completeStamp ? 1 : -1 }) for (let i = 0; i < payloadUpdateRacers.length; i++) { const r = payloadUpdateRacers[i], { completeStamp, skipped } = r.progress, racerObj = racers[r.lane] if (!racerObj || racerObj.completeStamp > 0 || (r.userID === currentUserID && completeStamp <= 0 && config.trackLocally)) { continue } if (r.disqualified) { racingMiniMap.ticker.remove(animateRacerTicker, racerObj) racingMiniMap.stage.removeChild(racerObj.sprite, racerObj.nitroSprite) if (racerObj.ghostSprite) { racingMiniMap.stage.removeChild(racerObj.ghostSprite) } racerObj.sprite.destroy() racerObj.ghostSprite.destroy() racerObj.nitroSprite.destroy() racers[r.lane] = null continue } racerObj.lastUpdated = Date.now() racerObj.fromX = racerObj.sprite.x if (racerObj.completeStamp === null && completeStamp > 0) { racerObj.completeStamp = completeStamp racerObj.toX = racingMiniMap.renderer.width - RACER_WIDTH - PADDING racerObj.moveMS = moveFinishMS if (racerObj.nitroDisableFade) { racerObj.nitroToX = racingMiniMap.renderer.width - RACER_WIDTH - PADDING racerObj.nitroDisableFade = false } } else { racerObj.moveMS = 1e3 racerObj.toX = r.progress.percentageFinished * (racingMiniMap.renderer.width - RACER_WIDTH - CROSSING_LINE_WIDTH - PADDING - 1) racerObj.sprite.x = racerObj.fromX } if (racerObj.ghostSprite) { racerObj.ghostSprite.x = racerObj.toX racerObj.ghostSprite.renderable = true } if (skipped !== racerObj.skipped) { if (racerObj.skipped === 0) { racerObj.nitroFromX = racerObj.fromX racerObj.nitroSprite.x = racerObj.fromX racerObj.nitroSprite.renderable = true } racerObj.skipped = skipped // because infinite nitros exist? :/ racerObj.nitroToX = racerObj.toX racerObj.nitroSprite.alpha = 1 if (racerObj.completeStamp !== null) { racerObj.nitroToX = racingMiniMap.renderer.width - RACER_WIDTH - PADDING } } if (completeStamp > 0 && i + 1 < payloadUpdateRacers.length) { const nextRacer = payloadUpdateRacers[i + 1], nextRacerObj = racers[nextRacer?.lane] if (nextRacerObj && nextRacerObj.completeStamp === null && nextRacer.progress.completeStamp > 0 && nextRacer.progress.completeStamp > completeStamp) { moveFinishMS += 250 } } } }) if (config.trackLocally) { let lessonLength = 0 server.on("status", (e) => { if (e.status === "countdown") { lessonLength = e.lessonLength } }) const originalSendPlayerUpdate = server.sendPlayerUpdate server.sendPlayerUpdate = (data) => { originalSendPlayerUpdate(data) const racerObj = racers.find((r) => r?.userID === currentUserID) if (!racerObj) { return } const percentageFinished = (data.t / (lessonLength || 1)) racerObj.lastUpdated = Date.now() racerObj.fromX = racerObj.sprite.x racerObj.moveMS = 500 racerObj.toX = percentageFinished * (racingMiniMap.renderer.width - RACER_WIDTH - CROSSING_LINE_WIDTH - PADDING - 1) racerObj.sprite.x = racerObj.fromX if (racerObj.ghostSprite) { racerObj.ghostSprite.x = racerObj.toX racerObj.ghostSprite.renderable = true } if (data.s) { if (racerObj.skipped === 0) { racerObj.nitroFromX = racerObj.fromX racerObj.nitroSprite.x = racerObj.fromX racerObj.nitroSprite.renderable = true } racerObj.skipped = data.s // because infinite nitros exist? but I'm not going to test that! :/ racerObj.nitroToX = racerObj.toX racerObj.nitroSprite.alpha = 1 racerObj.nitroDisableFade = percentageFinished === 1 if (racerObj.completeStamp !== null) { racerObj.nitroToX = racingMiniMap.renderer.width - RACER_WIDTH - PADDING } } } } ///////////// // Final // ///////////// container.append(racingMiniMap.view) raceContainer.before(container)// ==UserScript== // @name New Userscript // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @match https://www.nitrotype.com/garage // @icon https://www.google.com/s2/favicons?sz=64&domain=nitrotype.com // @grant none // ==/UserScript== (function() { 'use strict'; // Your code here... })();