Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (async function WallUtilities() {
- // Invert Y helper
- const invertY = (y) => -y;
- // Fallback Coordinate System
- function getTileFromXY(x, y) {
- const tileX = Math.floor((x - 1) / 20) + 1;
- const tileY = Math.floor((y - 1) / 10) + 1;
- return { x: tileX, y: tileY };
- }
- // --- STATE MANAGEMENT ---
- const protectedChunks = {};
- const renderers = ["KiwiTest"];
- // Auto-add script runner to renderers using localStorage
- try {
- let myName = "ScriptRunner";
- if (typeof localStorage !== 'undefined' && localStorage.username) {
- myName = localStorage.username;
- }
- if (!renderers.includes(myName)) renderers.push(myName);
- } catch(e) {}
- let activeTool = null;
- let isInitializing = false;
- let currentTargetTile = { x: 0, y: 0 };
- let lastWorldX = 0;
- let lastWorldY = 0;
- let lineStart = null;
- let circleStart = null;
- // --- CSS STYLES (Dynamic Theme Variables) ---
- const style = document.createElement('style');
- style.innerHTML = `
- :root {
- --wu-r: 220;
- --wu-g: 20;
- --wu-b: 60;
- }
- .wu-glass {
- background: rgba(var(--wu-r), var(--wu-g), var(--wu-b), 0.15);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- border: 1px solid rgba(calc(var(--wu-r) + 35), calc(var(--wu-g) + 130), calc(var(--wu-b) + 90), 0.3);
- box-shadow: 0 4px 30px rgba(var(--wu-r), var(--wu-g), var(--wu-b), 0.1);
- color: #ffcccc;
- font-family: 'Segoe UI', sans-serif;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.2s ease;
- }
- .wu-glass:hover { background: rgba(var(--wu-r), var(--wu-g), var(--wu-b), 0.3); color: #fff; }
- #wu-btn { position: fixed; bottom: 20px; right: 20px; width: 50px; height: 50px; font-weight: bold; font-size: 18px; z-index: 9999; color: #fff;}
- #wu-hotbar {
- position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%);
- display: none; padding: 10px; gap: 10px; z-index: 9998;
- flex-direction: row; align-items: center;
- }
- #wu-hotbar.active { display: flex; }
- #wu-hotbar button { padding: 8px 16px; font-size: 14px; font-weight: 600; }
- #wu-hotbar button.active-tool { background: rgba(var(--wu-r), calc(var(--wu-g) + 60), calc(var(--wu-b) + 20), 0.5); border-color: #fff; color: #fff; }
- #wu-confirm {
- position: fixed; bottom: 140px; left: 50%; transform: translateX(-50%);
- display: none; padding: 10px 20px; gap: 15px; z-index: 9998;
- flex-direction: row; align-items: center; font-size: 14px;
- }
- #wu-confirm.active { display: flex; }
- #wu-confirm button { padding: 6px 12px; background: rgba(var(--wu-r), var(--wu-g), var(--wu-b), 0.2); border-color: rgba(var(--wu-r), var(--wu-g), var(--wu-b), 0.5); color: #fff; }
- #wu-info, #wu-theme {
- position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
- display: none; padding: 20px; text-align: center; z-index: 10000;
- flex-direction: column; gap: 10px; min-width: 250px;
- }
- #wu-info.active, #wu-theme.active { display: flex; }
- #wu-info h3, #wu-theme h3 { margin: 0; color: #ff8888; }
- #wu-info p, #wu-theme label { margin: 5px 0; font-size: 13px; opacity: 0.9; display: flex; justify-content: space-between; align-items: center;}
- #wu-theme input[type="range"] { width: 120px; margin-left: 10px; }
- @keyframes notifAnim {
- 0% { transform: translate(-50%, -100px); opacity: 0; }
- 5% { transform: translate(-50%, 0); opacity: 1; }
- 95% { transform: translate(-50%, 0); opacity: 1; }
- 100% { transform: translate(-50%, -100px); opacity: 0; }
- }
- #wu-notif {
- position: fixed; top: 20px; left: 50%; transform: translateX(-50%);
- padding: 12px 24px; font-size: 13px; z-index: 10001;
- animation: notifAnim 15s ease-in-out forwards;
- pointer-events: none;
- }
- `;
- document.head.appendChild(style);
- // --- HTML ELEMENTS ---
- const wuBtn = document.createElement('button');
- wuBtn.id = 'wu-btn'; wuBtn.className = 'wu-glass'; wuBtn.innerText = 'WU';
- document.body.appendChild(wuBtn);
- const hotbar = document.createElement('div');
- hotbar.id = 'wu-hotbar'; hotbar.className = 'wu-glass';
- hotbar.innerHTML = `
- <button id="btn-prct" class="wu-glass">Prct</button>
- <button id="btn-nprt" class="wu-glass">N-Prt</button>
- <button id="btn-clr" class="wu-glass">Clr</button>
- <button id="btn-line" class="wu-glass">Line</button>
- <button id="btn-circle" class="wu-glass">Circle</button>
- <button id="btn-theme" class="wu-glass">Themes</button>
- <button id="btn-info" class="wu-glass">?</button>
- `;
- document.body.appendChild(hotbar);
- const confirmBox = document.createElement('div');
- confirmBox.id = 'wu-confirm'; confirmBox.className = 'wu-glass';
- confirmBox.innerHTML = `<span id="confirm-coords">Tile: 0, 0</span><button id="btn-confirm" class="wu-glass">Confirm</button>`;
- document.body.appendChild(confirmBox);
- const infoWindow = document.createElement('div');
- infoWindow.id = 'wu-info'; infoWindow.className = 'wu-glass';
- infoWindow.innerHTML = `
- <h3>WallUtilities</h3>
- <p>WallUtilities is on alpha release. expect bugs to occur.</p>
- <p style="font-weight:bold; color:#fff;">v0.1.8 Alpha</p>
- `;
- document.body.appendChild(infoWindow);
- const themeWindow = document.createElement('div');
- themeWindow.id = 'wu-theme'; themeWindow.className = 'wu-glass';
- themeWindow.innerHTML = `
- <h3>UI Theme</h3>
- <label>Red: <input type="range" id="theme-r" min="0" max="255" value="220"></label>
- <label>Green: <input type="range" id="theme-g" min="0" max="255" value="20"></label>
- <label>Blue: <input type="range" id="theme-b" min="0" max="255" value="60"></label>
- `;
- document.body.appendChild(themeWindow);
- const notif = document.createElement('div');
- notif.id = 'wu-notif'; notif.className = 'wu-glass';
- notif.innerText = 'WallUtils. uses v2 logic, so be careful with custom colors!';
- document.body.appendChild(notif);
- setTimeout(() => notif.remove(), 15000); // 15 seconds
- // --- UI INTERACTIONS ---
- wuBtn.onclick = () => hotbar.classList.toggle('active');
- infoWindow.onclick = () => infoWindow.classList.remove('active');
- themeWindow.onclick = (e) => { if(e.target === themeWindow) themeWindow.classList.remove('active'); };
- document.getElementById('btn-info').onclick = (e) => { e.stopPropagation(); infoWindow.classList.add('active'); themeWindow.classList.remove('active'); };
- document.getElementById('btn-theme').onclick = (e) => { e.stopPropagation(); themeWindow.classList.add('active'); infoWindow.classList.remove('active'); };
- // Theme Sliders Logic
- ['theme-r', 'theme-g', 'theme-b'].forEach(id => {
- document.getElementById(id).addEventListener('input', (e) => {
- const color = id.split('-')[1];
- document.documentElement.style.setProperty(`--wu-${color}`, e.target.value);
- });
- });
- const tools = ['prct', 'nprt', 'clr', 'line', 'circle'];
- tools.forEach(tool => {
- document.getElementById(`btn-${tool}`).onclick = (e) => {
- e.stopPropagation();
- if (activeTool === tool) {
- activeTool = null;
- lineStart = null;
- circleStart = null;
- confirmBox.classList.remove('active');
- document.getElementById(`btn-${tool}`).classList.remove('active-tool');
- } else {
- tools.forEach(t => document.getElementById(`btn-${t}`).classList.remove('active-tool'));
- activeTool = tool;
- lineStart = null;
- circleStart = null;
- document.getElementById(`btn-${tool}`).classList.add('active-tool');
- confirmBox.classList.add('active');
- }
- };
- });
- // --- MOUSE TRACKING ---
- document.addEventListener('mousemove', (e) => {
- let mouseX, mouseY;
- if (typeof cursor !== 'undefined' && typeof cursor.x === 'number' && typeof cursor.y === 'number') {
- mouseX = cursor.x;
- mouseY = invertY(cursor.y);
- } else if (typeof w !== 'undefined' && w.mouse && typeof w.mouse.x === 'number') {
- mouseX = w.mouse.x;
- mouseY = w.mouse.y;
- } else {
- mouseX = e.clientX;
- mouseY = e.clientY;
- }
- if (!isNaN(mouseX) && !isNaN(mouseY)) {
- lastWorldX = mouseX;
- lastWorldY = mouseY;
- }
- });
- // 0.25s Coordinate Preview Update
- setInterval(() => {
- if (!activeTool) return;
- if (activeTool === 'line' && lineStart) {
- document.getElementById('confirm-coords').innerText = `Line Start: ${lineStart.x}, ${lineStart.y} | Select End...`;
- return;
- }
- if (activeTool === 'circle' && circleStart) {
- document.getElementById('confirm-coords').innerText = `Circle Center: ${circleStart.x}, ${circleStart.y} | Select Edge...`;
- return;
- }
- const tile = getTileFromXY(lastWorldX, lastWorldY);
- if (tile && !isNaN(tile.x) && !isNaN(tile.y)) {
- currentTargetTile = { x: tile.x, y: tile.y };
- document.getElementById('confirm-coords').innerText = `Tile: ${currentTargetTile.x}, ${currentTargetTile.y} | Pos: ${Math.floor(lastWorldX)}, ${Math.floor(lastWorldY)}`;
- }
- }, 250);
- // --- CONFIRM BUTTON LOGIC ---
- document.getElementById('btn-confirm').onclick = async () => {
- if (!activeTool) return;
- const { x, y } = currentTargetTile;
- const key = `${x},${y}`;
- if (activeTool === 'prct') {
- await protectChunk(x, y);
- } else if (activeTool === 'nprt') {
- // De-protects chunk, removes arrays, but DOES NOT clear the text inside it
- if (protectedChunks[key]) delete protectedChunks[key];
- } else if (activeTool === 'clr') {
- // Clears 20x10 area with blanks REGARDLESS of protection status
- let clearQueue = [];
- for (let cy = 1; cy <= 10; cy++) {
- for (let cx = 1; cx <= 20; cx++) {
- const worldX = (x - 1) * 20 + cx;
- const worldY = (y - 1) * 10 + cy;
- clearQueue.push({ x: worldX, y: invertY(worldY) });
- }
- }
- for (let i = 0; i < clearQueue.length; i += 200) {
- const batch = clearQueue.slice(i, i + 200);
- batch.forEach(p => writeCharAt(' ', 0, p.x, p.y));
- if (i + 200 < clearQueue.length) await new Promise(r => setTimeout(r, 400));
- }
- // If it WAS protected, update the arrays to match the blank state
- if (protectedChunks[key]) {
- protectedChunks[key].chars = Array.from({length: 10}, () => Array(20).fill(' '));
- protectedChunks[key].colors = Array.from({length: 10}, () => Array(20).fill(-1));
- protectedChunks[key].lastModified = Date.now();
- }
- } else if (activeTool === 'line') {
- if (!lineStart) {
- lineStart = { x: Math.floor(lastWorldX), y: Math.floor(lastWorldY) };
- } else {
- const lineEnd = { x: Math.floor(lastWorldX), y: Math.floor(lastWorldY) };
- await drawLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y);
- lineStart = null;
- }
- return;
- } else if (activeTool === 'circle') {
- if (!circleStart) {
- circleStart = { x: Math.floor(lastWorldX), y: Math.floor(lastWorldY) };
- } else {
- const circleEnd = { x: Math.floor(lastWorldX), y: Math.floor(lastWorldY) };
- await drawCircle(circleStart.x, circleStart.y, circleEnd.x, circleEnd.y);
- circleStart = null;
- }
- return;
- }
- activeTool = null;
- confirmBox.classList.remove('active');
- tools.forEach(t => document.getElementById(`btn-${t}`).classList.remove('active-tool'));
- };
- // --- LINE DRAWING ALGORITHM (CID 29) ---
- async function drawLine(x0, y0, x1, y1) {
- const dx = Math.abs(x1 - x0);
- const dy = Math.abs(y1 - y0);
- const sx = (x0 < x1) ? 1 : -1;
- const sy = (y0 < y1) ? 1 : -1;
- let err = dx - dy;
- let queue = [];
- while (true) {
- queue.push({ x: x0, y: y0 });
- if (x0 === x1 && y0 === y1) break;
- const e2 = 2 * err;
- if (e2 > -dy) { err -= dy; x0 += sx; }
- if (e2 < dx) { err += dx; y0 += sy; }
- }
- await batchWrite(queue, '█', 29);
- }
- // --- CIRCLE DRAWING ALGORITHM (CID 29) ---
- async function drawCircle(cx, cy, ex, ey) {
- const r = Math.round(Math.sqrt((ex - cx) ** 2 + (ey - cy) ** 2));
- if (r === 0) {
- await batchWrite([{ x: cx, y: cy }], '█', 29);
- return;
- }
- let queue = [];
- let x = r;
- let y = 0;
- let err = 0;
- // Midpoint circle algorithm
- while (x >= y) {
- queue.push({ x: cx + x, y: cy + y });
- queue.push({ x: cx + y, y: cy + x });
- queue.push({ x: cx - y, y: cy + x });
- queue.push({ x: cx - x, y: cy + y });
- queue.push({ x: cx - x, y: cy - y });
- queue.push({ x: cx - y, y: cy - x });
- queue.push({ x: cx + y, y: cy - x });
- queue.push({ x: cx + x, y: cy - y });
- if (err <= 0) {
- y += 1;
- err += 2 * y + 1;
- }
- if (err > 0) {
- x -= 1;
- err -= 2 * x + 1;
- }
- }
- // Deduplicate points
- const uniqueQueue = Array.from(new Set(queue.map(p => `${p.x},${p.y}`))).map(s => {
- const [px, py] = s.split(',').map(Number);
- return { x: px, y: py };
- });
- await batchWrite(uniqueQueue, '█', 29);
- }
- // --- BATCH WRITER HELPER ---
- async function batchWrite(queue, char, color) {
- for (let i = 0; i < queue.length; i += 200) {
- const batch = queue.slice(i, i + 200);
- batch.forEach(p => writeCharAt(char, color, p.x, invertY(p.y)));
- if (i + 200 < queue.length) await new Promise(r => setTimeout(r, 400));
- }
- }
- // --- CORE PROTECTION LOGIC ---
- async function protectChunk(tileX, tileY) {
- tileX = Math.floor(Number(tileX));
- tileY = Math.floor(Number(tileY));
- if (isNaN(tileX) || isNaN(tileY)) return;
- const key = `${tileX},${tileY}`;
- if (protectedChunks[key]) return;
- isInitializing = true;
- const chars = Array.from({length: 10}, () => Array(20).fill(' '));
- const colors = Array.from({length: 10}, () => Array(20).fill(-1));
- const writeQueue = [];
- for (let cy = 1; cy <= 10; cy++) {
- for (let cx = 1; cx <= 20; cx++) {
- const worldX = (tileX - 1) * 20 + cx;
- const worldY = (tileY - 1) * 10 + cy;
- const invertedY = invertY(worldY);
- // Strict type checking to prevent [object Object] infinite loops
- const info = getCharInfoXY(worldX, invertedY);
- let currentChar = ' ';
- if (info) {
- if (typeof info === 'string') currentChar = info;
- else if (typeof info === 'object' && typeof info.char === 'string') currentChar = info.char;
- }
- let wrapCX = cx > 20 ? 1 : (cx < 1 ? 20 : cx);
- let wrapCY = cy > 10 ? 1 : (cy < 1 ? 10 : cy);
- let currentColor = -1;
- const apiColor = getCharColor(tileX, tileY, wrapCX, wrapCY);
- if (typeof apiColor === 'number' && apiColor !== 0 && apiColor !== 15) currentColor = apiColor;
- else if (info && typeof info === 'object' && typeof info.color === 'number') currentColor = info.color;
- else if (typeof apiColor === 'number') currentColor = apiColor;
- chars[cy-1][cx-1] = currentChar;
- colors[cy-1][cx-1] = currentColor;
- if (currentChar === ' ') {
- chars[cy-1][cx-1] = '█';
- colors[cy-1][cx-1] = 30;
- writeQueue.push({ char: '█', color: 30, x: worldX, y: invertedY });
- }
- }
- }
- protectedChunks[key] = { chars, colors, lastModified: Date.now() };
- for (let i = 0; i < writeQueue.length; i += 200) {
- const batch = writeQueue.slice(i, i + 200);
- batch.forEach(item => writeCharAt(item.char, item.color, item.x, item.y));
- if (i + 200 < writeQueue.length) await new Promise(r => setTimeout(r, 400));
- }
- setTimeout(() => isInitializing = false, 1000);
- }
- // --- 1-SECOND BACKGROUND REGENERATION LOOP ---
- setInterval(async () => {
- if (isInitializing) return;
- let writeQueue = [];
- for (const [key, chunk] of Object.entries(protectedChunks)) {
- // Pause regen if authorized user just typed
- if (Date.now() - chunk.lastModified < 1500) continue;
- const [tileX, tileY] = key.split(',').map(Number);
- for (let cy = 1; cy <= 10; cy++) {
- for (let cx = 1; cx <= 20; cx++) {
- const worldX = (tileX - 1) * 20 + cx;
- const worldY = (tileY - 1) * 10 + cy;
- const invertedY = invertY(worldY);
- const info = getCharInfoXY(worldX, invertedY);
- let currentChar = ' ';
- if (info) {
- if (typeof info === 'string') currentChar = info;
- else if (typeof info === 'object' && typeof info.char === 'string') currentChar = info.char;
- }
- let currentColor = -1;
- const apiColor = getCharColor(tileX, tileY, cx, cy);
- if (typeof apiColor === 'number' && apiColor !== 0 && apiColor !== 15) currentColor = apiColor;
- else if (info && typeof info === 'object' && typeof info.color === 'number') currentColor = info.color;
- else if (typeof apiColor === 'number') currentColor = apiColor;
- const savedChar = chunk.chars[cy-1][cx-1];
- const savedColor = chunk.colors[cy-1][cx-1];
- // Only regenerate if there's an actual mismatch
- let needsCharRegen = (currentChar !== savedChar);
- let needsColorRegen = (savedColor !== -1 && currentColor !== savedColor);
- if (needsCharRegen || needsColorRegen) {
- const finalChar = needsCharRegen ? savedChar : currentChar;
- const finalColor = needsColorRegen ? savedColor : (savedColor === -1 ? currentColor : savedColor);
- writeQueue.push({ char: finalChar, color: finalColor, x: worldX, y: invertedY });
- }
- }
- }
- }
- for (let i = 0; i < writeQueue.length; i += 200) {
- const batch = writeQueue.slice(i, i + 200);
- batch.forEach(item => writeCharAt(item.char, item.color, item.x, item.y));
- if (i + 200 < writeQueue.length) await new Promise(r => setTimeout(r, 400));
- }
- }, 1000);
- // --- EDIT EVENT LISTENER (INSTANT REGENERATION & ARRAY UPDATES) ---
- if (typeof w !== 'undefined' && w.on) {
- w.on("edit", (x, y, char, color, user) => {
- if (isInitializing) return;
- const numX = Number(x);
- const numY = Number(y);
- if (isNaN(numX) || isNaN(numY)) return;
- const tile = getTileFromXY(numX, invertY(numY));
- if (!tile || isNaN(tile.x) || isNaN(tile.y)) return;
- const key = `${Math.floor(tile.x)},${Math.floor(tile.y)}`;
- if (!protectedChunks[key]) return;
- // Safe user check
- const userName = typeof user === 'string' ? user : (user?.name || user?.username || '');
- const isAuthorized = renderers.some(r => userName.toLowerCase().includes(r.toLowerCase()));
- // Exact local coordinates (1 to 20, 1 to 10)
- const cx = numX - (tile.x - 1) * 20;
- const invY = invertY(numY);
- const cy = invY - (tile.y - 1) * 10;
- if (cx >= 1 && cx <= 20 && cy >= 1 && cy <= 10) {
- if (isAuthorized) {
- // Authorized user: Update arrays and timestamp to pause background regen
- protectedChunks[key].chars[cy-1][cx-1] = char;
- protectedChunks[key].colors[cy-1][cx-1] = (color === '' || color === undefined || color === null) ? -1 : color;
- protectedChunks[key].lastModified = Date.now();
- } else {
- // Unauthorized user: Regenerate / Restore from arrays instantly
- const savedChar = protectedChunks[key].chars[cy-1][cx-1];
- const savedColor = protectedChunks[key].colors[cy-1][cx-1];
- if (savedColor !== -1) {
- writeCharAt(savedChar, savedColor, numX, invertY(numY));
- } else {
- writeCharAt(savedChar, color, numX, invertY(numY));
- }
- }
- }
- });
- }
- console.log("%c[WallUtilities v0.1.8 Alpha] Loaded successfully.", "color: #f44; font-weight: bold;");
- })();
Advertisement