Ninja.io ESP Player Tracker

Advanced ESP tracker for ninja.io with player position tracking

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})();
Ninja.io ESP Player Tracker | Robomonkey