// ==UserScript==
// @name Display Attack
// @namespace http://tampermonkey.net/
// @version 0.2.3
// @description Display attacks and attack values next to users in multiplayer modes with attack.
// @author PMP
// @match https://*.jstris.jezevec10.com/*
// @grant none
// ==/UserScript==
const DELAY = 1500; // ms
const FADEOUT = 0.15; // s
const SPIKE_TIMER = 1000;
const MAX_HEIGHT = 250;
class Displayer {
constructor(index) {
this.index = index;
this.id = 0;
this.displayedActions = [];
this.spike = 0;
this.lastAttack = 0;
this.lastSpikeAttack = 0;
}
displayNewAction(value, atk) {
let ctime = (new Date()).getTime();
let spike_tracker = document.getElementById(`atk_spike_${this.index+1}`);
if (ctime - this.lastAttack < SPIKE_TIMER) {
this.spike += value;
} else {
this.spike = value
}
if (this.spike >= 10) {
spike_tracker.classList.remove("fade");
spike_tracker.classList.add("fade", "in");
spike_tracker.innerHTML = `${this.spike} SPIKE`;
this.lastSpikeAttack = ctime;
setTimeout((time) => {
if (this.lastSpikeAttack == time) {
spike_tracker.classList.remove("in");
setTimeout((remove_from) => {
remove_from.innerHTML = "";
}, FADEOUT * 1000, spike_tracker);
this.spike = 0;
}
}, SPIKE_TIMER, ctime);
}
this.lastAttack = ctime;
let action = document.createElement("p");
action.innerHTML = `+${value} ${atk}`;
action.style.textAlign = "center";
if (value >= 5) {
action.style.fontSize = "large";
action.style.fontWeight = "bold";
}
if (value >= 10) {
action.style.color = "red";
}
action.setAttribute("id", `atk_text_${this.index+1}_${this.id++}`);
action.setAttribute("class", "fade in");
document.getElementById(`atk_div_${this.index+1}`).prepend(action);
this.displayedActions.splice(0, 0, action.id);
setTimeout((ind, id) => {
try {
let target = document.getElementById(`atk_text_${ind+1}_${id-1}`);
target.classList.remove("in");
setTimeout((target) => {
try {
this.displayedActions = this.displayedActions.filter((i) => i != target.id);
target.parentNode.removeChild(target);
} catch (e) {} // idc
}, FADEOUT * 1000, target);
} catch (e) {} // idc
}, DELAY, this.index, this.id);
}
reset() {
for (let action of this.displayedActions) {
try{
action.parentNode.removeChild(action);
}catch(e){}
}
this.displayedActions = [];
this.id = 0;
}
}
class DisplayerManager {
constructor() {
this.displayers = [];
}
createDisplayer() {
let a = new Displayer();
a.index = this.addDisplayer(a);
return a;
}
addDisplayer(displayer) {
for (let i = 0; i < this.displayers.length; i++) {
if (this.displayers[i] == null) {
this.displayers[i] = displayer;
return i;
}
}
this.displayers.push(displayer);
return this.displayers.length - 1;
}
destroyDisplayer(displayer) {
for (let i = 0; i < this.displayers.length; i++) {
if (this.displayers[i] == displayer) {
this.displayers[i] = null;
return i;
}
}
}
}
(function() {
'use strict';
window.addEventListener("load", function() {
window.displayerManager = new DisplayerManager();
let lstages = document.getElementsByClassName("lstage");
if (lstages.length == 0) {
let canvases = document.querySelectorAll("div#main > canvas"); // who tf uses the same ID for multiple thing smh
for (let canvas of canvases) {
let div = document.createElement("div");
div.setAttribute("class", "lstage");
canvas.parentNode.insertBefore(div, canvas);
div.appendChild(canvas);
}
}
lstages = document.getElementsByClassName("lstage");
for (let i = 1; i <= lstages.length; i++) {
let lstage = lstages[i-1];
let num = window.displayerManager.createDisplayer();
let spike_tracker = document.createElement("p");
spike_tracker.setAttribute("id", `atk_spike_${num.index+1}`);
spike_tracker.setAttribute("style", `max-width: 96px; color: yellow; font-weight: bold;`);
spike_tracker.setAttribute("class", "fade in");
lstage.appendChild(spike_tracker);
let atkdiv = document.createElement("div");
atkdiv.setAttribute("style", `max-width: 96px; max-height: ${MAX_HEIGHT}px; overflow: hidden;`);
atkdiv.setAttribute("id", `atk_div_${num.index+1}`);
lstage.appendChild(atkdiv);
}
if(typeof trim != "function"){var trim=a=>{a=a.slice(0,-1);a=a.substr(a.indexOf("{")+1);return a}}
if(typeof getArgs != "function"){
var getArgs=a=>{
let args = a.toString().match(/function\s*(?:[_a-zA-Z]\w*\s*)?\(((?:(?:[_a-zA-Z]\w*)\s*,\s*?)*(?:[_a-zA-Z]\w*)?)\)/);
if (args.length > 1) return args[1].split(/\s*,\s*/g);
return [];
}
}
let displayActionText = function() {
try{
let names = ["", "", "Single", "Double", "Triple", "Quad", "T‑Spin Mini", "T‑Spin", "T‑Spin Mini Single", "T‑Spin Single", "T‑Spin Double", "T‑Spin Triple", "Perfect Clear!", "Combo/Ren", "Multi"];
let playerNum;
let parseCanvasName = function(name) {
let number = name.match(/(\d+)$/);
if (number === null) return 1; // no number, assume is first player
return parseInt(number[0]);
}
switch(this.v.constructor.name) {
case "Ctx2DView":
case "View":
playerNum = parseCanvasName(this.v.ctx.canvas.id) - 1;
break;
case "WebGLView":
playerNum = parseCanvasName(this.v.ctxs[0].elem.id) - 1;
break;
case "SlotView":
console.log(JSON.stringify(this.v.displayer));
playerNum = (this.v.displayer) ? this.v.displayer.index : -1;
break;
default:
console.log("Uhoh looks like something unknown happened >.<");
break;
}
if (this.GameStats.ordered[0].initialVal != this.GameStats.ordered[0].value && playerNum != -1) {
if (!this.displayer) {
this.displayer = window.displayerManager.displayers[playerNum];
}
this.displayer.displayNewAction(atk + cba, ((b2b && this.isBack2Back) ? "B2B " : "") + names[type] + ((cmb > 0) ? ` combo${cmb}` : ""));
}
} catch(e) {console.log(e);}
}
let replacer = function(match, p1, p2, p3) {return match + `let atk = ${p1}; let cba = ${p2}; let type = ${p3}.type; let b2b = ${p3}.b2b; let cmb = ${p3}.cmb;` + trim(displayActionText.toString());}
try {
GameCore.prototype.checkLineClears = new Function(...getArgs(GameCore.prototype.checkLineClears), trim(GameCore.prototype.checkLineClears.toString()).replace(/(_0x[a-f0-9]+x[a-f0-9]+)\+ (_0x[a-f0-9]+x[a-f0-9]+);let (_0x[a-f0-9]+x[a-f0-9]+)=\{type:_0x[a-f0-9]+x[a-f0-9]+,b2b:this\[_0x[a-f0-9]+\[\d+]],cmb:this\[_0x[a-f0-9]+\[\d+]]};/, replacer));
} catch(e) {
console.error(e);
console.error("Could not inject into line clears!");
}
try{
Replayer.prototype.checkLineClears = function(a) {
GameCore.prototype.checkLineClears.call(this, a);
}
Replayer.prototype.restart = new Function(...getArgs(Replayer.prototype.restart), `try{if(this.v.displayer && this.v.displayer.reset) this.v.displayer.reset()}catch(e) {console.error(e);}` + trim(Replayer.prototype.restart.toString()));
} catch(e) {
console.error(e);
console.error("Could not inject into line clears!");
}
try {
SlotView.prototype.onResized = function() {
this.block_size = this.slot.gs.liveBlockSize;
this.holdQueueBlockSize = this.slot.gs.holdQueueBlockSize;
this.drawBgGrid();
this.clearMainCanvas();
if (this.slot.gs.isExtended) {
this.QueueHoldEnabled = true;
this.holdCanvas.style.display = 'block';
this.queueCanvas.style.display = 'block';
if (this.holdCanvas.width > 70) {
if (this.displayer === undefined) {
this.displayer = window.displayerManager.createDisplayer();
}
try {
let top = this.holdCanvas.height + parseInt(this.holdCanvas.style.top);
let left = parseInt(this.holdCanvas.style.left);
if (!document.getElementById(`atk_spike_${this.displayer.index+1}`)) {
let spike_tracker = document.createElement("p");
spike_tracker.setAttribute("class", "layer fade in");
spike_tracker.setAttribute("style", `top: ${top}px; left: ${left}px; width: ${this.holdCanvas.width}px; height: 20px; color: yellow; font-weight: bold;`);
spike_tracker.setAttribute("id", `atk_spike_${this.displayer.index+1}`);
this.holdCanvas.parentNode.appendChild(spike_tracker);
}
if (!document.getElementById(`atk_div_${this.displayer.index+1}`)) {
let atkdiv = document.createElement("div");
atkdiv.setAttribute("class", "layer");
atkdiv.setAttribute("style", `top: ${top+40}px; left: ${left}px; width: ${this.holdCanvas.width}px; max-height: ${MAX_HEIGHT}px; overflow: hidden;`);
atkdiv.setAttribute("id", `atk_div_${this.displayer.index+1}`);
this.holdCanvas.parentNode.appendChild(atkdiv);
}
} catch (e) {console.error(e);}
}
} else {
this.QueueHoldEnabled = false;
this.holdCanvas.style.display = 'none';
this.queueCanvas.style.display = 'none';
console.log("You are using the Display Attack plugin, which will only function IF H&Q is on.");
}
};
} catch (e) {
console.error(e);
console.error("Could not inject into SlotView!");
}
});
})();