Detects movement and draws red boxes, right-click to lock onto targets
Size
11.8 KB
Version
1.0.1
Created
Mar 19, 2026
Updated
28 days ago
1// ==UserScript==
2// @name Veck.io Movement Tracker & Auto-Lock
3// @description Detects movement and draws red boxes, right-click to lock onto targets
4// @version 1.0.1
5// @match https://*.veck.io/*
6// @icon https://veck.io/favicon/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Movement Tracker & Auto-Lock initialized');
12
13 let canvas, ctx;
14 let overlayCanvas;
15 let previousFrame = null;
16 let detectedMovements = [];
17 let lockedTarget = null;
18 let isLocked = false;
19
20 // Configuration
21 const config = {
22 sensitivity: 30, // Movement detection sensitivity (lower = more sensitive)
23 minArea: 100, // Minimum area to consider as movement
24 boxColor: 'rgba(255, 0, 0, 0.8)', // Red box color
25 boxWidth: 3,
26 scanInterval: 50, // Scan every 50ms
27 lockColor: 'rgba(0, 255, 0, 0.8)' // Green for locked target
28 };
29
30 function init() {
31 // Wait for the game canvas to be ready
32 const gameCanvas = document.querySelector('#unity-canvas');
33 if (!gameCanvas) {
34 console.log('Game canvas not found, retrying...');
35 setTimeout(init, 1000);
36 return;
37 }
38
39 canvas = gameCanvas;
40 console.log('Game canvas found:', canvas);
41
42 // Create overlay canvas for drawing boxes
43 createOverlayCanvas();
44
45 // Start movement detection
46 startMovementDetection();
47
48 // Add right-click handler for locking
49 setupLockHandler();
50
51 console.log('Movement tracker fully initialized');
52 }
53
54 function createOverlayCanvas() {
55 overlayCanvas = document.createElement('canvas');
56 overlayCanvas.id = 'movement-overlay';
57 overlayCanvas.style.position = 'absolute';
58 overlayCanvas.style.top = '0';
59 overlayCanvas.style.left = '0';
60 overlayCanvas.style.pointerEvents = 'none';
61 overlayCanvas.style.zIndex = '9999';
62
63 // Match game canvas size
64 const updateSize = () => {
65 const rect = canvas.getBoundingClientRect();
66 overlayCanvas.width = rect.width;
67 overlayCanvas.height = rect.height;
68 overlayCanvas.style.width = rect.width + 'px';
69 overlayCanvas.style.height = rect.height + 'px';
70 overlayCanvas.style.top = rect.top + 'px';
71 overlayCanvas.style.left = rect.left + 'px';
72 };
73
74 updateSize();
75 window.addEventListener('resize', updateSize);
76
77 document.body.appendChild(overlayCanvas);
78 ctx = overlayCanvas.getContext('2d');
79 console.log('Overlay canvas created');
80 }
81
82 function startMovementDetection() {
83 const tempCanvas = document.createElement('canvas');
84 const tempCtx = tempCanvas.getContext('2d');
85
86 setInterval(() => {
87 if (!canvas || !ctx) return;
88
89 try {
90 // Set temp canvas size to match game canvas
91 tempCanvas.width = canvas.width;
92 tempCanvas.height = canvas.height;
93
94 // Capture current frame
95 tempCtx.drawImage(canvas, 0, 0);
96 const currentFrame = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
97
98 if (previousFrame) {
99 // Detect movement between frames
100 detectedMovements = detectMovement(previousFrame, currentFrame, tempCanvas.width, tempCanvas.height);
101
102 // Draw boxes on overlay
103 drawMovementBoxes();
104 }
105
106 previousFrame = currentFrame;
107 } catch (e) {
108 // Canvas might be tainted or not accessible
109 console.error('Error capturing frame:', e);
110 }
111 }, config.scanInterval);
112 }
113
114 function detectMovement(prevFrame, currFrame, width, height) {
115 const movements = [];
116 const diffData = [];
117 const blockSize = 16; // Check in blocks for performance
118
119 // Create difference map
120 for (let y = 0; y < height; y += blockSize) {
121 for (let x = 0; x < width; x += blockSize) {
122 let diff = 0;
123 let count = 0;
124
125 // Sample block
126 for (let by = 0; by < blockSize && y + by < height; by++) {
127 for (let bx = 0; bx < blockSize && x + bx < width; bx++) {
128 const i = ((y + by) * width + (x + bx)) * 4;
129 const rDiff = Math.abs(prevFrame.data[i] - currFrame.data[i]);
130 const gDiff = Math.abs(prevFrame.data[i + 1] - currFrame.data[i + 1]);
131 const bDiff = Math.abs(prevFrame.data[i + 2] - currFrame.data[i + 2]);
132 diff += (rDiff + gDiff + bDiff) / 3;
133 count++;
134 }
135 }
136
137 const avgDiff = diff / count;
138 if (avgDiff > config.sensitivity) {
139 diffData.push({ x, y, diff: avgDiff });
140 }
141 }
142 }
143
144 // Group nearby movement blocks into regions
145 const regions = groupMovementRegions(diffData, blockSize);
146
147 return regions.filter(r => r.area > config.minArea);
148 }
149
150 function groupMovementRegions(blocks, blockSize) {
151 if (blocks.length === 0) return [];
152
153 const regions = [];
154 const visited = new Set();
155
156 blocks.forEach((block, idx) => {
157 if (visited.has(idx)) return;
158
159 const region = {
160 minX: block.x,
161 minY: block.y,
162 maxX: block.x + blockSize,
163 maxY: block.y + blockSize,
164 blocks: [block]
165 };
166
167 // Find connected blocks
168 const queue = [idx];
169 visited.add(idx);
170
171 while (queue.length > 0) {
172 const currentIdx = queue.shift();
173 const current = blocks[currentIdx];
174
175 blocks.forEach((other, otherIdx) => {
176 if (visited.has(otherIdx)) return;
177
178 const distance = Math.sqrt(
179 Math.pow(current.x - other.x, 2) +
180 Math.pow(current.y - other.y, 2)
181 );
182
183 if (distance < blockSize * 3) {
184 visited.add(otherIdx);
185 queue.push(otherIdx);
186 region.blocks.push(other);
187 region.minX = Math.min(region.minX, other.x);
188 region.minY = Math.min(region.minY, other.y);
189 region.maxX = Math.max(region.maxX, other.x + blockSize);
190 region.maxY = Math.max(region.maxY, other.y + blockSize);
191 }
192 });
193 }
194
195 region.width = region.maxX - region.minX;
196 region.height = region.maxY - region.minY;
197 region.area = region.width * region.height;
198 region.centerX = region.minX + region.width / 2;
199 region.centerY = region.minY + region.height / 2;
200
201 regions.push(region);
202 });
203
204 return regions;
205 }
206
207 function drawMovementBoxes() {
208 if (!ctx || !overlayCanvas) return;
209
210 // Clear overlay
211 ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
212
213 // Scale factor between game canvas and overlay
214 const scaleX = overlayCanvas.width / canvas.width;
215 const scaleY = overlayCanvas.height / canvas.height;
216
217 // Draw boxes around detected movements
218 detectedMovements.forEach((movement, idx) => {
219 const x = movement.minX * scaleX;
220 const y = movement.minY * scaleY;
221 const w = movement.width * scaleX;
222 const h = movement.height * scaleY;
223
224 // Check if this is the locked target
225 const isLockedTarget = isLocked && lockedTarget &&
226 Math.abs(lockedTarget.centerX - movement.centerX) < 50 &&
227 Math.abs(lockedTarget.centerY - movement.centerY) < 50;
228
229 ctx.strokeStyle = isLockedTarget ? config.lockColor : config.boxColor;
230 ctx.lineWidth = config.boxWidth;
231 ctx.strokeRect(x, y, w, h);
232
233 // Draw center dot
234 ctx.fillStyle = isLockedTarget ? config.lockColor : config.boxColor;
235 ctx.beginPath();
236 ctx.arc(x + w/2, y + h/2, 4, 0, Math.PI * 2);
237 ctx.fill();
238
239 // Update locked target position
240 if (isLockedTarget) {
241 lockedTarget = movement;
242 }
243 });
244
245 // If locked, draw crosshair on target
246 if (isLocked && lockedTarget) {
247 const x = lockedTarget.centerX * scaleX;
248 const y = lockedTarget.centerY * scaleY;
249
250 ctx.strokeStyle = config.lockColor;
251 ctx.lineWidth = 2;
252
253 // Draw crosshair
254 ctx.beginPath();
255 ctx.moveTo(x - 20, y);
256 ctx.lineTo(x + 20, y);
257 ctx.moveTo(x, y - 20);
258 ctx.lineTo(x, y + 20);
259 ctx.stroke();
260
261 // Draw lock indicator
262 ctx.fillStyle = config.lockColor;
263 ctx.font = 'bold 14px Arial';
264 ctx.fillText('LOCKED', x + 25, y - 10);
265 }
266 }
267
268 function setupLockHandler() {
269 document.addEventListener('contextmenu', (e) => {
270 // Get click position relative to canvas
271 const rect = canvas.getBoundingClientRect();
272 const clickX = (e.clientX - rect.left) * (canvas.width / rect.width);
273 const clickY = (e.clientY - rect.top) * (canvas.height / rect.height);
274
275 // Find closest movement to click
276 let closestMovement = null;
277 let closestDistance = Infinity;
278
279 detectedMovements.forEach(movement => {
280 const distance = Math.sqrt(
281 Math.pow(movement.centerX - clickX, 2) +
282 Math.pow(movement.centerY - clickY, 2)
283 );
284
285 if (distance < closestDistance && distance < 200) {
286 closestDistance = distance;
287 closestMovement = movement;
288 }
289 });
290
291 if (closestMovement) {
292 // Lock onto target
293 lockedTarget = closestMovement;
294 isLocked = true;
295 console.log('Locked onto target at:', lockedTarget.centerX, lockedTarget.centerY);
296
297 // Auto-aim towards target (move mouse to target)
298 aimAtTarget();
299
300 e.preventDefault();
301 } else if (isLocked) {
302 // Unlock if right-clicking away from targets
303 isLocked = false;
304 lockedTarget = null;
305 console.log('Target unlocked');
306 e.preventDefault();
307 }
308 });
309
310 // Optional: Auto-track locked target
311 setInterval(() => {
312 if (isLocked && lockedTarget) {
313 aimAtTarget();
314 }
315 }, 100);
316 }
317
318 function aimAtTarget() {
319 if (!lockedTarget) return;
320
321 const rect = canvas.getBoundingClientRect();
322 const targetScreenX = rect.left + (lockedTarget.centerX * rect.width / canvas.width);
323 const targetScreenY = rect.top + (lockedTarget.centerY * rect.height / canvas.height);
324
325 // Create mouse move event to aim at target
326 const mouseMoveEvent = new MouseEvent('mousemove', {
327 view: window,
328 bubbles: true,
329 cancelable: true,
330 clientX: targetScreenX,
331 clientY: targetScreenY
332 });
333
334 canvas.dispatchEvent(mouseMoveEvent);
335 }
336
337 // Start when page loads
338 if (document.readyState === 'loading') {
339 document.addEventListener('DOMContentLoaded', init);
340 } else {
341 init();
342 }
343
344})();