// ==UserScript== // @name wplace overlay script made by Furry Crew // @namespace https://c11v.dev/userscripts/wplace-overlay // @version 2.3.1 // @description Minimal overlay. No export/import. Camera lock infra kept without UI. GUI can be minimized. // @match https://wplace.live/* // @run-at document-idle // @inject-into page // @grant none // @license GPL-3.0-or-later // @downloadURL https://update.greasyfork.icu/scripts/545754/wplace%20overlay%20script%20made%20by%20Furry%20Crew.user.js // @updateURL https://update.greasyfork.icu/scripts/545754/wplace%20overlay%20script%20made%20by%20Furry%20Crew.meta.js // ==/UserScript== (function () { // single-instance guard const KEY = "__WPO_SIMPLE_SINGLETON__"; try { if (window[KEY]?.destroy) window[KEY].destroy(true); } catch {} const singleton = { destroy: ()=>{} }; window[KEY] = singleton; const LS_KEY = "wplace.overlay.gui.simple.v1"; const WM_TEXT_MAIN = "Made by Furry Crew"; const log = (...a)=>console.log("[wplace-overlay]", ...a); const clamp=(v,a,b)=>Math.min(b,Math.max(a,v)); function toast(msg){ const el=document.createElement("div"); el.textContent=msg; el.style.cssText="position:fixed;left:50%;bottom:16px;transform:translateX(-50%);padding:8px 12px;background:rgba(0,0,0,.85);color:#fff;font:12px system-ui,sans-serif;border-radius:8px;z-index:2147483647"; document.body.appendChild(el); setTimeout(()=>el.remove(),1200); } async function waitForBody(){ if(document.body) return; await new Promise(r=>{ const obs=new MutationObserver(()=>{ if(document.body){ obs.disconnect(); r(); } }); obs.observe(document.documentElement,{childList:true,subtree:true}); }); } let state = { on:true, x:0, y:0, scale:1, rot:0, opacity:0.25, imageInfo:{ w:0, h:0, name:"" }, step:5, lock:false, minimized:false }; try { const prev = JSON.parse(localStorage.getItem(LS_KEY)||"{}"); Object.assign(state, prev); } catch {} function save(){ localStorage.setItem(LS_KEY, JSON.stringify(state)); } // overlay canvas let cvs=null, ctx=null, imgBitmap=null, iw=0, ih=0; function makeCanvas(){ cvs=document.createElement("canvas"); ctx=cvs.getContext("2d"); Object.assign(cvs.style,{ position:"fixed", top:"0", left:"0", width:"100vw", height:"100vh", pointerEvents:"none", zIndex:"2147483647" }); cvs.width=window.innerWidth; cvs.height=window.innerHeight; const onResize=()=>{ cvs.width=innerWidth; cvs.height=innerHeight; draw(); }; window.addEventListener("resize", onResize); cvs.__onResize = onResize; document.body.appendChild(cvs); } function clear(){ if(!ctx) return; ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,cvs.width,cvs.height); } function drawWatermark(){ const pad = 10, baseSize = 14; ctx.save(); ctx.imageSmoothingEnabled=true; ctx.globalAlpha = 0.9; ctx.font = `600 ${baseSize}px ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif`; ctx.textAlign = "left"; ctx.textBaseline = "bottom"; ctx.shadowColor = "rgba(0,0,0,0.7)"; ctx.shadowBlur = 4; ctx.shadowOffsetY = 1; ctx.fillStyle = "white"; ctx.fillText(WM_TEXT_MAIN, 10, cvs.height - pad); ctx.restore(); } function draw(){ if(!ctx) return; clear(); if(imgBitmap && state.on){ const cx=cvs.width/2, cy=cvs.height/2; ctx.save(); ctx.globalAlpha=clamp(state.opacity,0,1); ctx.imageSmoothingEnabled=false; ctx.translate(cx+state.x, cy+state.y); ctx.rotate(state.rot*Math.PI/180); ctx.scale(state.scale, state.scale); ctx.drawImage(imgBitmap, -iw/2, -ih/2); ctx.restore(); } drawWatermark(); } async function loadFromFile(file){ try{ const bmp=await createImageBitmap(file); imgBitmap=bmp; iw=bmp.width; ih=bmp.height; state.imageInfo={ w:iw, h:ih, name:file.name||"" }; draw(); refreshFooter(); toast(`Loaded ${file.name} ${iw}×${ih}`); save(); }catch{ toast("Failed to load file"); } } function chooseImageFile(){ const inp=document.createElement("input"); inp.type="file"; inp.accept="image/png,image/jpeg,image/webp,image/gif"; inp.onchange=()=>{ const f=inp.files && inp.files[0]; if(f) loadFromFile(f); }; inp.click(); } // camera lock infra kept without UI let isLeftDown = false; const lockProxy = document.createElement("div"); Object.assign(lockProxy.style, { position:"fixed", inset:"0", zIndex:"2147483646", pointerEvents:"none" }); lockProxy.onclick = (e)=>{ const target = document.elementFromPoint(e.clientX, e.clientY); if(target && target !== lockProxy){ const evt = new MouseEvent("click", {bubbles:true, cancelable:true, clientX:e.clientX, clientY:e.clientY, button:0}); target.dispatchEvent(evt); } }; ["mousedown","mousemove","mouseup","wheel","contextmenu","touchstart","touchmove","touchend"].forEach(type=>{ lockProxy.addEventListener(type, ev=>{ ev.stopImmediatePropagation(); ev.preventDefault(); }, {passive:false}); }); function applyLockSideEffects(){ lockProxy.style.pointerEvents = state.lock ? "auto" : "none"; } function installLockHandlers(){ window.addEventListener("wheel", (e)=>{ if(!state.lock) return; e.stopImmediatePropagation(); e.preventDefault(); }, {capture:true, passive:false}); window.addEventListener("mousedown", (e)=>{ if(!state.lock) return; isLeftDown = e.button === 0; e.stopImmediatePropagation(); e.preventDefault(); }, {capture:true}); window.addEventListener("mousemove", (e)=>{ if(!state.lock) return; if(isLeftDown){ e.stopImmediatePropagation(); e.preventDefault(); } }, {capture:true}); window.addEventListener("mouseup", (e)=>{ if(!state.lock) return; isLeftDown = false; e.stopImmediatePropagation(); e.preventDefault(); }, {capture:true}); window.addEventListener("touchstart", (e)=>{ if(!state.lock) return; e.stopImmediatePropagation(); e.preventDefault(); }, {capture:true, passive:false}); window.addEventListener("touchmove", (e)=>{ if(!state.lock) return; e.stopImmediatePropagation(); e.preventDefault(); }, {capture:true, passive:false}); window.addEventListener("keydown", (e)=>{ if(!state.lock) return; const k = e.key.toLowerCase(); const block = new Set(["w","a","s","d","arrowup","arrowdown","arrowleft","arrowright","+","-","=","_"," ","pageup","pagedown","home","end"]); if(block.has(k) || (e.ctrlKey && (k==="+" || k==="=" || k==="-" ))){ e.stopImmediatePropagation(); e.preventDefault(); } }, {capture:true}); } installLockHandlers(); // GUI let guiHost=null, shadow=null, keepAliveId=null; function buildGUI(){ guiHost=document.createElement("div"); shadow=guiHost.attachShadow({mode:"open"}); const style=document.createElement("style"); style.textContent=` :host { all: initial; } *, *::before, *::after { box-sizing: border-box; } .panel{ position:fixed; top:16px; right:16px; background:#0f0f10; color:#eaeaea; font:12px system-ui,sans-serif; border:1px solid #222; border-radius:12px; padding:12px; z-index:2147483647; box-shadow:0 12px 24px rgba(0,0,0,.4); user-select:none; width:auto; max-width:min(420px, calc(100vw - 32px)); max-height:calc(100vh - 32px); overflow:auto; } .title{ display:flex; align-items:center; justify-content:space-between; font-weight:700; margin-bottom:8px; cursor:move; gap:8px; } .title-left{ display:flex; align-items:center; gap:8px; } .sublabel{ color:#a9a9a9; font-size:10px; white-space:nowrap; } .title-btn{ margin-left:auto; background:#171718; color:#eaeaea; border:1px solid #2d2d2f; border-radius:8px; padding:4px 8px; cursor:pointer; font:12px system-ui,sans-serif; } .btns{ display:flex; gap:8px; flex-wrap:wrap; margin-bottom:6px; } .row{ display:grid; grid-template-columns:1fr auto; gap:8px; align-items:center; margin:10px 0; } .controls{ display:grid; grid-template-columns: 1fr minmax(68px,92px); gap:8px; grid-column:1 / span 2; align-items:center; } input[type="number"]{ width:100%; min-width:68px; background:#171718; color:#eaeaea; border:1px solid #2d2d2f; border-radius:8px; padding:6px 8px; font:12px system-ui,sans-serif; } input[type="range"]{ width:100%; } button,label{ background:#171718; color:#eaeaea; border:1px solid #2d2d2f; border-radius:8px; padding:6px 8px; cursor:pointer; font:12px system-ui,sans-serif; } button:active{ transform:translateY(1px); } .mono{ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } .footer{ margin-top:8px; border-top:1px solid #1d1d1f; padding-top:6px; display:flex; justify-content:space-between; align-items:center; color:#a6a6a6; font-size:10px; gap:8px; flex-wrap:wrap; } .footer .mono { white-space:nowrap; } .dpad{ display:grid; grid-template-columns:repeat(3,32px); grid-template-rows:repeat(3,32px); gap:6px; } .dpad button{ padding:0; height:32px; width:32px; } .dpad .spacer{ visibility:hidden; } .min-note{ color:#a6a6a6; font-size:10px; } @media (max-width: 420px){ .controls{ grid-template-columns: 1fr minmax(60px,80px); } .dpad{ grid-template-columns:repeat(3,28px); grid-template-rows:repeat(3,28px); } .dpad button{ width:28px; height:28px; } } `; const wrap=document.createElement("div"); wrap.innerHTML=`