Skip to content

Instantly share code, notes, and snippets.

@nabbynz
Last active February 20, 2025 02:48
Show Gist options
  • Save nabbynz/4a231295bef494d4c5de3637fb8c41f5 to your computer and use it in GitHub Desktop.
Save nabbynz/4a231295bef494d4c5de3637fb8c41f5 to your computer and use it in GitHub Desktop.
Alternative Pizza Respawn Warnings
// ==UserScript==
// @name Alternative Pizza Respawn Warnings
// @description An alternative to the default Pizza Tile Respawn Warnings
// @version 2.1.0
// @match *://*.koalabeast.com/game
// @match *://*.koalabeast.com/game?*
// @updateURL https://gist.github.com/nabbynz/4a231295bef494d4c5de3637fb8c41f5/raw/Alternative_Pizza_Respawn_Warnings.user.js
// @downloadURL https://gist.github.com/nabbynz/4a231295bef494d4c5de3637fb8c41f5/raw/Alternative_Pizza_Respawn_Warnings.user.js
// @grant none
// @author nabby
// ==/UserScript==
console.log('START: ' + GM_info.script.name + ' (v' + GM_info.script.version + ' by ' + GM_info.script.author + ')');
'use strict';
/* eslint-env jquery */
/* globals tagpro, tagproConfig, PIXI */
/* eslint-disable no-multi-spaces */
// use false or '' to not apply that style.
// use 'auto' for text_color to match the texture pack color.
const tilesToRedraw = {
'5.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // neutral boosts
'14.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // red boosts
'15.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // blue boosts
'6.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // juke juice
'6.2': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // rolling bomb
'6.3': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // tagpro
'6.4': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // max speed
'10.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // bombs
'13.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // neutral portals
'24.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // red portals
'25.1': { circle_stroke_color: '#555555', circle_fill_color: '#111111', text_color: 'auto', text_background_color: '#000000', text_background_opacity: 0.8 }, // blue portals
};
tagpro.ready(function() {
let redrawBaseDynamicTiles = function() {
const height = Object.keys(tilesToRedraw).length * 40;
const canvas = createCanvas(480, height);
const ctx = canvas.getContext('2d');
const start = 1.5 * Math.PI;
const portion = 2 * Math.PI / 12;
let y = 0;
for (let tileId in tilesToRedraw) {
for (let i = 1; i <= 12; i++) {
const suffix = tileId.slice(-1) * 100 + i;
const fullTileId = Math.floor(tileId) + '.' + suffix;
const x = (i - 1) * 40;
ctx.save();
if (tilesToRedraw[tileId].circle_fill_color) {
ctx.fillStyle = tilesToRedraw[tileId].circle_fill_color;
ctx.beginPath();
ctx.arc(x + 20, y + 20, 15, 0, Math.PI * 2);
ctx.fill();
}
if (tilesToRedraw[tileId].circle_stroke_color) {
ctx.strokeStyle = tilesToRedraw[tileId].circle_stroke_color;
ctx.lineWidth = 2;
ctx.setLineDash([4, 4]);
ctx.beginPath();
ctx.arc(x + 20, y + 20, 15.5, 0, Math.PI * 2);
ctx.stroke();
ctx.closePath();
ctx.setLineDash([]);
}
if (tilesToRedraw[tileId].text_color) { // draw 5, 4, 3, 2, 1 text
const secondsLeft = Math.floor((14 - i) / 2);
ctx.drawImage(PIXI.utils.TextureCache[fullTileId].baseTexture.resource.source, PIXI.utils.TextureCache[fullTileId].orig.x, PIXI.utils.TextureCache[fullTileId].orig.y, PIXI.utils.TextureCache[fullTileId].orig.width, PIXI.utils.TextureCache[fullTileId].orig.height, x, y, 40, 40);
if (tilesToRedraw[tileId].text_background_color && tilesToRedraw[tileId].text_background_opacity > 0) {
ctx.fillStyle = tilesToRedraw[tileId].text_background_color;
ctx.globalAlpha = tilesToRedraw[tileId].text_background_opacity;
ctx.beginPath();
ctx.arc(x + 20, y + 20, 10, 0, Math.PI * 2);
ctx.fill();
}
if (i > 2) { // skips the first 500ms
ctx.fillStyle = tilesToRedraw[tileId].text_color === 'auto' ? (tilesToRedraw[tileId].average_color ? tilesToRedraw[tileId].average_color : '#cccccc') : tilesToRedraw[tileId].text_color;
ctx.globalAlpha = 1;
ctx.font = 'bold 21px monospace';
ctx.textAlign = 'center';
ctx.fillText(secondsLeft, x + 20, y + 27);
}
} else { // draw pizza segments...
ctx.beginPath();
ctx.moveTo(x + 20, y + 20);
ctx.arc(x + 20, y + 20, 15, start, start + i * portion);
ctx.lineTo(x + 20, y + 20);
ctx.clip();
ctx.drawImage(PIXI.utils.TextureCache[fullTileId].baseTexture.resource.source, PIXI.utils.TextureCache[fullTileId].orig.x, PIXI.utils.TextureCache[fullTileId].orig.y, PIXI.utils.TextureCache[fullTileId].orig.width, PIXI.utils.TextureCache[fullTileId].orig.height, x, y, 40, 40);
}
ctx.restore();
delete tagpro.tiles[fullTileId].slices; // prevents the default pizzas
}
y += 40;
}
let baseTexture = PIXI.Texture.from(canvas);
y = 0;
for (let tileId in tilesToRedraw) {
for (let i = 1; i <= 12; i++) {
const suffix = tileId.slice(-1) * 100 + i;
const fullTileId = Math.floor(tileId) + '.' + suffix;
const x = (i - 1) * 40;
let texture = new PIXI.Texture(baseTexture, new PIXI.Rectangle(x, y, 40, 40));
texture.isAPRW = true;
if (PIXI.utils.TextureCache[fullTileId]) {
PIXI.Texture.removeFromCache(fullTileId);
}
PIXI.Texture.addToCache(texture, fullTileId);
}
y += 40;
}
};
let getTexturePackAverageColors = function() {
for (let tileId in tilesToRedraw) {
let textureCacheId = tileId;
if (tileId === '5.1' || tileId === '14.1' || tileId === '15.1' || tileId === '13.1' || tileId === '24.1' || tileId === '25.1') {
textureCacheId += '01';
} else if (tileId === '10.1') {
textureCacheId = '10';
}
if (textureCacheId) {
const tile = PIXI.utils.TextureCache[textureCacheId];
if (tile) {
const canvas = tile.baseTexture.resource.source;
if (canvas && (canvas instanceof HTMLCanvasElement || canvas instanceof OffscreenCanvas)) {
let ctx = canvas.getContext('2d', { willReadFrequently: true });
let pixelData = ctx.getImageData(tile.frame.x, tile.frame.y, tile.frame.width, tile.frame.height).data;
tilesToRedraw[tileId].average_color = getAverageColor(pixelData, true, true, 1);
}
}
}
}
};
setTimeout(() => { // <--- need a trigger here when ??? is ready
let doGetTexturePackAverageColors = false;
for (let tileId in tilesToRedraw) {
if (tilesToRedraw[tileId].text_color === 'auto') {
doGetTexturePackAverageColors = true;
break;
}
}
let start = function() {
document.removeEventListener('visibilitychange', start);
if (tagpro.isPTM === 'loading') { // need to wait for "Preset Texture Mods" to finish (if installed)
setTimeout(start, 100);
return;
}
if (doGetTexturePackAverageColors) {
getTexturePackAverageColors();
}
redrawBaseDynamicTiles();
};
if (document.visibilityState === 'hidden') {
document.addEventListener('visibilitychange', start);
} else {
start();
}
}, 1000);
}); // tagpro.ready()
function createCanvas(width, height, forceDOM = false) {
if (!forceDOM && typeof OffscreenCanvas !== 'undefined') {
return new OffscreenCanvas(width, height); // An 'OffscreenCanvas' is smaller and faster than a normal canvas.
} else {
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
return canvas;
}
}
function getAverageColor(data, asHex = false, ignoreGrey = false, pixelInterval = 1) {
let rgb = { r: null, g: null, b: null };
let count = 0;
let i = -4;
while ((i += pixelInterval * 4) < data.length) {
// const luminance = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114; // perceptive luminance (/ 255)
const isGrey = ignoreGrey && (Math.abs(data[i] - data[i + 1]) + Math.abs(data[i + 1] - data[i + 2]) < 30);
if (!isGrey && data[i + 3]) {
rgb.r += data[i];
rgb.g += data[i + 1];
rgb.b += data[i + 2];
count++;
}
}
if (count === 0) { // transparent or all grey
return null;
}
rgb.r = Math.floor(rgb.r / count);
rgb.g = Math.floor(rgb.g / count);
rgb.b = Math.floor(rgb.b / count);
if (asHex) {
return '#' + tagpro.renderer.rgbToHex(rgb.r, rgb.g, rgb.b).slice(2);
} else {
return rgb;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment