mirror of
https://github.com/SwingTheVine/Wplace-BlueMarble.git
synced 2026-03-11 17:15:38 +00:00
Finished multi-tile templates
This commit is contained in:
parent
031f114433
commit
7c54997b73
8 changed files with 45 additions and 135 deletions
4
dist/BlueMarble.user.js
vendored
4
dist/BlueMarble.user.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -35,7 +35,7 @@
|
|||
<a href="https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/LICENSE.txt" target="_blank"><img alt="Software License: MPL-2.0" src="https://img.shields.io/badge/Software_License-MPL--2.0-brightgreen?style=flat"></a>
|
||||
<a href="https://discord.gg/tpeBPy46hf" target="_blank"><img alt="Contact Me" src="https://img.shields.io/badge/Contact_Me-gray?style=flat&logo=Discord&logoColor=white&logoSize=auto&labelColor=cornflowerblue"></a>
|
||||
<a href="" target="_blank"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-59hrs_0mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
|
||||
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-428-black?style=flat"></a>
|
||||
<a href="" target="_blank"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-434-black?style=flat"></a>
|
||||
<a href="" target="_blank"><img alt="Total Lines of Code" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=code"></a>
|
||||
<a href="" target="_blank"><img alt="Total Comments" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=comments"></a>
|
||||
<a href="" target="_blank"><img alt="Compression" src="https://img.shields.io/badge/Compression-73.03%25-blue"></a>
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "wplace-bluemarble",
|
||||
"version": "0.65.74",
|
||||
"version": "0.65.80",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wplace-bluemarble",
|
||||
"version": "0.65.74",
|
||||
"version": "0.65.80",
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"terser": "^5.43.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wplace-bluemarble",
|
||||
"version": "0.65.74",
|
||||
"version": "0.65.80",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "node build/build.js",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name Blue Marble
|
||||
// @namespace https://github.com/SwingTheVine/
|
||||
// @version 0.65.74
|
||||
// @version 0.65.80
|
||||
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
|
||||
// @author SwingTheVine
|
||||
// @license MPL-2.0
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ export default class Template {
|
|||
console.log(`Draw Size X: ${drawSizeX}\nDraw Size Y: ${drawSizeY}`);
|
||||
|
||||
// Change the canvas size and wipe the canvas
|
||||
const canvasWidth = (drawSizeX * shreadSize) + (this.coords[2] * shreadSize);
|
||||
const canvasHeight = (drawSizeY * shreadSize) + (this.coords[3] * shreadSize);
|
||||
const canvasWidth = (drawSizeX * shreadSize) + ((pixelX % this.tileSize) * shreadSize);
|
||||
const canvasHeight = (drawSizeY * shreadSize) + ((pixelY % this.tileSize) * shreadSize);
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
|
||||
|
|
|
|||
24
src/main.js
24
src/main.js
|
|
@ -165,7 +165,7 @@ document.head.appendChild(stylesheetLink);
|
|||
// CONSTRUCTORS
|
||||
const observers = new Observers(); // Constructs a new Observers object
|
||||
const overlay = new Overlay(name, version); // Constructs a new Overlay object
|
||||
const templateManager = new TemplateManager(name, version); // Constructs a new TemplateManager object
|
||||
const templateManager = new TemplateManager(name, version, overlay); // Constructs a new TemplateManager object
|
||||
const apiManager = new ApiManager(templateManager); // Constructs a new ApiManager object
|
||||
|
||||
overlay.setApiManager(apiManager); // Sets the API manager
|
||||
|
|
@ -201,12 +201,12 @@ function buildOverlayMain() {
|
|||
.addHr().buildElement()
|
||||
|
||||
.addDiv({'id': 'bm-contain-automation'})
|
||||
.addCheckbox({'id': 'bm-input-stealth', 'textContent': 'Stealth', 'checked': true}).buildElement()
|
||||
.addButtonHelp({'title': 'Waits for the website to make requests, instead of sending requests.'}).buildElement()
|
||||
.addBr().buildElement()
|
||||
.addCheckbox({'id': 'bm-input-possessed', 'textContent': 'Possessed', 'checked': true}).buildElement()
|
||||
.addButtonHelp({'title': 'Controls the website as if it were possessed.'}).buildElement()
|
||||
.addBr().buildElement()
|
||||
// .addCheckbox({'id': 'bm-input-stealth', 'textContent': 'Stealth', 'checked': true}).buildElement()
|
||||
// .addButtonHelp({'title': 'Waits for the website to make requests, instead of sending requests.'}).buildElement()
|
||||
// .addBr().buildElement()
|
||||
// .addCheckbox({'id': 'bm-input-possessed', 'textContent': 'Possessed', 'checked': true}).buildElement()
|
||||
// .addButtonHelp({'title': 'Controls the website as if it were possessed.'}).buildElement()
|
||||
// .addBr().buildElement()
|
||||
.addDiv({'id': 'bm-contain-coords'})
|
||||
.addButton({'id': 'bm-button-coords', 'className': 'bm-help', 'style': 'margin-top: 0;', 'innerHTML': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4 6"><circle cx="2" cy="2" r="2"></circle><path d="M2 6 L3.7 3 L0.3 3 Z"></path><circle cx="2" cy="2" r="0.7" fill="white"></circle></svg></svg>'},
|
||||
(instance, button) => {
|
||||
|
|
@ -256,15 +256,15 @@ function buildOverlayMain() {
|
|||
instance.handleDisplayStatus(`Drew to canvas!`);
|
||||
}
|
||||
}).buildElement()
|
||||
.addButton({'id': 'bm-button-disable', 'textContent': 'Disable'}).buildElement()
|
||||
// .addButton({'id': 'bm-button-disable', 'textContent': 'Disable'}).buildElement()
|
||||
.buildElement()
|
||||
.addTextarea({'id': overlay.outputStatusId, 'placeholder': `Status: Sleeping...\nVersion: ${version}`, 'readOnly': true}).buildElement()
|
||||
.addDiv({'id': 'bm-contain-buttons-action'})
|
||||
.addDiv()
|
||||
.addButton({'id': 'bm-button-teleport', 'className': 'bm-help', 'textContent': '✈'}).buildElement()
|
||||
.addButton({'id': 'bm-button-favorite', 'className': 'bm-help', 'innerHTML': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="10,2 12,7.5 18,7.5 13.5,11.5 15.5,18 10,14 4.5,18 6.5,11.5 2,7.5 8,7.5" fill="white"></polygon></svg>'}).buildElement()
|
||||
.addButton({'id': 'bm-button-templates', 'className': 'bm-help', 'innerHTML': '🖌'}).buildElement()
|
||||
.addButton({'id': 'bm-button-convert', 'className': 'bm-help', 'innerHTML': '🎨'},
|
||||
// .addButton({'id': 'bm-button-teleport', 'className': 'bm-help', 'textContent': '✈'}).buildElement()
|
||||
// .addButton({'id': 'bm-button-favorite', 'className': 'bm-help', 'innerHTML': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="10,2 12,7.5 18,7.5 13.5,11.5 15.5,18 10,14 4.5,18 6.5,11.5 2,7.5 8,7.5" fill="white"></polygon></svg>'}).buildElement()
|
||||
// .addButton({'id': 'bm-button-templates', 'className': 'bm-help', 'innerHTML': '🖌'}).buildElement()
|
||||
.addButton({'id': 'bm-button-convert', 'className': 'bm-help', 'innerHTML': '🎨', 'title': 'Template Color Converter'},
|
||||
(instance, button) => {
|
||||
button.addEventListener('click', () => {
|
||||
window.open('https://pepoafonso.github.io/color_converter_wplace/', '_blank', 'noopener noreferrer');
|
||||
|
|
|
|||
|
|
@ -37,11 +37,12 @@ export default class TemplateManager {
|
|||
/** The constructor for the {@link TemplateManager} class.
|
||||
* @since 0.55.8
|
||||
*/
|
||||
constructor(name, version) {
|
||||
constructor(name, version, overlay) {
|
||||
|
||||
// Meta
|
||||
this.name = name; // Name of userscript
|
||||
this.version = version; // Version of userscript
|
||||
this.overlay = overlay; // The main instance of the Overlay class
|
||||
this.templatesVersion = '1.0.0'; // Version of JSON schema
|
||||
this.userID = null; // The ID of the current user
|
||||
this.encodingBase = '!#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'; // Characters to use for encoding/decoding
|
||||
|
|
@ -116,18 +117,19 @@ export default class TemplateManager {
|
|||
* @param {File} blob - The file blob to create a template from
|
||||
* @param {string} name - The display name of the template
|
||||
* @param {Array<number, number, number, number>} coords - The coordinates of the top left corner of the template
|
||||
* @since 0.65.77
|
||||
*/
|
||||
async createTemplate(blob, name, coords) {
|
||||
|
||||
// Creates the JSON object if it does not already exist
|
||||
if (!this.templatesJSON) {this.templatesJSON = await this.createJSON(); console.log(`Creating JSON...`);}
|
||||
|
||||
console.log(`Awaiting creation...`);
|
||||
this.overlay.handleDisplayStatus(`Creating template at ${coords.join(', ')}...`);
|
||||
|
||||
// Creates a new template instance
|
||||
const template = new Template({
|
||||
displayName: name,
|
||||
sortID: Object.keys(this.templatesJSON.templates).length || 0,
|
||||
sortID: 0, // Object.keys(this.templatesJSON.templates).length || 0, // Uncomment this to enable multiple templates (1/2)
|
||||
authorID: numberToEncoded(this.userID || 0, this.encodingBase),
|
||||
file: blob,
|
||||
coords: coords
|
||||
|
|
@ -142,8 +144,11 @@ export default class TemplateManager {
|
|||
"tiles": template.chunked
|
||||
};
|
||||
|
||||
this.templatesArray = []; // Remove this to enable multiple templates (2/2)
|
||||
this.templatesArray.push(template); // Pushes the Template object instance to the Template Array
|
||||
|
||||
this.overlay.handleDisplayStatus(`Template created at ${coords.join(', ')}!`);
|
||||
|
||||
console.log(Object.keys(this.templatesJSON.templates).length);
|
||||
console.log(this.templatesJSON);
|
||||
console.log(this.templatesArray);
|
||||
|
|
@ -160,11 +165,22 @@ export default class TemplateManager {
|
|||
*/
|
||||
deleteTemplate() {
|
||||
|
||||
}
|
||||
|
||||
/** Disables the template from view
|
||||
*/
|
||||
async disableTemplate() {
|
||||
|
||||
// Creates the JSON object if it does not already exist
|
||||
if (!this.templatesJSON) {this.templatesJSON = await this.createJSON(); console.log(`Creating JSON...`);}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/** Draws all templates on that tile
|
||||
* @param {File} tileBlob - The pixels that are placed on a tile
|
||||
* @param {[number, number]} tileCoords - The tile coordinates [x, y]
|
||||
* @since 0.65.77
|
||||
*/
|
||||
async drawTemplateOnTile(tileBlob, tileCoords) {
|
||||
|
||||
|
|
@ -200,6 +216,10 @@ export default class TemplateManager {
|
|||
.filter(Boolean);
|
||||
|
||||
console.log(templateBlobs);
|
||||
|
||||
if (templateBlobs.length > 0) {
|
||||
this.overlay.handleDisplayStatus(`Displaying ${templateBlobs.length} template${templateBlobs.length == 1 ? '' : 's'}.`);
|
||||
}
|
||||
|
||||
const tileBitmap = await createImageBitmap(tileBlob);
|
||||
|
||||
|
|
@ -243,114 +263,4 @@ export default class TemplateManager {
|
|||
#parseOSU() {
|
||||
|
||||
}
|
||||
|
||||
/** Sets the template to the image passed in.
|
||||
* @param {File} file - The file of the template image.
|
||||
* @since 0.55.8
|
||||
* @deprecated Since 0.65.43
|
||||
*/
|
||||
setTemplateImage(file) {
|
||||
|
||||
this.template = file;
|
||||
this.templateState = 'file';
|
||||
|
||||
// const url = URL.createObjectURL(file); // Creates a blob URL
|
||||
// window.open(url, '_blank'); // Opens a new tab with blob
|
||||
// setTimeout(() => URL.revokeObjectURL(url), 10000); // Destroys the blob 10 seconds later
|
||||
}
|
||||
|
||||
/** Draws the template on the tile.
|
||||
* @param {File|Blob} tileBlob - The blob of the tile
|
||||
* @param {Array<number, number, number, number>} [coordsTilePixel=[0,0,0,0]] - A number array of the four coordinates
|
||||
* @returns {File|Blob} A image/png blob file
|
||||
* @since 0.63.59
|
||||
* @deprecated Since 0.65.43
|
||||
*/
|
||||
async drawTemplate(tileBlob, coordsTilePixel=[0, 0, 0, 0]) {
|
||||
|
||||
// Only continue if template state is NOT 'file' NOR 'template'
|
||||
if (!((this.templateState == 'file') || (this.templateState == 'template'))) {return;}
|
||||
|
||||
const tileSize = 1000; // Pixels in a tile
|
||||
const drawMult = 3; // Multiplier of draw size
|
||||
const drawSize = tileSize * drawMult; // Draw multiplier
|
||||
|
||||
coordsTilePixel = !!coordsTilePixel?.length ? coordsTilePixel : [0, 0, 0, 0]; // Set to default if [] passed in
|
||||
|
||||
console.log(this.template);
|
||||
// If the template has already been drawn, don't draw it again
|
||||
const templateBitmap = this.templateState == 'template' ? this.template : await createImageBitmap(await this.shreadBlob(this.template));
|
||||
const tileBitmap = await createImageBitmap(tileBlob);
|
||||
|
||||
const canvas = new OffscreenCanvas(drawSize, drawSize);
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
context.imageSmoothingEnabled = false; // Nearest neighbor
|
||||
|
||||
// Tells the canvas to ignore anything outside of this area
|
||||
context.beginPath();
|
||||
context.rect(0, 0, drawSize, drawSize);
|
||||
context.clip();
|
||||
|
||||
context.clearRect(0, 0, drawSize, drawSize); // Draws transparent background
|
||||
context.drawImage(tileBitmap, 0, 0, drawSize, drawSize);
|
||||
context.drawImage(templateBitmap, coordsTilePixel[2]*3, coordsTilePixel[3]*3);
|
||||
|
||||
const final = await canvas.convertToBlob({ type: 'image/png' });
|
||||
|
||||
// If the template is not drawn yet...
|
||||
if (this.templateState != 'template') {
|
||||
// (99% chance templateState is 'file')
|
||||
this.template = templateBitmap; // Store the drawn template
|
||||
this.templateState = 'template'; // Indicate that the template has been drawn
|
||||
|
||||
// const url = URL.createObjectURL(final); // Creates a blob URL
|
||||
// window.open(url, '_blank'); // Opens a new tab with blob
|
||||
// setTimeout(() => URL.revokeObjectURL(url), 10000); // Destroys the blob 10 seconds later
|
||||
}
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
/** Shreads the blob so that every pixel is surrounded by adjacent transparent pixels
|
||||
* @param {Blob|File} blob - The blob to manipulate
|
||||
* @param {number} [shrinkFactor=3] - An odd number that will place each pixel in the center. Even numbers will be increased by 1
|
||||
* @param {string} [fileType='image/png'] - The File type to output as. PNG is one of the few transparent types.
|
||||
* @returns {Promise<File>} - A Promise that resolved to a image/png file blob
|
||||
* @since 0.63.37
|
||||
*/
|
||||
async shreadBlob(blob, shreadSize = 3, fileType = 'image/png') {
|
||||
|
||||
const bitmap = await createImageBitmap(blob); // Creates a bitmap image
|
||||
|
||||
shreadSize |= 1; // Converts shreadSize to always be odd by forcing the right-most bit to be 1.
|
||||
|
||||
const width = bitmap.width * Math.round(shreadSize); // Width of the canvas based on shread size times blob
|
||||
const height = bitmap.height * Math.round(shreadSize); // Height of the canvas based on shread size times blob
|
||||
|
||||
const canvas = document.createElement('canvas'); // Creates a canvas
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const context = canvas.getContext('2d'); // Gets the context of the canvas
|
||||
context.imageSmoothingEnabled = false; // Nearest Neighbor scaling
|
||||
|
||||
context.drawImage(bitmap, 0, 0, width, height); // Fills the canvas with the blob
|
||||
|
||||
const imageData = context.getImageData(0, 0, width, height); // Data of the image on the canvas
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
// For every pixel...
|
||||
|
||||
// ... Make it transparent unless it is the "center"
|
||||
if ((x % shreadSize !== 1) || (y % shreadSize !== 1)) {
|
||||
const pixelIndex = (y * width + x) * 4; // Find the pixel index in an array where every 4 indexes are 1 pixel
|
||||
imageData.data[pixelIndex + 3] = 0; // Make the pixel transparent on the alpha channel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.putImageData(imageData, 0, 0);
|
||||
return new Promise((resolve) => {canvas.toBlob(resolve, fileType);});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue