Size
15.7 KB
Version
1.1.1
Created
Nov 6, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name Ninja.io ESP Player Tracker
3// @description Advanced ESP tracker for ninja.io with player position tracking
4// @version 1.1.1
5// @match https://*.ninja.io/*
6// @icon https://ninja.io/favicon.ico
7// @grant GM.getValue
8// @grant GM.setValue
9// ==/UserScript==
10(function() {
11 'use strict';
12
13 console.log('[ESP Tracker] Script loaded');
14
15 // Configuration
16 const CONFIG = {
17 espEnabled: true,
18 showBoxes: true,
19 showLines: true,
20 showHealth: true,
21 showDistance: true,
22 boxColor: '#00FF00',
23 lineColor: '#FF0000',
24 textColor: '#FFFFFF',
25 enemyColor: '#FF0000',
26 allyColor: '#00FF00',
27 updateInterval: 16 // ~60 FPS
28 };
29
30 let espCanvas = null;
31 let espCtx = null;
32 let gameCanvas = null;
33 let players = [];
34 let capturedPlayers = new Map();
35
36 // Debounce function for performance
37 function debounce(func, wait) {
38 let timeout;
39 return function executedFunction(...args) {
40 const later = () => {
41 clearTimeout(timeout);
42 func(...args);
43 };
44 clearTimeout(timeout);
45 timeout = setTimeout(later, wait);
46 };
47 }
48
49 // Initialize ESP overlay
50 function initESP() {
51 console.log('[ESP Tracker] Attempting to set up overlay...');
52
53 gameCanvas = document.getElementById('view');
54 if (!gameCanvas) {
55 console.log('[ESP Tracker] Game canvas not found yet, retrying in 500ms...');
56 setTimeout(initESP, 500);
57 return;
58 }
59
60 console.log('[ESP Tracker] Game canvas found! Size:', gameCanvas.width, 'x', gameCanvas.height);
61
62 // Wait for canvas to have dimensions
63 if (gameCanvas.width === 0 || gameCanvas.height === 0) {
64 console.log('[ESP Tracker] Canvas has no dimensions yet, retrying...');
65 setTimeout(initESP, 500);
66 return;
67 }
68
69 // Create overlay canvas
70 espCanvas = document.createElement('canvas');
71 espCanvas.id = 'esp-overlay';
72 espCanvas.width = gameCanvas.width;
73 espCanvas.height = gameCanvas.height;
74 espCanvas.style.position = 'absolute';
75 espCanvas.style.top = gameCanvas.offsetTop + 'px';
76 espCanvas.style.left = gameCanvas.offsetLeft + 'px';
77 espCanvas.style.pointerEvents = 'none';
78 espCanvas.style.zIndex = '9999';
79
80 gameCanvas.parentElement.appendChild(espCanvas);
81 espCtx = espCanvas.getContext('2d');
82
83 console.log('[ESP Tracker] Overlay canvas created successfully!');
84 console.log('[ESP Tracker] Overlay position:', espCanvas.style.top, espCanvas.style.left);
85 console.log('[ESP Tracker] Overlay size:', espCanvas.width, 'x', espCanvas.height);
86
87 // Handle canvas resize
88 const resizeObserver = new ResizeObserver(debounce(() => {
89 if (gameCanvas && espCanvas) {
90 espCanvas.width = gameCanvas.width;
91 espCanvas.height = gameCanvas.height;
92 espCanvas.style.top = gameCanvas.offsetTop + 'px';
93 espCanvas.style.left = gameCanvas.offsetLeft + 'px';
94 console.log('[ESP Tracker] Canvas resized:', espCanvas.width, 'x', espCanvas.height);
95 }
96 }, 100));
97
98 resizeObserver.observe(gameCanvas);
99
100 // Hook into the game's rendering
101 hookGameRendering();
102
103 // Start ESP rendering loop
104 startESPLoop();
105 }
106
107 // Hook into game rendering to capture player data
108 function hookGameRendering() {
109 console.log('[ESP Tracker] Installing rendering hooks...');
110
111 // Hook CanvasRenderingContext2D methods to intercept player rendering
112 const originalDrawImage = CanvasRenderingContext2D.prototype.drawImage;
113 const originalFillRect = CanvasRenderingContext2D.prototype.fillRect;
114
115 let drawCallCount = 0;
116
117 // Intercept drawImage calls (sprites/players)
118 CanvasRenderingContext2D.prototype.drawImage = function(...args) {
119 if (this.canvas === gameCanvas) {
120 drawCallCount++;
121
122 // Capture potential player positions
123 const x = args[args.length === 3 ? 1 : args.length === 5 ? 1 : 5];
124 const y = args[args.length === 3 ? 2 : args.length === 5 ? 2 : 6];
125 const width = args[args.length === 9 ? 7 : args.length === 5 ? 3 : 0];
126 const height = args[args.length === 9 ? 8 : args.length === 5 ? 4 : 0];
127
128 // Filter for player-sized sprites
129 if (x !== undefined && y !== undefined && width > 10 && height > 10 && width < 200 && height < 200) {
130 const key = `${Math.round(x)}_${Math.round(y)}`;
131 capturedPlayers.set(key, {
132 x: x,
133 y: y,
134 width: width || 50,
135 height: height || 50,
136 timestamp: Date.now()
137 });
138 }
139 }
140 return originalDrawImage.apply(this, args);
141 };
142
143 // Intercept fillRect calls (health bars, UI elements)
144 CanvasRenderingContext2D.prototype.fillRect = function(x, y, width, height) {
145 if (this.canvas === gameCanvas) {
146 // Detect health bars (small rectangles above players)
147 if (width > 10 && width < 100 && height > 2 && height < 20) {
148 const key = `${Math.round(x)}_${Math.round(y - 60)}`;
149 if (capturedPlayers.has(key)) {
150 const player = capturedPlayers.get(key);
151 player.healthBarWidth = width;
152 player.health = (width / 50) * 100; // Estimate health percentage
153 }
154 }
155 }
156 return originalFillRect.apply(this, arguments);
157 };
158
159 // Update players list periodically
160 setInterval(() => {
161 const now = Date.now();
162 players = Array.from(capturedPlayers.values()).filter(p => now - p.timestamp < 500);
163
164 // Clean up old entries
165 for (const [key, player] of capturedPlayers.entries()) {
166 if (now - player.timestamp > 500) {
167 capturedPlayers.delete(key);
168 }
169 }
170 }, 50);
171
172 // Log hook status every 5 seconds
173 setInterval(() => {
174 console.log('[ESP Tracker] Status - Draw calls:', drawCallCount, 'Players tracked:', players.length);
175 drawCallCount = 0;
176 }, 5000);
177
178 console.log('[ESP Tracker] Rendering hooks installed successfully!');
179 }
180
181 // Draw ESP overlays
182 function drawESP() {
183 if (!espCtx || !espCanvas || !CONFIG.espEnabled) return;
184
185 // Clear canvas
186 espCtx.clearRect(0, 0, espCanvas.width, espCanvas.height);
187
188 if (players.length === 0) return;
189
190 // Get center of screen (local player position estimate)
191 const centerX = espCanvas.width / 2;
192 const centerY = espCanvas.height / 2;
193
194 players.forEach((player) => {
195 const { x, y, width, height, health } = player;
196
197 // Skip if player is at center (likely local player)
198 const distFromCenter = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
199 if (distFromCenter < 50) return;
200
201 // Determine color based on health or distance
202 let color = CONFIG.enemyColor;
203 if (health && health > 70) {
204 color = '#FF0000'; // High health - red
205 } else if (health && health > 30) {
206 color = '#FFA500'; // Medium health - orange
207 } else if (health) {
208 color = '#FFFF00'; // Low health - yellow
209 }
210
211 // Draw box around player
212 if (CONFIG.showBoxes) {
213 espCtx.strokeStyle = color;
214 espCtx.lineWidth = 2;
215 espCtx.strokeRect(x - width/2, y - height/2, width, height);
216 }
217
218 // Draw line from center to player
219 if (CONFIG.showLines) {
220 espCtx.strokeStyle = color;
221 espCtx.lineWidth = 1;
222 espCtx.setLineDash([5, 5]);
223 espCtx.beginPath();
224 espCtx.moveTo(centerX, centerY);
225 espCtx.lineTo(x, y);
226 espCtx.stroke();
227 espCtx.setLineDash([]);
228 }
229
230 // Draw player info text
231 espCtx.font = 'bold 12px Arial';
232 espCtx.fillStyle = CONFIG.textColor;
233 espCtx.strokeStyle = '#000000';
234 espCtx.lineWidth = 3;
235
236 let yOffset = y - height/2 - 10;
237
238 // Show distance
239 if (CONFIG.showDistance) {
240 const distance = Math.round(distFromCenter / 10);
241 const distText = `${distance}m`;
242 espCtx.strokeText(distText, x - espCtx.measureText(distText).width/2, yOffset);
243 espCtx.fillText(distText, x - espCtx.measureText(distText).width/2, yOffset);
244 yOffset -= 15;
245 }
246
247 // Show health
248 if (CONFIG.showHealth && health) {
249 const healthText = `HP: ${Math.round(health)}%`;
250 espCtx.strokeText(healthText, x - espCtx.measureText(healthText).width/2, yOffset);
251 espCtx.fillText(healthText, x - espCtx.measureText(healthText).width/2, yOffset);
252 }
253
254 // Draw health bar
255 if (health) {
256 const barWidth = 50;
257 const barHeight = 5;
258 const barX = x - barWidth/2;
259 const barY = y - height/2 - 25;
260
261 // Background
262 espCtx.fillStyle = '#333333';
263 espCtx.fillRect(barX, barY, barWidth, barHeight);
264
265 // Health
266 espCtx.fillStyle = color;
267 espCtx.fillRect(barX, barY, (barWidth * health) / 100, barHeight);
268
269 // Border
270 espCtx.strokeStyle = '#FFFFFF';
271 espCtx.lineWidth = 1;
272 espCtx.strokeRect(barX, barY, barWidth, barHeight);
273 }
274 });
275 }
276
277 // ESP rendering loop
278 function startESPLoop() {
279 console.log('[ESP Tracker] Starting ESP render loop...');
280
281 let frameCount = 0;
282 function render() {
283 drawESP();
284 frameCount++;
285 requestAnimationFrame(render);
286 }
287
288 requestAnimationFrame(render);
289
290 // Log render status every 5 seconds
291 setInterval(() => {
292 console.log('[ESP Tracker] Rendering - FPS:', Math.round(frameCount / 5), 'Players visible:', players.length);
293 frameCount = 0;
294 }, 5000);
295 }
296
297 // Add control panel
298 function createControlPanel() {
299 console.log('[ESP Tracker] Creating control panel...');
300
301 const panel = document.createElement('div');
302 panel.id = 'esp-control-panel';
303 panel.innerHTML = `
304 <div style="position: fixed; top: 10px; right: 10px; background: rgba(0, 0, 0, 0.9); color: white; padding: 15px; border-radius: 8px; z-index: 10000; font-family: Arial; font-size: 12px; min-width: 200px; box-shadow: 0 4px 6px rgba(0,0,0,0.3);">
305 <div style="font-weight: bold; margin-bottom: 10px; color: #00FF00; font-size: 14px;">🎯 ESP Tracker</div>
306 <label style="display: block; margin: 5px 0; cursor: pointer;">
307 <input type="checkbox" id="esp-toggle" ${CONFIG.espEnabled ? 'checked' : ''}> Enable ESP
308 </label>
309 <label style="display: block; margin: 5px 0; cursor: pointer;">
310 <input type="checkbox" id="esp-boxes" ${CONFIG.showBoxes ? 'checked' : ''}> Show Boxes
311 </label>
312 <label style="display: block; margin: 5px 0; cursor: pointer;">
313 <input type="checkbox" id="esp-lines" ${CONFIG.showLines ? 'checked' : ''}> Show Lines
314 </label>
315 <label style="display: block; margin: 5px 0; cursor: pointer;">
316 <input type="checkbox" id="esp-health" ${CONFIG.showHealth ? 'checked' : ''}> Show Health
317 </label>
318 <label style="display: block; margin: 5px 0; cursor: pointer;">
319 <input type="checkbox" id="esp-distance" ${CONFIG.showDistance ? 'checked' : ''}> Show Distance
320 </label>
321 <div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #444; font-size: 10px; color: #888;">
322 Players tracked: <span id="player-count" style="color: #00FF00; font-weight: bold;">0</span>
323 </div>
324 </div>
325 `;
326 document.body.appendChild(panel);
327
328 console.log('[ESP Tracker] Control panel created!');
329
330 // Add event listeners
331 document.getElementById('esp-toggle').addEventListener('change', (e) => {
332 CONFIG.espEnabled = e.target.checked;
333 GM.setValue('espEnabled', CONFIG.espEnabled);
334 console.log('[ESP Tracker] ESP enabled:', CONFIG.espEnabled);
335 });
336
337 document.getElementById('esp-boxes').addEventListener('change', (e) => {
338 CONFIG.showBoxes = e.target.checked;
339 GM.setValue('showBoxes', CONFIG.showBoxes);
340 console.log('[ESP Tracker] Show boxes:', CONFIG.showBoxes);
341 });
342
343 document.getElementById('esp-lines').addEventListener('change', (e) => {
344 CONFIG.showLines = e.target.checked;
345 GM.setValue('showLines', CONFIG.showLines);
346 console.log('[ESP Tracker] Show lines:', CONFIG.showLines);
347 });
348
349 document.getElementById('esp-health').addEventListener('change', (e) => {
350 CONFIG.showHealth = e.target.checked;
351 GM.setValue('showHealth', CONFIG.showHealth);
352 console.log('[ESP Tracker] Show health:', CONFIG.showHealth);
353 });
354
355 document.getElementById('esp-distance').addEventListener('change', (e) => {
356 CONFIG.showDistance = e.target.checked;
357 GM.setValue('showDistance', CONFIG.showDistance);
358 console.log('[ESP Tracker] Show distance:', CONFIG.showDistance);
359 });
360
361 // Update player count
362 setInterval(() => {
363 const countEl = document.getElementById('player-count');
364 if (countEl) {
365 countEl.textContent = players.length;
366 }
367 }, 500);
368 }
369
370 // Load saved settings
371 async function loadSettings() {
372 console.log('[ESP Tracker] Loading saved settings...');
373 CONFIG.espEnabled = await GM.getValue('espEnabled', true);
374 CONFIG.showBoxes = await GM.getValue('showBoxes', true);
375 CONFIG.showLines = await GM.getValue('showLines', true);
376 CONFIG.showHealth = await GM.getValue('showHealth', true);
377 CONFIG.showDistance = await GM.getValue('showDistance', true);
378 console.log('[ESP Tracker] Settings loaded:', CONFIG);
379 }
380
381 // Initialize everything
382 async function init() {
383 console.log('[ESP Tracker] Initializing...');
384
385 await loadSettings();
386
387 // Wait for page to be ready
388 const startInit = () => {
389 console.log('[ESP Tracker] Starting initialization sequence...');
390 setTimeout(() => {
391 initESP();
392 createControlPanel();
393 }, 1000);
394 };
395
396 if (document.readyState === 'loading') {
397 console.log('[ESP Tracker] Waiting for DOM to load...');
398 document.addEventListener('DOMContentLoaded', startInit);
399 } else {
400 console.log('[ESP Tracker] DOM already loaded, starting now...');
401 startInit();
402 }
403 }
404
405 // Start the tracker
406 init();
407})();