Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Owot Wiper (converted for tw.2s4.me) — Full Movable HUD with working engine and fallback renderer
- (function () {
- 'use strict';
- // ---- Utilities & Fallback drawing (writeCharAt/writer) ----
- // We'll provide a fallback renderer that draws to a fixed-position canvas
- // if the environment does not provide writeCharToXY.
- function createRenderCanvas() {
- let render = document.getElementById('solar-wiper-render');
- if (!render) {
- render = document.createElement('canvas');
- render.id = 'solar-wiper-render';
- Object.assign(render.style, {
- position: 'fixed',
- top: '0',
- left: '0',
- width: '100%',
- height: '100%',
- pointerEvents: 'none',
- zIndex: '9998',
- });
- document.body.appendChild(render);
- }
- const setSize = () => {
- render.width = window.innerWidth;
- render.height = window.innerHeight;
- };
- setSize();
- window.addEventListener('resize', setSize);
- return render;
- }
- // Fallback writer using a render canvas — draws glyphs at pixel positions computed from a char grid.
- function fallbackWriteChar(char, fgColor, x, y, bgColor, cellW = 10, cellH = 18, fontSize = 16) {
- const canvas = createRenderCanvas();
- const ctx = canvas.getContext('2d');
- // If background specified, fill that cell
- if (typeof bgColor === 'number') {
- ctx.fillStyle = '#' + bgColor.toString(16).padStart(6, '0');
- ctx.fillRect(x * cellW, y * cellH, cellW, cellH);
- }
- if (char && char !== ' ') {
- ctx.font = `${fontSize}px monospace`;
- ctx.textBaseline = 'top';
- ctx.fillStyle = '#' + (typeof fgColor === 'number' ? fgColor.toString(16).padStart(6, '0') : fgColor);
- ctx.fillText(char, x * cellW, y * cellH);
- }
- }
- // Expose a safe writer function that prefers global writeCharToXY if available
- function safeWriteChar(char, fgColor, x, y, bgColor) {
- try {
- if (typeof writeCharToXY === 'function') {
- // original environment function: writeCharToXY(char, fg, x, y, bg)
- writeCharToXY(char, fgColor, x, y, bgColor);
- } else if (typeof window.writeCharAt === 'function') {
- // user-provided writeCharAt
- window.writeCharAt(char, fgColor, x, y, bgColor);
- } else {
- // fallback to canvas renderer
- fallbackWriteChar(char, fgColor, x, y, bgColor);
- }
- } catch (e) {
- // if any error, try fallback
- fallbackWriteChar(char, fgColor, x, y, bgColor);
- }
- }
- // ---- Main class ----
- function initSolarWiper() {
- if (typeof api_chat_send === 'function') {
- api_chat_send("Thank you for using solar wiper demo. Enjoy the full HUD!");
- }
- class OWOTSolarWiper {
- constructor() {
- // core state
- this.isWiping = false;
- this.stopRequested = false;
- this.wipeText = '█';
- this.wipeColor = 0xFFFF00;
- this.bgColor = 0x000000;
- this.startX = 0;
- this.startY = 0;
- this.endX = 100;
- this.endY = 40;
- this.delay = 10; // ms
- this.batchSize = 10;
- this.charsWritten = 0;
- this.totalChars = 0;
- this.thinkMode = false;
- this.followMode = false;
- this.fastMode = false;
- this.advancedMode = false;
- this.reverseDirection = false;
- this.randomDelay = false;
- // preview & render canvases
- this.previewOverlay = null;
- this.renderCanvas = createRenderCanvas();
- // create UI + preview
- this.createUI();
- this.createPreviewOverlay();
- // expose globally
- window.OWOTSolarWiperInstance = this;
- // keep preview in sync on resize
- window.addEventListener('resize', () => this.updatePreview());
- }
- // ----- UI -----
- createUI() {
- const existing = document.getElementById('solar-wiper-hud');
- if (existing) existing.remove();
- const ui = document.createElement('div');
- ui.id = 'solar-wiper-hud';
- ui.style.cssText = `
- position: fixed; top: 20px; right: 20px;
- background: #1a1a1a; color: #e0e0e0; padding: 12px;
- border-radius: 8px; box-shadow: 0 6px 24px rgba(0,0,0,0.6);
- z-index: 10010; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
- width: 320px; border:1px solid #333; font-size:12px;
- `;
- ui.innerHTML = `
- <div id="hud-header" style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid #333;">
- <div style="font-weight:600;font-size:13px;color:#fff;">Solar Wiper Demo <span style="font-weight:400;color:#888;font-size:11px;">v1.0</span></div>
- <div style="display:flex;gap:6px;align-items:center;">
- <button id="hud-toggle" title="Collapse/Expand" style="background:transparent;border:1px solid #444;color:#888;width:26px;height:26px;border-radius:5px;cursor:pointer;">−</button>
- <button id="hud-close" title="Close HUD" style="background:transparent;border:1px solid #444;color:#888;width:26px;height:26px;border-radius:5px;cursor:pointer;">×</button>
- </div>
- </div>
- <div id="hud-body" style="display:block; gap:8px;">
- <div>
- <label style="display:block;font-size:10px;color:#aaa;margin-bottom:4px;">Character</label>
- <input id="wipe-text" value="█" style="width:100%;padding:6px;border-radius:6px;border:1px solid #333;background:#0d0d0d;color:#fff;">
- </div>
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
- <div>
- <label style="display:block;font-size:10px;color:#aaa;margin-bottom:4px;">Foreground</label>
- <input id="fg-color" type="color" value="#ffff00" style="width:100%;height:32px;border-radius:6px;border:1px solid #333;">
- </div>
- <div>
- <label style="display:block;font-size:10px;color:#aaa;margin-bottom:4px;">Background</label>
- <input id="bg-color" type="color" value="#000000" style="width:100%;height:32px;border-radius:6px;border:1px solid #333;">
- </div>
- </div>
- <div style="background:#0d0d0d;padding:8px;border-radius:6px;border:1px solid #282828;margin-top:8px;">
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;">
- <div>
- <label style="display:block;font-size:9px;color:#666;">START X</label>
- <input id="start-x" type="number" value="0" style="width:100%;padding:6px;border-radius:4px;background:#111;color:#fff;border:1px solid #333;">
- </div>
- <div>
- <label style="display:block;font-size:9px;color:#666;">START Y</label>
- <input id="start-y" type="number" value="0" style="width:100%;padding:6px;border-radius:4px;background:#111;color:#fff;border:1px solid #333;">
- </div>
- </div>
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:6px;">
- <div>
- <label style="display:block;font-size:9px;color:#666;">END X</label>
- <input id="end-x" type="number" value="100" style="width:100%;padding:6px;border-radius:4px;background:#111;color:#fff;border:1px solid #333;">
- </div>
- <div>
- <label style="display:block;font-size:9px;color:#666;">END Y</label>
- <input id="end-y" type="number" value="40" style="width:100%;padding:6px;border-radius:4px;background:#111;color:#fff;border:1px solid #333;">
- </div>
- </div>
- <div style="display:flex;gap:6px;margin-top:8px;">
- <button id="use-cursor-start" style="flex:1;padding:6px;border-radius:6px;border:1px solid #333;background:#222;color:#fff;cursor:pointer;">Cursor → Start</button>
- <button id="use-cursor-end" style="flex:1;padding:6px;border-radius:6px;border:1px solid #333;background:#222;color:#fff;cursor:pointer;">Cursor → End</button>
- </div>
- </div>
- <div style="display:flex;flex-direction:column;gap:6px;">
- <label style="display:flex;align-items:center;padding:6px;border-radius:6px;background:#0d0d0d;border:1px solid #282828;cursor:pointer;"><input id="think-mode" type="checkbox" style="margin-right:8px;">Think Mode (spiral)</label>
- <label style="display:flex;align-items:center;padding:6px;border-radius:6px;background:#0d0d0d;border:1px solid #282828;cursor:pointer;"><input id="follow-mode" type="checkbox" style="margin-right:8px;">Follow Mode (smart)</label>
- <label style="display:flex;align-items:center;padding:6px;border-radius:6px;background:#0d0d0d;border:1px solid #282828;cursor:pointer;"><input id="fast-mode" type="checkbox" style="margin-right:8px;">Fast Mode (batch)</label>
- </div>
- <label style="display:flex;align-items:center;gap:8px;margin-top:6px;"><input id="advanced-toggle" type="checkbox"> Show advanced options</label>
- <div id="advanced-options" style="display:none;background:#0d0d0d;padding:8px;border-radius:6px;border:1px solid #282828;">
- <label style="display:block;font-size:11px;color:#ddd;margin-bottom:6px;">Batch Size<input id="batch-size" type="number" value="10" min="1" style="width:100%;margin-top:6px;padding:6px;border-radius:6px;background:#111;border:1px solid #333;color:#fff;"></label>
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;">
- <label style="display:block;font-size:11px;color:#ddd;">Min Delay<input id="min-delay" type="number" value="0" style="width:100%;margin-top:6px;padding:6px;border-radius:6px;background:#111;border:1px solid #333;color:#fff;"></label>
- <label style="display:block;font-size:11px;color:#ddd;">Max Delay<input id="max-delay" type="number" value="100" style="width:100%;margin-top:6px;padding:6px;border-radius:6px;background:#111;border:1px solid #333;color:#fff;"></label>
- </div>
- <label style="display:flex;align-items:center;margin-top:6px;"><input id="reverse-direction" type="checkbox" style="margin-right:8px;">Reverse direction</label>
- <label style="display:flex;align-items:center;margin-top:4px;"><input id="random-delay" type="checkbox" style="margin-right:8px;">Randomize delay</label>
- </div>
- <div style="display:flex;gap:8px;margin-top:8px;">
- <button id="start-wipe" style="flex:1;padding:10px;border-radius:6px;border:none;background:#fff;color:#000;font-weight:600;cursor:pointer;">Start Wipe</button>
- <button id="stop-wipe" style="flex:1;padding:10px;border-radius:6px;border:none;background:#c0392b;color:#fff;font-weight:600;display:none;cursor:pointer;">Stop</button>
- </div>
- <div id="progress-wrap" style="display:none;margin-top:8px;">
- <div style="background:#0d0d0d;border-radius:6px;height:8px;overflow:hidden;">
- <div id="progress-bar" style="width:0%;height:100%;background:#2ecc71;transition:width 0.12s;"></div>
- </div>
- <div id="progress-text" style="text-align:center;color:#aaa;font-size:11px;margin-top:6px;">0 / 0</div>
- </div>
- <div id="hud-status" style="margin-top:8px;padding:8px;background:#0d0d0d;border-radius:6px;text-align:center;color:#aaa;font-size:12px;">Ready</div>
- </div>
- `;
- document.body.appendChild(ui);
- // draggable header
- this.makeHUDDraggable(ui);
- // collapse/close
- document.getElementById('hud-toggle').onclick = () => {
- const body = document.getElementById('hud-body');
- if (body.style.display === 'none') { body.style.display = 'block'; document.getElementById('hud-toggle').textContent = '−'; }
- else { body.style.display = 'none'; document.getElementById('hud-toggle').textContent = '+'; }
- };
- document.getElementById('hud-close').onclick = () => { ui.remove(); this.hidePreview(); };
- // wire inputs
- document.getElementById('wipe-text').oninput = (e) => this.wipeText = e.target.value || '█';
- document.getElementById('fg-color').oninput = (e) => this.wipeColor = parseInt(e.target.value.slice(1), 16);
- document.getElementById('bg-color').oninput = (e) => this.bgColor = parseInt(e.target.value.slice(1), 16);
- const sx = document.getElementById('start-x'), sy = document.getElementById('start-y'), ex = document.getElementById('end-x'), ey = document.getElementById('end-y');
- sx.oninput = (e) => this.startX = parseInt(e.target.value) || 0;
- sy.oninput = (e) => this.startY = parseInt(e.target.value) || 0;
- ex.oninput = (e) => this.endX = parseInt(e.target.value) || 0;
- ey.oninput = (e) => this.endY = parseInt(e.target.value) || 0;
- document.getElementById('use-cursor-start').onclick = () => {
- if (typeof cursorCoords === 'undefined') { this.updateStatus('cursorCoords not found', '#c0392b'); return; }
- const x = cursorCoords[0] * 16 + cursorCoords[2];
- const y = cursorCoords[1] * 8 + cursorCoords[3];
- sx.value = x; sy.value = y; sx.oninput({target:{value:x}}); sy.oninput({target:{value:y}});
- this.updatePreview();
- };
- document.getElementById('use-cursor-end').onclick = () => {
- if (typeof cursorCoords === 'undefined') { this.updateStatus('cursorCoords not found', '#c0392b'); return; }
- const x = cursorCoords[0] * 16 + cursorCoords[2];
- const y = cursorCoords[1] * 8 + cursorCoords[3];
- ex.value = x; ey.value = y; ex.oninput({target:{value:x}}); ey.oninput({target:{value:y}});
- this.updatePreview();
- };
- document.getElementById('think-mode').onchange = (e) => { this.thinkMode = e.target.checked; if (e.target.checked) { document.getElementById('follow-mode').checked = false; this.followMode = false; } };
- document.getElementById('follow-mode').onchange = (e) => { this.followMode = e.target.checked; if (e.target.checked) { document.getElementById('think-mode').checked = false; this.thinkMode = false; } };
- document.getElementById('fast-mode').onchange = (e) => { this.fastMode = e.target.checked; };
- document.getElementById('advanced-toggle').onchange = (e) => {
- this.advancedMode = e.target.checked;
- document.getElementById('advanced-options').style.display = e.target.checked ? 'block' : 'none';
- };
- document.getElementById('batch-size').oninput = (e) => { this.batchSize = Math.max(1, parseInt(e.target.value) || 1); };
- document.getElementById('min-delay').oninput = (e) => { document.getElementById('min-delay').value = e.target.value; };
- document.getElementById('max-delay').oninput = (e) => { document.getElementById('max-delay').value = e.target.value; };
- document.getElementById('reverse-direction').onchange = (e) => { this.reverseDirection = e.target.checked; };
- document.getElementById('random-delay').onchange = (e) => { this.randomDelay = e.target.checked; };
- // Start / stop
- document.getElementById('start-wipe').onclick = () => this.startWipe();
- document.getElementById('stop-wipe').onclick = () => this.stopWipe();
- // Prevent HUD from stealing key events from the main page
- ui.addEventListener('keydown', (e) => e.stopPropagation());
- ui.addEventListener('keyup', (e) => e.stopPropagation());
- ui.addEventListener('keypress', (e) => e.stopPropagation());
- }
- makeHUDDraggable(hud) {
- const header = hud.querySelector('#hud-header');
- if (!header) return;
- let isDragging = false;
- let offsetX = 0, offsetY = 0;
- header.style.cursor = 'move';
- header.onmousedown = (e) => {
- isDragging = true;
- offsetX = e.clientX - hud.getBoundingClientRect().left;
- offsetY = e.clientY - hud.getBoundingClientRect().top;
- document.body.style.userSelect = 'none';
- };
- document.addEventListener('mouseup', () => { isDragging = false; document.body.style.userSelect = ''; });
- document.addEventListener('mousemove', (e) => {
- if (!isDragging) return;
- hud.style.left = (e.clientX - offsetX) + 'px';
- hud.style.top = (e.clientY - offsetY) + 'px';
- hud.style.right = 'auto';
- });
- }
- createPreviewOverlay() {
- const overlay = document.getElementById('solar-wiper-preview') || document.createElement('canvas');
- overlay.id = 'solar-wiper-preview';
- overlay.style.position = 'fixed';
- overlay.style.top = '0';
- overlay.style.left = '0';
- overlay.style.pointerEvents = 'none';
- overlay.style.zIndex = '10005';
- overlay.style.display = 'none';
- // Append if not present
- if (!overlay.parentElement) document.body.appendChild(overlay);
- // proper resolution
- const resize = () => {
- overlay.width = window.innerWidth;
- overlay.height = window.innerHeight;
- };
- resize();
- window.addEventListener('resize', resize);
- this.previewOverlay = overlay;
- }
- showPreview() {
- if (this.previewOverlay) {
- this.previewOverlay.style.display = 'block';
- this.updatePreview();
- }
- }
- hidePreview() {
- if (this.previewOverlay) this.previewOverlay.style.display = 'none';
- }
- updatePreview() {
- const canvas = this.previewOverlay;
- if (!canvas || canvas.style.display === 'none') return;
- const ctx = canvas.getContext('2d');
- canvas.width = window.innerWidth;
- canvas.height = window.innerHeight;
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- // defensive about environment variables
- const charW = (typeof cellW !== 'undefined') ? cellW : 10;
- const charH = (typeof cellH !== 'undefined') ? cellH : 18;
- const posX = (typeof positionX !== 'undefined') ? positionX : 0;
- const posY = (typeof positionY !== 'undefined') ? positionY : 0;
- const zoom = (typeof zoom !== 'undefined') ? zoom : 1;
- const minX = Math.min(this.startX, this.endX);
- const maxX = Math.max(this.startX, this.endX);
- const minY = Math.min(this.startY, this.endY);
- const maxY = Math.max(this.startY, this.endY);
- const screenX1 = (minX * charW - posX) * zoom;
- const screenY1 = (minY * charH - posY) * zoom;
- const screenX2 = ((maxX + 1) * charW - posX) * zoom;
- const screenY2 = ((maxY + 1) * charH - posY) * zoom;
- // Draw translucent fill
- ctx.fillStyle = 'rgba(155,89,182,0.08)';
- ctx.fillRect(screenX1, screenY1, screenX2 - screenX1, screenY2 - screenY1);
- // dashed border
- ctx.lineWidth = 2;
- ctx.setLineDash([8, 6]);
- ctx.strokeStyle = '#e74c3c';
- ctx.beginPath();
- ctx.moveTo(screenX1, screenY1);
- ctx.lineTo(screenX2, screenY1);
- ctx.lineTo(screenX2, screenY2);
- ctx.lineTo(screenX1, screenY2);
- ctx.closePath();
- ctx.stroke();
- // corners
- ctx.setLineDash([]);
- ctx.fillStyle = '#e74c3c';
- ctx.beginPath();
- ctx.arc(screenX1, screenY1, 6, 0, Math.PI * 2);
- ctx.fill();
- ctx.fillStyle = '#3498db';
- ctx.beginPath();
- ctx.arc(screenX2, screenY2, 6, 0, Math.PI * 2);
- ctx.fill();
- // text label
- ctx.font = 'bold 12px Arial';
- ctx.fillStyle = '#9b59b6';
- ctx.fillText(`${maxX - minX + 1} × ${maxY - minY + 1}`, screenX1 + 6, screenY1 - 8);
- }
- updateStatus(msg, color = '#888') {
- const el = document.getElementById('hud-status');
- if (el) { el.textContent = msg; el.style.color = color; }
- }
- updateProgress() {
- const bar = document.getElementById('progress-bar');
- const text = document.getElementById('progress-text');
- if (!bar || !text) return;
- const pct = (this.totalChars === 0) ? 0 : Math.round((this.charsWritten / this.totalChars) * 100);
- bar.style.width = pct + '%';
- text.textContent = `${this.charsWritten} / ${this.totalChars}`;
- }
- // ----- Wipe algorithms -----
- async startWipe() {
- if (this.isWiping) return;
- // ensure coordinates valid
- if (this.startX > this.endX) { const t = this.startX; this.startX = this.endX; this.endX = t; document.getElementById('start-x').value = this.startX; document.getElementById('end-x').value = this.endX; }
- if (this.startY > this.endY) { const t = this.startY; this.startY = this.endY; this.endY = t; document.getElementById('start-y').value = this.startY; document.getElementById('end-y').value = this.endY; }
- // set delays from advanced options
- const minDelay = parseInt(document.getElementById('min-delay').value) || 0;
- const maxDelay = parseInt(document.getElementById('max-delay').value) || 100;
- this.delay = Math.max(0, Math.min(this.delay || 10, maxDelay));
- this.batchSize = Math.max(1, parseInt(document.getElementById('batch-size').value) || 1);
- // UI
- this.isWiping = true;
- this.stopRequested = false;
- document.getElementById('start-wipe').style.display = 'none';
- document.getElementById('stop-wipe').style.display = 'block';
- document.getElementById('progress-wrap').style.display = 'block';
- this.charsWritten = 0;
- this.totalChars = 0;
- this.updateStatus('Preparing wipe...', '#888');
- // Choose workflow
- const minX = Math.min(this.startX, this.endX);
- const maxX = Math.max(this.startX, this.endX);
- const minY = Math.min(this.startY, this.endY);
- const maxY = Math.max(this.startY, this.endY);
- if (this.followMode) {
- await this.followWipe(minX, maxX, minY, maxY);
- } else if (this.thinkMode) {
- await this.thinkWipe(minX, maxX, minY, maxY);
- } else {
- await this.dynamicSideWipe(minX, maxX, minY, maxY);
- }
- this.isWiping = false;
- document.getElementById('start-wipe').style.display = 'block';
- document.getElementById('stop-wipe').style.display = 'none';
- this.updateStatus(this.stopRequested ? `Stopped at ${this.charsWritten}` : `Wipe complete: ${this.charsWritten}`, '#888');
- }
- async processCells(cells) {
- // cells: array of {x,y}
- this.totalChars = cells.length;
- this.charsWritten = 0;
- this.updateProgress();
- let index = 0;
- const batchSize = this.fastMode ? this.batchSize : 1;
- while (index < cells.length && !this.stopRequested) {
- const batchEnd = Math.min(index + batchSize, cells.length);
- for (let i = index; i < batchEnd; i++) {
- const cell = cells[i];
- // cycle through characters if multiple provided
- const charIndex = i % this.wipeText.length;
- const ch = this.wipeText[charIndex] || '█';
- safeWriteChar(ch, this.wipeColor, cell.x, cell.y, this.bgColor);
- this.charsWritten++;
- }
- index = batchEnd;
- this.updateProgress();
- // compute delay
- let d = this.delay || 10;
- if (this.randomDelay) {
- const minD = parseInt(document.getElementById('min-delay').value) || 0;
- const maxD = parseInt(document.getElementById('max-delay').value) || 100;
- d = Math.floor(Math.random() * (maxD - minD + 1)) + minD;
- }
- // await next tick
- await this.sleep(d);
- }
- }
- async dynamicSideWipe(minX, maxX, minY, maxY) {
- this.updateStatus('Computing dynamic sides...', '#888');
- await this.sleep(20);
- const width = maxX - minX + 1;
- const height = maxY - minY + 1;
- const maxDim = Math.max(width, height);
- const numSides = Math.max(4, 4 + Math.floor(maxDim / 20));
- // generate perimeter start points
- const perimeter = 2 * (width + height);
- const step = perimeter / numSides;
- const starts = [];
- for (let i = 0; i < numSides; i++) {
- let d = i * step;
- let px, py;
- if (d < width) { px = minX + Math.floor(d); py = minY; }
- else if (d < width + height) { px = maxX; py = minY + Math.floor(d - width); }
- else if (d < 2 * width + height) { px = maxX - Math.floor(d - (width + height)); py = maxY; }
- else { px = minX; py = maxY - Math.floor(d - (2 * width + height)); }
- starts.push({ x: px, y: py });
- }
- // compute distance to nearest start for each cell
- const cells = [];
- for (let y = minY; y <= maxY; y++) {
- for (let x = minX; x <= maxX; x++) {
- let minDist = Infinity;
- for (const p of starts) {
- const dist = Math.hypot(x - p.x, y - p.y);
- if (dist < minDist) minDist = dist;
- }
- cells.push({ x, y, dist: minDist });
- }
- }
- // sort by distance then group and shuffle each group
- cells.sort((a, b) => a.dist - b.dist);
- const grouped = [];
- let group = [], last = Math.floor(cells[0] ? cells[0].dist : 0);
- for (const c of cells) {
- const d = Math.floor(c.dist);
- if (d !== last) {
- OWOTSolarWiper.shuffleArray(group);
- grouped.push(...group);
- group = [];
- last = d;
- }
- group.push({ x: c.x, y: c.y });
- }
- if (group.length) { OWOTSolarWiper.shuffleArray(group); grouped.push(...group); }
- // optional reverse
- if (this.reverseDirection) grouped.reverse();
- this.updateStatus(`Wiping ${grouped.length} cells...`, '#888');
- await this.processCells(grouped);
- }
- async followWipe(minX, maxX, minY, maxY) {
- this.updateStatus('Scanning for non-empty cells...', '#888');
- await this.sleep(10);
- const cellsToWipe = [];
- for (let y = minY; y <= maxY; y++) {
- for (let x = minX; x <= maxX; x++) {
- let shouldWipe = true;
- try {
- if (typeof getChar === 'function') {
- const tileX = Math.floor(x / 16);
- const tileY = Math.floor(y / 8);
- const charX = ((x % 16) + 16) % 16;
- const charY = ((y % 8) + 8) % 8;
- const data = getChar(tileX, tileY, charX, charY);
- if (!data) shouldWipe = false;
- else {
- const ch = data.char ?? (Array.isArray(data) ? data[0] : '') ?? '';
- const color = data.color ?? (Array.isArray(data) ? data[1] : 0);
- if (ch === '' || ch === ' ' || ch === '\x00' || color === 0xffffff || color === 16777215) {
- shouldWipe = false;
- }
- }
- } else {
- // if no getChar, assume we want to wipe everything
- shouldWipe = true;
- }
- } catch (e) {
- shouldWipe = true;
- }
- if (shouldWipe) cellsToWipe.push({ x, y });
- }
- }
- this.updateStatus(`Found ${cellsToWipe.length} cells`, '#888');
- await this.processCells(cellsToWipe);
- }
- async thinkWipe(minX, maxX, minY, maxY) {
- this.updateStatus('Computing spiral path...', '#888');
- await this.sleep(20);
- const path = [];
- let left = minX, right = maxX, top = minY, bottom = maxY;
- while (left <= right && top <= bottom) {
- for (let x = left; x <= right; x++) path.push({ x, y: top });
- top++;
- for (let y = top; y <= bottom; y++) path.push({ x: right, y });
- right--;
- if (top <= bottom) {
- for (let x = right; x >= left; x--) path.push({ x, y: bottom });
- bottom--;
- }
- if (left <= right) {
- for (let y = bottom; y >= top; y--) path.push({ x: left, y });
- left++;
- }
- }
- if (this.reverseDirection) path.reverse();
- this.updateStatus(`Path length ${path.length}`, '#888');
- await this.processCells(path);
- }
- stopWipe() {
- this.stopRequested = true;
- this.updateStatus('Stop requested...', '#c0392b');
- }
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- static shuffleArray(arr) {
- for (let i = arr.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1));
- [arr[i], arr[j]] = [arr[j], arr[i]];
- }
- }
- }
- // expose class and instance
- window.OWOTSolarWiper = OWOTSolarWiper;
- window.OWOTSolarWiperInstance = new OWOTSolarWiper();
- }
- // initialize when ready
- if (document.readyState === 'complete' || document.readyState === 'interactive') {
- setTimeout(initSolarWiper, 0);
- } else {
- window.addEventListener('load', initSolarWiper);
- }
- })();
Advertisement