Greasy Fork is available in English.

VisNotes MPP

Visualization of notes (based on Chacha-26 script)

От 26.11.2023. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         VisNotes MPP
// @namespace    http://tampermonkey.net/
// @version      0.9.3
// @description  Visualization of notes (based on Chacha-26 script)
// @author       Hustandant#1917
// @match        *://multiplayerpiano.net/*
// @icon         https://github.com/Hustoroff/mpp/blob/main/icon.png?raw=true
// @grant        none
// @license      MIT
// @run-at       document-end
// ==/UserScript==
window.addEventListener('load', (event) => {
//++++++++++++++++++ You can change this (Hot keys) ++++++++++++++++++++

const OnOff = "113"; //F2
const FirstKey = "3"; //3
const SecondKey = "Tab"; //Tab

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

var notes = 0, nps = 0, fps = 0;

async function ping() {
  let start = Date.now();
  try { await fetch(`${window.location.protocol}${MPP.client.uri.slice(4)}`) }
  catch(err) {}
  return (Date.now() - start);
};

function fpsCount() { fps += 1; requestAnimationFrame(fpsCount) };

requestAnimationFrame(fpsCount);

if(!localStorage.getItem("speed")) localStorage.setItem("speed", 60);
var noteSpeed = localStorage.getItem("speed"); //default 60 per sec

MPP.client.on("a", function(msg) {
    let message = msg.a.split(" ");
    if(message[0] == "speed" && msg.p.id == MPP.client.participantId && !isNaN(Number(message[1]))) {
        noteSpeed = (Number(message[1]) > 1000) ? 1000 : (Number(message[1]) <= 0) ? 1 : Number(message[1]);
        localStorage.setItem("speed", noteSpeed);
        document.getElementById("nspd").innerText = noteSpeed;
    }
});

MPP.client.emit("notification", {
		title: "VisNotes MPP script (by Hustandant#1917)",
        id:"Script_notification",
		duration:20000,
        target:"#piano",
        html:`<p><h3><font id="f2" color="">F2</font> - show/hide notes window</h3></br></p><p><h3><font id="3d" color="">Tab+3</font> - on/off darkly window</h3></br></p><p><h4><font color="limegreen">${noteSpeed}</font> - current speed (<span style="background-color: black"><font color="red">to chat "speed" [min - 1 max - 1000]</font></span>)</h4></br></p><p><h5><span style="background-color: black">Example: "speed 60"</span></h5></br></p> Join our discord server: <a target="_blank" href="https://discord.gg/A3SDgxS2Q2">https://discord.gg/A3SDgxS2Q2<a>`
});

const stat = document.createElement("div");
    stat.id = "stat_notes";
    stat.style.opacity = "1";
    stat.style.position = "fixed";
    stat.style["z-index"] = 150;
    stat.style.display = "block";
    stat.style.float = "right";
    stat.style.margin = "auto";
    stat.style.top = `${document.getElementById("piano").height}px`;
    stat.style["background-color"] = "rgba(137, 137, 137, 0.414)";
    stat.style["backdrop-filter"] = "blur(1px)";
    stat.style["font-size"] = "21px"
    stat.innerHTML = `Notes: <span id="notes">0</span> NPS: <span id="nps">0</span> Speed: <span id="nspd">${localStorage.getItem("speed")}</span> NQ: <span id="nquota">${MPP.noteQuota.points}</span> Ping: <span id="ping"></span> FPS: <span id="fps"></span>`;
    stat.style.marginLeft = `${String(document.getElementById("piano").offsetLeft + document.getElementById("piano").getElementsByTagName("canvas")[0].offsetLeft)}px`;


const canvas = document.createElement("canvas");
    canvas.height = parseInt(document.getElementById("piano").style["margin-top"]);
    canvas.width = Math.round(MPP.piano.renderer.width - (MPP.piano.renderer.width - MPP.piano.keys.c7.rect.x2));
    canvas.id = "track_of_notes";
    canvas.style.opacity = "1";
    canvas.style.top = "0";
    canvas.style.display = "block";
    canvas.style.float = "right";
    canvas.style.position = "fixed";
    canvas.style.margin = "auto";
    canvas.style.marginLeft = `${String(document.getElementById("piano").offsetLeft + document.getElementById("piano").getElementsByTagName("canvas")[0].offsetLeft)}px`;
    canvas.style["z-index"] = 150;

    const ctx = window.ctx = canvas.getContext("2d");
    const pixel = window.pixel = ctx.createImageData(document.getElementById("piano").querySelector("canvas").width,canvas.height);
    pixel.data.fill(0);
    let lastUpdate = 0, onlyevery = 4, counter = 0, prevTime = Date.now();
    const noteDB = {};

    Object.keys(MPP.piano.keys).forEach(key => noteDB[key] = MPP.piano.keys[key].rect.x);

    window.redraw = function() {
        if (lastUpdate <= canvas.height && counter++ % 4 == 0) {
            const currentTime = Date.now();
            const deltaTime = currentTime - prevTime;

            ctx.globalCompositeOperation = "copy";
            ctx.drawImage(ctx.canvas, 0, -Math.ceil(deltaTime * (noteSpeed / 1000)));
            ctx.globalCompositeOperation = "source-over";
            ctx.putImageData(pixel, 0, canvas.height - 1);

            prevTime = currentTime;

            if (lastUpdate++ == 0) {
                pixel.data.fill();
            }
        }
        requestAnimationFrame(redraw);
    };

    redraw();
    redraw(); // :)
    redraw(); // :)
    redraw(); // :)

    window.showNote = function(note, col, ch = 0) {
        if (note in noteDB) {
            lastUpdate = 0;
            const idx = (noteDB[note]) * 4;
            if(note.split("").includes("s")) {
                let otS = ((MPP.piano.keys[note].rect.w - Math.round(MPP.piano.renderer.blackBlipWidth))/2)*4;
                for(let i=0; i<(MPP.piano.renderer.blackBlipWidth)*4; i+=4){
                    pixel.data[idx + otS + i] = col[0];
                    pixel.data[idx + otS + i + 1] = col[1];
                    pixel.data[idx + otS + i + 2] = col[2];
                    pixel.data[idx + otS + i + 3] = 255;
                }
            } else {
                let ot = (Math.round((MPP.piano.keys[note].rect.w - Math.round(MPP.piano.renderer.whiteBlipWidth))/2))*4;
                for(let i=0; i<(MPP.piano.renderer.whiteBlipWidth)*4; i+=4){
                    pixel.data[idx + ot + i] = col[0];
                    pixel.data[idx + ot + i + 1] = col[1];
                    pixel.data[idx + ot + i + 2] = col[2];
                    pixel.data[idx + ot + i + 3] = 255;
                }
            }
        }
    }

    document.body.append(canvas);
    document.body.append(stat);

window.addEventListener('resize', resize);

function resize() {
  canvas.width = canvas.width;
  canvas.height = canvas.height;
  canvas.style.width = `${canvas.width / window.devicePixelRatio}px`;
  canvas.style.height = `${canvas.height / window.devicePixelRatio}px`;
  canvas.style.marginLeft = `${String(document.getElementById("piano").offsetLeft + document.getElementById("piano").getElementsByTagName("canvas")[0].offsetLeft)}px`;
};

window.onload = () => {
    if(!localStorage.getItem("display")) localStorage.setItem("display", document.getElementById("track_of_notes").style.display);
    if(!localStorage.getItem("theme")) localStorage.setItem("theme", document.getElementById("track_of_notes").style["background-color"]);

    document.getElementById("track_of_notes").style.display = localStorage.getItem("display");
    document.getElementById("track_of_notes").style["background-color"] = localStorage.getItem("theme");
    document.getElementById("3d").color = (localStorage.getItem("theme") == "rgb(16, 0, 0)") ? "limegreen" : "firebrick";
    document.getElementById("f2").color = (localStorage.getItem("display") == "block") ? "limegreen" : "firebrick";
    noteSpeed = (localStorage.getItem("speed")) ? localStorage.getItem("speed") : 60;
};

window.addEventListener("keydown", function(key) {
    if(key.keyCode == OnOff) {
        document.getElementById("track_of_notes").style.display = (document.getElementById("track_of_notes").style.display == "block") ? "none" : "block";
        localStorage.setItem("display", document.getElementById("track_of_notes").style.display);
        document.getElementById("f2").color = (localStorage.getItem("display") == "block") ? "limegreen" : "firebrick";
        return;
    }
});

function runOnKeys(func, ...codes) {
    let pressed = new Set();

    document.addEventListener('keydown', function(event) {
        pressed.add(event.key);

        for (let code of codes) if (!pressed.has(code)) return;
        pressed.clear();
        func();
    });

    document.addEventListener('keyup', function(event) { pressed.delete(event.key) });
};

    runOnKeys(() => {
          document.getElementById("track_of_notes").style["background-color"] = (document.getElementById("track_of_notes").style["background-color"] == "rgb(16, 0, 0)") ? "" : "rgb(16, 0, 0)";
          localStorage.setItem("theme", document.getElementById("track_of_notes").style["background-color"]);
          document.getElementById("3d").color = (localStorage.getItem("theme") == "rgb(16, 0, 0)") ? "limegreen" : "firebrick";
      },
      FirstKey,
      SecondKey
    );
function stats() { document.getElementById("notes").innerText = notes; document.getElementById("nquota").innerText = MPP.noteQuota.points };

function grad(nq, nqmax) { document.getElementById("nquota").style.color = `rgb(255, ${Math.round((nq/nqmax)*255)}, ${Math.round((nq/nqmax)*255)})` };

setInterval(async () => {
    document.getElementById("nps").innerText = nps;
    document.getElementById("nquota").innerText = MPP.noteQuota.points;
    document.getElementById("ping").innerText = await ping();
    document.getElementById("fps").innerText = fps;
    fps = nps = 0;
    grad(MPP.noteQuota.points, MPP.noteQuota.max);
}, 1000);

const colcache = Object.create(null);
MPP.piano.renderer.__proto__.vis = MPP.piano.renderer.__proto__.visualize;
MPP.piano.renderer.__proto__.visualize = function (n, c, ch) {
  notes += 1;
  nps += 1;
    stats();
    grad(MPP.noteQuota.points, MPP.noteQuota.max);
  this.vis(n,c,ch);
  let co = c in colcache ? colcache[c] : Object.freeze(colcache[c] = [c[1]+c[2], c[3]+c[4], c[5]+c[6]].map(x => parseInt(x, 16)));
  showNote(n.note, co);
};
});