// ==UserScript== // @name 7Placer+ // @namespace https://i.imgur.com/n084VLd.png // @version 1.5.0a // @description Pixel place.io bot - an advanced bot that includes image botting, painting, and account management features. (Border drawing feature added) // @author Azti & SamaelWired // @match https://pixelplace.io/* // @require https://update.greasyfork.org/scripts/498080/1395134/Hacktimer.js // @require https://pixelplace.io/js/jquery.min.js?v2=1 // @require https://pixelplace.io/js/jquery-ui.min.js?v2=1 // @grant none // @run-at document-start // @downloadURL none // ==/UserScript== (function() { "use strict"; /* ======================================================================= Canvas Worker - Marks ocean areas (approx #CCCCCC) as -1 when scanning canvas ========================================================================== */ const cloadercode = ` const canvasworkerTimet = performance.now(); const colors = [ 0xFFFFFF, 0xC4C4C4, 0xA6A6A6, 0x888888, 0x6F6F6F, 0x555555, 0x3A3A3A, 0x222222, 0x000000, 0x003638, 0x006600, 0x477050, 0x1B7400, 0x22B14C, 0x02BE01, 0x51E119, 0x94E044, 0x34EB6B, 0x98FB98, 0x75CEA9, 0xCAFF70, 0xFBFF5B, 0xE5D900, 0xFFCC00, 0xC1A162, 0xE6BE0C, 0xE59500, 0xFF7000, 0xFF3904, 0xE50000, 0xCE2939, 0xFF416A, 0x9F0000, 0x4D082C, 0x6B0000, 0x440414, 0xFF755F, 0xA06A42, 0x633C1F, 0x99530D, 0xBB4F00, 0xFFC49F, 0xFFDFCC, 0xFF7EBB, 0xFFA7D1, 0xEC08EC, 0xBB276C, 0xCF6EE4, 0x7D26CD, 0x820080, 0x591C91, 0x330077, 0x020763, 0x5100FF, 0x0000EA, 0x044BFF, 0x013182, 0x005BA1, 0x6583CF, 0x36BAFF, 0x0083C7, 0x00D3DD, 0x45FFC8, 0xB5E8EE ]; const oceanHex = 0xCCCCCC; function isOceanColor(colornum) { const r1 = (colornum >> 16) & 0xFF, g1 = (colornum >> 8) & 0xFF, b1 = colornum & 0xFF; const r2 = (oceanHex >> 16) & 0xFF, g2 = (oceanHex >> 8) & 0xFF, b2 = oceanHex & 0xFF; const dr = r1 - r2, dg = g1 - g2, db = b1 - b2; // Consider it ocean if the sum of squares of differences is less than 100 return (dr*dr + dg*dg + db*db) < 100; } self.addEventListener('message', async (event) => { const imageResponse = await fetch('https://pixelplace.io/canvas/' + event.data + '.png?t200000=' + Date.now()); const imageBlob = await imageResponse.blob(); const imageBitmap = await createImageBitmap(imageBlob); const canvas = new OffscreenCanvas(imageBitmap.width, imageBitmap.height); const ctx = canvas.getContext('2d'); ctx.drawImage(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const pixelData = imageData.data; const CanvasArray = Array.from({ length: canvas.width }, () => Array.from({ length: canvas.height }, () => 50)); for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const pixelIndex = (y * canvas.width + x) * 4; const a = pixelData[pixelIndex + 3]; if (a < 1) { continue; } const r = pixelData[pixelIndex]; const g = pixelData[pixelIndex + 1]; const b = pixelData[pixelIndex + 2]; const colornum = (r << 16) | (g << 8) | b; if (isOceanColor(colornum)) { CanvasArray[x][y] = -1; continue; } const colorIndex = colors.indexOf(colornum); CanvasArray[x][y] = colorIndex; } } self.postMessage(CanvasArray); const canvasworkerendTime = performance.now(); var processingTime = canvasworkerendTime - canvasworkerTimet; self.postMessage(processingTime); }); `; const cloaderblob = new Blob([cloadercode], { type: 'application/javascript' }); const canvasworker = new Worker(URL.createObjectURL(cloaderblob)); function loadcanvas(canvas) { canvasworker.onmessage = function(event) { if (Array.isArray(event.data)) { canvas.CanvasArray = event.data; } else { console.log("[7placer] Processing took: " + Math.round(event.data) + "ms"); } }; canvasworker.postMessage(canvas_Canvas.ID); } /* ======================================================================= Style Settings - Tracker, Drop Area and Canvas Preview ========================================================================== */ const trackercss = { top: '0px', left: '0px', borderColor: 'rgb(138,43,226)', color: 'rgb(138,43,226)', backgroundColor: 'black', opacity: '60%', display: 'none', transition: 'all 0.06s ease-in-out', pointerEvents: 'none' }; const drop = { width: 'calc(100% - 2em)', height: 'calc(100% - 2em)', position: 'fixed', left: '0px', top: '0px', backgroundColor: 'rgba(0, 0, 0, 0.533)', zIndex: '9999', display: 'flex', color: 'white', fontSize: '48pt', justifyContent: 'center', alignItems: 'center', border: '3px white dashed', borderRadius: '18px', margin: '1em' }; const canvascss = { position: 'absolute', pointerEvents: 'none', left: '0px', top: '0px', imageRendering: 'pixelated', opacity: '50%', animation: 'blink 3s ease-out infinite' }; const blink = document.createElement("style"); blink.type = "text/css"; blink.innerText = ` @keyframes blink { 0% { opacity: .30; } 50% { opacity: .10; } 100% { opacity: .30; } } `; document.head.appendChild(blink); /* ======================================================================= Canvas Class ========================================================================== */ class Canvas { constructor() { Canvas.ID = this.ParseID(); Canvas.isProcessed = false; loadcanvas(this); Canvas.customCanvas = this.createPreviewCanvas(); } ParseID() { return parseInt(window.location.href.split("/").slice(-1)[0].split("-")[0]); } static get instance() { if (!Canvas._instance) Canvas._instance = new Canvas(); return Canvas._instance; } set CanvasArray(array) { this._CanvasArray = array; Canvas.isProcessed = true; } get CanvasArray() { return this._CanvasArray; } getColor(x, y) { try { return this._CanvasArray[x][y]; } catch(e) { return 50; } } updatePixel(x, y, color) { if (!Canvas.isProcessed) return; this.CanvasArray[x][y] = color; } createPreviewCanvas() { const canvas = $(``).css(canvascss); $('#canvas').ready(function () { $('#painting-move').append(canvas); }); const ctx = canvas[0].getContext("2d"); return ctx; } } const canvas_Canvas = Canvas; /* ======================================================================= Palive and Time Functions ========================================================================== */ function randomString(charList, num) { return Array.from({ length: num }, () => charList.charAt(Math.floor(Math.random() * charList.length))).join(''); } function randomString1(num) { const charList = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'; return randomString(charList, num); } function randomString2(num) { const charList = 'gmbonjklezcfxta1234567890GMBONJKLEZCFXTA'; return randomString(charList, num); } function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } const paliveCharmap = { "0": "g", "1": "n", "2": "b", "3": "r", "4": "z", "5": "s", "6": "l", "7": "x", "8": "i", "9": "o" }; function getPalive(serverTime, userId) { const tDelay = getTDelay(serverTime); const sequenceLengths = [6, 5, 9, 4, 5, 3, 6, 6, 3]; const currentTimestamp = Math.floor(Date.now() / 1000) + tDelay - 5400; const timestampString = currentTimestamp.toString(); const timestampCharacters = timestampString.split(''); let result = ''; for (let i = 0; i < sequenceLengths.length; i++) { const sequenceNumber = sequenceLengths[i]; result += randInt(0, 1) == 1 ? randomString2(sequenceNumber) : randomString1(sequenceNumber); const letter = paliveCharmap[parseInt(timestampCharacters[i])]; result += randInt(0, 1) == 0 ? letter.toUpperCase() : letter; } result += userId.toString().substring(0, 1) + (randInt(0, 1) == 1 ? randomString2(randInt(4, 20)) : randomString1(randInt(4, 25))); return result + "0="; } function getTDelay(serverTime) { const currentTime = new Date().getTime() / 1e3; return Math.floor(serverTime - currentTime); } /* ======================================================================= Auth and API Functions ========================================================================== */ class Auth { constructor(authObj) { this.authKey = authObj.authKey; this.authId = authObj.authId; this.authToken = authObj.authToken; } } function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } async function getPainting(authId, authKey, authToken) { const originalAuthId = getCookie('authId'); const originalAuthKey = getCookie('authKey'); const originalAuthToken = getCookie('authToken'); document.cookie = `authId=${authId}; path=/`; document.cookie = `authKey=${authKey}; path=/`; document.cookie = `authToken=${authToken}; path=/`; try { const response = await fetch(`https://pixelplace.io/api/get-painting.php?id=${canvas_Canvas.ID}&connected=1`, { headers: { 'Accept': 'application/json, text/javascript, */*; q=0.01' }, credentials: 'include' }); const json = response.json(); return json; } finally { document.cookie = `authId=${originalAuthId}; path=/`; document.cookie = `authKey=${originalAuthKey}; path=/`; document.cookie = `authToken=${originalAuthToken}; path=/`; } } /* ======================================================================= Account Management and Command Functions ========================================================================== */ const window2 = window; var LocalAccounts = new Map(); function storagePush() { const obj = Object.fromEntries(LocalAccounts); localStorage.setItem('LocalAccounts', JSON.stringify(obj)); } function storageGet() { const storedAccounts = localStorage.getItem('LocalAccounts'); if (storedAccounts) { const parsedAccounts = JSON.parse(storedAccounts); LocalAccounts = new Map(Object.entries(parsedAccounts)); } else { LocalAccounts = new Map(); } } async function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function saveAuth(username, authId, authKey, authToken, print = true) { if (!authId || !authKey || !authToken) { console.log('[7p] saveAuth usage: saveAuth(username, authId, authKey, authToken)'); return; } const account = { authId, authKey, authToken }; LocalAccounts.set(username, account); storagePush(); if (print) console.log('Auth saved. Saved list: ', LocalAccounts); } async function getAuth(print = true) { const cookieStore = window2.cookieStore; const authToken = await cookieStore.get("authToken"); const authKey = await cookieStore.get("authKey"); const authId = await cookieStore.get("authId"); if (authToken == null || authKey == null || authId == null) { console.log('[7p] Please login first!'); return; } if (print) console.log(`authId = "${authId.value}", authKey = "${authKey.value}", authToken = "${authToken.value}"`); return { authToken: authToken.value, authKey: authKey.value, authId: authId.value }; } async function saveAccount() { storageGet(); const AuthObj = await getAuth(false); const userinfo = await getPainting(AuthObj.authId, AuthObj.authKey, AuthObj.authToken); saveAuth(userinfo.user.name, AuthObj.authId, AuthObj.authKey, AuthObj.authToken, false); console.log('Auth saved. Saved list: ', LocalAccounts); } function getAccounts() { storageGet(); if (!LocalAccounts || LocalAccounts.size == 0) { console.log('No accounts found'); return; } console.log(`Found ${LocalAccounts.size} accounts`); console.log(LocalAccounts); } function deleteAccount(identifier) { if (identifier == null) { console.log('deleteAccount usage: deleteAccount(user or index)'); return; } storageGet(); if (typeof identifier == 'string') { if (identifier == 'all') { LocalAccounts.forEach((value, key) => { LocalAccounts.delete(key); }); return; } if (!LocalAccounts.has(identifier)) { console.log(`[7p] Error deleting: No account with name ${identifier}`); return; } LocalAccounts.delete(identifier); console.log(`[7p] Deleted account ${identifier}.`); console.log(LocalAccounts); } if (typeof identifier == 'number') { const keys = Array.from(LocalAccounts.keys()); if (identifier > keys.length) { console.log(`[7p] Error deleting: No account with index ${identifier}`); return; } LocalAccounts.delete(keys[identifier]); console.log(`Deleted account ${identifier}`); console.log(LocalAccounts); } storagePush(); } async function connect(username) { storageGet(); const account = LocalAccounts.get(username); const connectedbot = window2.seven.bots.find((bot) => bot.generalinfo && bot.generalinfo.user && bot.generalinfo.user.name == username ); if (!username) { console.log('[7p] Missing bot username, connect("username")'); return; } if (username == 'all') { for (const [username, account] of LocalAccounts) { const connectedbot = window2.seven.bots.find((bot) => bot.generalinfo && bot.generalinfo.user && bot.generalinfo.user.name == username ); const auth = new Auth(account); if (connectedbot) { console.log(`[7p] Account ${username} is already connected.`); continue; } new WSBot(auth, username); await delay(500); } return; } if (!account) { console.log(`[7p] No account found with username ${username}`); return; } if (connectedbot) { console.log(`[7p] Account ${username} is already connected.`); return; } const auth = new Auth(account); new WSBot(auth, username); } function disconnect(username) { const bot = window2.seven.bots.find((bot) => bot.generalinfo && bot.generalinfo.user && bot.generalinfo.user.name == username ); if (!username) { console.log('[7p] disconnect requires a username, disconnect("username")'); return; } if (username == 'all') { if (window2.seven.bots.length == 1) { console.log('[7p] No bots connected.'); return; } for (const bot of window2.seven.bots) { closeBot(bot); } return; } if (!bot) { console.log(`[7p] No bot connected with username ${username}`); return; } closeBot(bot); } /* ======================================================================= Global Variables ========================================================================== */ const variables = window.seven = { bots: [], pixelspeed: 18, queue: [], inprogress: false, protect: false, tickspeed: 1000, order: 'fromCenter', saveAuth: saveAuth, getAuth: getAuth, saveAccount: saveAccount, getAccounts: getAccounts, deleteAccount: deleteAccount, connect: connect, disconnect: disconnect }; /* ======================================================================= WebSocket and Message Handling ========================================================================== */ function onClientMessage(event) { const msg = event.data; const bot = Client.instance; if (msg.startsWith("42")) { const msgData = JSON.parse(event.data.substr(2)); const type = msgData[0]; switch (type) { case "p": for (const pixel of msgData[1]) { const canvas = canvas_Canvas.instance; const x = pixel[0]; const y = pixel[1]; const color = pixel[2]; canvas.updatePixel(x, y, color); } break; case "canvas": for (const pixel of msgData[1]) { const canvas = canvas_Canvas.instance; const x = pixel[0]; const y = pixel[1]; const color = pixel[2]; canvas.updatePixel(x, y, color); } break; } } } async function onBotMessage(event, bot) { const message = event.data; if (message.startsWith("42")) { const msgData = JSON.parse(event.data.substr(2)); const type = msgData[0]; const botid = bot.generalinfo.user.id; const botname = bot.username; switch (type) { case "server_time": bot.paliveServerTime = msgData[1]; break; case "ping.alive": const hash = getPalive(bot.paliveServerTime, botid); console.log('[7p]', botname, ': pong =', hash, botid); bot.emit('pong.alive', `"` + hash + `"`); break; case "throw.error": if (msgData[1] == 49) { console.log(`[7p] [Bot ${botname}] Error (${msgData[1]}): This auth is not valid! Deleting account from saved accounts...`); deleteAccount(botname); closeBot(bot); return; } console.log(`[7p] [Bot ${botname}] Pixelplace WS error: ${msgData[1]}`); break; case "canvas": console.log(`[7p] Successfully connected to bot ${bot.username}`); variables.bots.push(bot); break; } } if (message.startsWith("0")) bot.ws.send('40'); if (message.startsWith("40")) bot.ws.send(`42["init",{"authKey":"${bot.auth.authKey}","authToken":"${bot.auth.authToken}","authId":"${bot.auth.authId}","boardId":${canvas_Canvas.ID}}]`); if (message.startsWith("2")) bot.ws.send('3'); } const customWS = window.WebSocket; window.WebSocket = function(url, protocols) { const client = new Client(); const socket = new customWS(url, protocols); socket.addEventListener("message", (event) => { onClientMessage(event); }); client.ws = socket; return socket; }; async function hookBot(bot) { console.log(`[7p] Attempting to connect account ${bot.username}`); const socket = new customWS("wss://pixelplace.io/socket.io/?EIO=4&transport=websocket"); socket.addEventListener("message", (event) => { onBotMessage(event, bot); }); socket.addEventListener("close", () => { Bot.botIndex -= 1; }); return socket; } function closeBot(bot) { if (bot instanceof Client) return; if (!bot) { console.log('[7placer] Cannot close bot that doesn\'t exist.'); return; } bot.ws.close(); const result = variables.bots.filter((checkedBot) => checkedBot.botid != bot.botid); variables.bots = result; console.log('[7placer] Ended bot ', bot.botid); } /* ======================================================================= Bot Classes – Bot, WSBot and Client ========================================================================== */ class Bot { constructor() { this.trackeriters = 0; this.lastplace = Date.now(); this.botid = Bot.botIndex; Bot.botIndex += 1; } emit(event, params) { this.ws.send(`42["${event}",${params}]`); } async placePixel(x, y, color, client = false, tracker = true) { var tick = 0; while (true) { const canvas = canvas_Canvas.instance; const canvascolor = canvas.getColor(x, y); if (canvascolor == color || canvascolor == -1) return true; if (Date.now() - this.lastplace >= variables.pixelspeed) { this.emit('p', `[${x},${y},${color},1]`); this.lastplace = Date.now(); canvas.updatePixel(x, y, color); if (tracker && this.trackeriters >= 6) { $(this.tracker).css({ top: y, left: x, display: 'block' }); this.trackeriters = 0; } this.trackeriters += 1; return true; } tick += 1; if (tick == variables.tickspeed) { tick = 0; await new Promise(resolve => setTimeout(resolve, 0)); } } } static async findAvailableBot() { const bots = variables.bots; var tick = 0; while (true) { for (var i = 0; i < bots.length; i++) { const bot = bots[i]; if (Date.now() - bot.lastplace >= variables.pixelspeed) { return bot; } } tick += 1; if (tick == variables.tickspeed) { tick = 0; await new Promise(resolve => setTimeout(resolve, 0)); } } } createTracker() { const tracker = $('
').text(`[7P] ${this.username}`).css(trackercss); $('#canvas').ready(function () { $('#painting-move').append(tracker); }); return tracker; } set ws(wss) { this._ws = wss; } get ws() { return this._ws; } } Bot.botIndex = 0; class WSBot extends Bot { constructor(auth, username) { super(); if (!username || !auth) { console.error("[7p ERROR]: 'auth' and 'username' should both be provided."); return; } this._auth = auth; this.username = username; this.startBot(); } async startBot() { this.generalinfo = await getPainting(this.auth.authId, this.auth.authKey, this.auth.authToken); this.tracker = this.createTracker(); this.ws = await hookBot(this); } get auth() { return this._auth; } } class Client extends Bot { constructor() { super(); this.username = 'Client'; Client.instance = this; this.tracker = this.createTracker(); variables.bots.push(this); } static get Client() { return Client.instance; } } /* ======================================================================= Queue Functions (Processing queued pixels and checking canvas state) ========================================================================== */ class Queue { constructor() { } static add(x, y, color) { variables.queue.push({ x: x, y: y, color: color }); } static clear() { variables.queue = []; } static async start() { if (!canvas_Canvas.isProcessed) { console.log('[7p] Error starting queue: Canvas has not been processed yet.'); Queue.stop(); return; } await Queue.sort(); var pos = 0; var tick = 0; variables.inprogress = true; while (variables.inprogress == true && variables.queue.length > 0) { const pixel = variables.queue[pos]; const currentColor = canvas_Canvas.instance.getColor(pixel.x, pixel.y); if (currentColor === pixel.color || currentColor === -1) { pos++; if (pos >= variables.queue.length) { if (variables.protect) pos = 0; else { Queue.stop(); break; } } continue; } const bot = await Bot.findAvailableBot(); await bot.placePixel(pixel.x, pixel.y, pixel.color); pos++; if (!variables.protect && pos == variables.queue.length) { Queue.stop(); break; } else if (variables.protect && pos == variables.queue.length) { pos = 0; } await new Promise(resolve => setTimeout(resolve, 0)); if (tick >= variables.tickspeed) { tick = 0; await new Promise(resolve => setTimeout(resolve, 0)); } tick += 1; } } static async sort() { const array = variables.queue; switch (variables.order) { case 'rand': array.sort(() => Math.random() - 0.5); break; case 'colors': array.sort((a, b) => a.color - b.color); break; case 'vertical': array.sort((a, b) => a.x - b.x); break; case 'horizontal': array.sort((a, b) => a.y - b.y); break; default: case 'circle': const CX = Math.floor((array[0].x + array[array.length - 1].x) / 2); const CY = Math.floor((array[0].y + array[array.length - 1].y) / 2); array.sort((a, b) => { const distanceA = Math.sqrt((a.x - CX) ** 2 + (a.y - CY) ** 2); const distanceB = Math.sqrt((b.x - CX) ** 2 + (b.y - CY) ** 2); return distanceA - distanceB; }); break; } } static stop() { variables.inprogress = false; canvas_Canvas.customCanvas.clearRect(0, 0, 3000, 3000); Queue.clear(); } } const SevenQueue = Queue; /* ======================================================================= Image Processing Tools ========================================================================== */ const colors = [ 0xFFFFFF, 0xC4C4C4, 0xA6A6A6, 0x888888, 0x6F6F6F, 0x555555, 0x3A3A3A, 0x222222, 0x000000, 0x003638, 0x006600, 0x477050, 0x1B7400, 0x22B14C, 0x02BE01, 0x51E119, 0x94E044, 0x34EB6B, 0x98FB98, 0x75CEA9, 0xCAFF70, 0xFBFF5B, 0xE5D900, 0xFFCC00, 0xC1A162, 0xE6BE0C, 0xE59500, 0xFF7000, 0xFF3904, 0xE50000, 0xCE2939, 0xFF416A, 0x9F0000, 0x4D082C, 0x6B0000, 0x440414, 0xFF755F, 0xA06A42, 0x633C1F, 0x99530D, 0xBB4F00, 0xFFC49F, 0xFFDFCC, 0xFF7EBB, 0xFFA7D1, 0xEC08EC, 0xBB276C, 0xCF6EE4, 0x7D26CD, 0x820080, 0x591C91, 0x330077, 0x020763, 0x5100FF, 0x0000EA, 0x044BFF, 0x013182, 0x005BA1, 0x6583CF, 0x36BAFF, 0x0083C7, 0x00D3DD, 0x45FFC8, 0xB5E8EE ]; function getColorDistance(c1, c2) { const r1 = (c1 >> 16) & 0xFF; const g1 = (c1 >> 8) & 0xFF; const b1 = c1 & 0xFF; const r2 = (c2 >> 16) & 0xFF; const g2 = (c2 >> 8) & 0xFF; const b2 = c2 & 0xFF; return (r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2; } function findClosestColor(color) { let minDistance = Infinity; let colorNumber; let index = 0; for (const pxpColor of colors) { const distance = getColorDistance(color, pxpColor); if (distance < minDistance) { minDistance = distance; colorNumber = index; } index += 1; } return colorNumber; } function previewCanvasImage(x, y, image) { const ctx = canvas_Canvas.customCanvas; const img = new Image(); img.onload = function () { ctx.drawImage(img, x, y); }; img.src = URL.createObjectURL(image); } async function ImageToPixels(image) { const result = []; const canvas = new OffscreenCanvas(image.width, image.height); const ctx = canvas.getContext('2d'); ctx.drawImage(image, 0, 0, image.width, image.height); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const pixelData = imageData.data; for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const pixelIndex = (y * canvas.width + x) * 4; const r = pixelData[pixelIndex]; const g = pixelData[pixelIndex + 1]; const b = pixelData[pixelIndex + 2]; const a = pixelData[pixelIndex + 3]; const colornum = (r << 16) | (g << 8) | b; if (a < 1) { continue; } const color = findClosestColor(colornum); result.push({ x, y, color }); } } return result; } async function botImage(x, y, image) { const bitmap = await createImageBitmap(image); const processed = await ImageToPixels(bitmap); previewCanvasImage(x, y, image); processed.forEach((pixel) => SevenQueue.add(pixel.x + x, pixel.y + y, pixel.color)); SevenQueue.start(); } /* ======================================================================= Square Creation Function ========================================================================== */ function BotSquare(x1, y1, x2, y2, color) { if (x2 < x1) [x1, x2] = [x2, x1]; if (y2 < y1) [y1, y2] = [y2, y1]; for (let x = x1; x <= x2; x++) { for (let y = y1; y <= y2; y++) { SevenQueue.add(x, y, color); } } SevenQueue.start(); } /* ======================================================================= Cursor and Selected Color Information ========================================================================== */ function getClientMouse() { const coordinates = $('#coordinates').text(); const [x, y] = coordinates.split(',').map(coord => parseInt(coord.trim())); const selectedcolor = $('#palette-buttons a.selected').data('id'); return [x, y, selectedcolor]; } /* ======================================================================= Image Botting – Drop Area ========================================================================== */ function createDropArea() { const dropobject = $('
').text('Drop Image').css(drop); const [x, y] = getClientMouse(); $('body').append(dropobject); dropobject.on("click", function () { dropobject.remove(); }); dropobject.on("drop", async function (event) { event.preventDefault(); event.stopPropagation(); const image = event.originalEvent.dataTransfer.files[0]; dropobject.remove(); await botImage(x, y, image); }).on('dragover', false); } /* ======================================================================= Keyboard Shortcuts Alt+W: Stop queue Alt+B: Create drop area Alt+X: Draw square from two points Alt+Y: Draw border (1 pixel) around area with ocean and canvas boundaries ========================================================================== */ var coord1 = null; var borderCoord1 = null; $(document).on('keyup', function (event) { if ($(':input[type="text"]').is(':focus')) return; switch (event.which) { case 87: // Alt+W if (!event.altKey) return; SevenQueue.stop(); break; case 66: // Alt+B if (!event.altKey) return; createDropArea(); break; case 88: // Alt+X: Draw square { const [x, y, color] = getClientMouse(); if (coord1 == null) { coord1 = { x: x, y: y }; return; } BotSquare(coord1.x, coord1.y, x, y, color); coord1 = null; } break; case 89: // Alt+Y: Draw border if (!event.altKey) return; { const [xB, yB, selectedColor] = getClientMouse(); if (borderCoord1 == null) { borderCoord1 = { x: xB, y: yB }; return; } // Second point selected, define rectangle area let xMin = Math.min(borderCoord1.x, xB); let xMax = Math.max(borderCoord1.x, xB); let yMin = Math.min(borderCoord1.y, yB); let yMax = Math.max(borderCoord1.y, yB); const canvasInstance = canvas_Canvas.instance; // Check each pixel in the selected area: for (let i = xMin; i <= xMax; i++) { for (let j = yMin; j <= yMax; j++) { // If pixel is not ocean (paintable canvas area) if (canvasInstance.getColor(i, j) !== -1) { let isBorder = false; // Check 4 directional neighbors: if (i - 1 >= 0 && canvasInstance.getColor(i - 1, j) === -1) isBorder = true; if (i + 1 < canvasInstance._CanvasArray.length && canvasInstance.getColor(i + 1, j) === -1) isBorder = true; if (j - 1 >= 0 && canvasInstance.getColor(i, j - 1) === -1) isBorder = true; if (j + 1 < canvasInstance._CanvasArray[0].length && canvasInstance.getColor(i, j + 1) === -1) isBorder = true; // If border pixel, add to queue (1 pixel border) if (isBorder) { SevenQueue.add(i, j, selectedColor); } } } } SevenQueue.start(); borderCoord1 = null; } break; } }); console.log('7Placer Loaded! Version: 1.5.0a'); })();