您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Uses a sprite sheet from Imgur and a combined grid data object (with gridTiles, gridLines, and an optional imgurLink) to draw custom tiles and lines on the TagPro map background. Also adds a homepage modal editor for adding and removing maps.
// ==UserScript== // @name TagPro Custom Map Tiles (Anchored PIXI Overlay & Editor) // @namespace http://tampermonkey.net/ // @version 1.3 // @description Uses a sprite sheet from Imgur and a combined grid data object (with gridTiles, gridLines, and an optional imgurLink) to draw custom tiles and lines on the TagPro map background. Also adds a homepage modal editor for adding and removing maps. // @author // @match https://tagpro.koalabeast.com/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // Run once TagPro is ready. tagpro.ready(function() { //////////////////////////////// // Setup Combined Grid Data //////////////////////////////// var defaultCombinedGridData = [ { "Asida": { "gridLines": [ { "start": { "x": 16.5, "y": 17.5 }, "end": { "x": 6, "y": 9.75 }, "color": "red" }, { "start": { "x": 6, "y": 9.75 }, "end": { "x": 6.75, "y": 9 }, "color": "red" }, { "start": { "x": 6.75, "y": 9 }, "end": { "x": 6.75, "y": 9.5 }, "color": "red" }, { "start": { "x": 6.75, "y": 9 }, "end": { "x": 6.25, "y": 9 }, "color": "red" }, { "start": { "x": 16.5, "y": 17.5 }, "end": { "x": 16.25, "y": 17.25 }, "color": "red" }, { "start": { "x": 16.5, "y": 17.5 }, "end": { "x": 11, "y": 6 }, "color": "#f79999" }, { "start": { "x": 11, "y": 6.25 }, "end": { "x": 10.75, "y": 7 }, "color": "#f79999" }, { "start": { "x": 11, "y": 6 }, "end": { "x": 12, "y": 6.5 }, "color": "#f79999" }, { "start": { "x": 20.25, "y": 9.25 }, "end": { "x": 30.75, "y": 16.75 }, "color": "blue" }, { "start": { "x": 30.75, "y": 16.75 }, "end": { "x": 31, "y": 17 }, "color": "blue" }, { "start": { "x": 30.75, "y": 17.25 }, "end": { "x": 30, "y": 18 }, "color": "blue" }, { "start": { "x": 30, "y": 18 }, "end": { "x": 30.5, "y": 18 }, "color": "blue" }, { "start": { "x": 30, "y": 17.75 }, "end": { "x": 30, "y": 17.75 }, "color": "blue" }, { "start": { "x": 30, "y": 18 }, "end": { "x": 30, "y": 17.5 }, "color": "blue" }, { "start": { "x": 20.25, "y": 9.25 }, "end": { "x": 20.25, "y": 9.25 }, "color": "#99c1f1" }, { "start": { "x": 20.25, "y": 9.25 }, "end": { "x": 20.25, "y": 9.25 }, "color": "#99c1f1" }, { "start": { "x": 20.25, "y": 9.25 }, "end": { "x": 26, "y": 21 }, "color": "#99c1f1" }, { "start": { "x": 26, "y": 21 }, "end": { "x": 26, "y": 20.25 }, "color": "#99c1f1" }, { "start": { "x": 26, "y": 21 }, "end": { "x": 25.25, "y": 20.5 }, "color": "#99c1f1" } ], "gridTiles": [ { "x": 18, "y": 13, "tileIndex": 0 }, { "x": 16, "y": 12, "tileIndex": 0 }, { "x": 17, "y": 15, "tileIndex": 0 }, { "x": 20, "y": 14, "tileIndex": 0 }, { "x": 20, "y": 12, "tileIndex": 0 }, { "x": 18, "y": 14, "tileIndex": 1 }, { "x": 17, "y": 12, "tileIndex": 1 }, { "x": 19, "y": 12, "tileIndex": 1 }, { "x": 20, "y": 13, "tileIndex": 1 }, { "x": 20, "y": 15, "tileIndex": 1 }, { "x": 16, "y": 15, "tileIndex": 1 }, { "x": 19, "y": 14, "tileIndex": 2 }, { "x": 17, "y": 14, "tileIndex": 2 }, { "x": 18, "y": 12, "tileIndex": 2 }, { "x": 19, "y": 13, "tileIndex": 3 }, { "x": 17, "y": 13, "tileIndex": 3 }, { "x": 18, "y": 15, "tileIndex": 3 }, { "x": 16, "y": 14, "tileIndex": 3 }, { "x": 19, "y": 15, "tileIndex": 4 }, { "x": 16, "y": 13, "tileIndex": 4 }, { "x": 19, "y": 11, "tileIndex": 4 }, { "x": 17, "y": 11, "tileIndex": 4 }, { "x": 18, "y": 11, "tileIndex": 4 }, { "x": 18, "y": 11, "tileIndex": 0 }, { "x": 20, "y": 11, "tileIndex": 2 }, { "x": 16, "y": 11, "tileIndex": 2 } ], "imgurLink": "https://i.imgur.com/oZDnzgO.png" } } ]; // Load any saved data from localStorage (or use the default). var combinedGridData; try { var stored = localStorage.getItem("tagproCombinedGridData"); if (stored) { combinedGridData = JSON.parse(stored); } else { combinedGridData = defaultCombinedGridData; } } catch (e) { console.error("Error loading combinedGridData from localStorage", e); combinedGridData = defaultCombinedGridData; } // Helper function to save the grid data. function saveCombinedGridData() { localStorage.setItem("tagproCombinedGridData", JSON.stringify(combinedGridData)); } //////////////////////////////// // Branch based on current page: //////////////////////////////// if (window.location.pathname.includes("/game")) { // If we are on a game page, wait until players are loaded. var waitForPlayers = setInterval(function(){ if (tagpro.players && Object.keys(tagpro.players).length > 0) { clearInterval(waitForPlayers); // Use the first available player as a reference. var firstPlayerId = Object.keys(tagpro.players)[0]; var playerContainer = tagpro.players[firstPlayerId].sprite.parent; // Wait until the map is available. var checkMapInterval = setInterval(function(){ if (tagpro.map && tagpro.map.name) { clearInterval(checkMapInterval); // Find matching grid data using tagpro.map.name. var currentMapData = null; for (var i = 0; i < combinedGridData.length; i++) { var mapNameKey = Object.keys(combinedGridData[i])[0]; if (mapNameKey === tagpro.map.name) { currentMapData = combinedGridData[i][mapNameKey]; break; } } if (!currentMapData) { return; } var gridLines = currentMapData.gridLines; var gridTiles = currentMapData.gridTiles; var spriteSheetUrl = currentMapData.imgurLink; // Get TagPro's background container. var bgContainer = tagpro.renderer.stage.children[0]; if (!bgContainer) { return; } var customOverlay = new PIXI.Container(); var tileSize = 40; var spriteSheetTexture = PIXI.Texture.from(spriteSheetUrl); // Add custom tiles. gridTiles.forEach(function(tile) { var posX = tile.x * tileSize; var posY = tile.y * tileSize; var index = tile.tileIndex; var srcX = (index % 10) * tileSize; var srcY = Math.floor(index / 10) * tileSize; var tileTexture = new PIXI.Texture(spriteSheetTexture.baseTexture, new PIXI.Rectangle(srcX, srcY, tileSize, tileSize)); var sprite = new PIXI.Sprite(tileTexture); sprite.x = posX; sprite.y = posY; customOverlay.addChild(sprite); }); // Draw custom lines. var lineGraphics = new PIXI.Graphics(); gridLines.forEach(function(line) { var lineColor = (typeof line.color === "string") ? PIXI.utils.string2hex(line.color) : (line.color !== undefined ? line.color : 0xFFFFFF); lineGraphics.lineStyle(2, lineColor, 1); var startX = line.start.x * tileSize; var startY = line.start.y * tileSize; var endX = line.end.x * tileSize; var endY = line.end.y * tileSize; lineGraphics.moveTo(startX, startY); lineGraphics.lineTo(endX, endY); }); customOverlay.addChild(lineGraphics); // Insert the overlay into the player container so it is rendered behind all players. if (playerContainer) { playerContainer.addChildAt(customOverlay, 0); } else { console.error("Player container not found."); } } }, 100); } }, 100); } else { // Non-game pages (e.g., the homepage): Set up the modal editor immediately. // Inject CSS for the modal editor. var style = document.createElement('style'); style.textContent = ` #customMapEditorModal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); display: none; justify-content: center; align-items: center; z-index: 10000; font-family: Arial, sans-serif; } #customMapEditorContent { background: #f9f9f9; padding: 20px; border-radius: 12px; max-width: 500px; width: 90%; max-height: 80%; overflow-y: auto; position: relative; box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2); border: 1px solid #ddd; } #customMapEditorContent h2 { margin-top: 0; font-size: 20px; color: #333; text-align: center; } .mapItem { display: flex; justify-content: space-between; align-items: center; padding: 10px; background: #ffffff; border-radius: 6px; margin-bottom: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .mapItem span { font-size: 16px; color: #444; } .mapItem button { background: #ff4d4d; color: white; border: none; padding: 5px 10px; cursor: pointer; border-radius: 4px; transition: background 0.2s ease-in-out; } .mapItem button:hover { background: #e60000; } #addMapTextbox { width: 100%; height: 100px; margin-top: 10px; border-radius: 6px; border: 1px solid #ccc; padding: 8px; font-size: 14px; } #customMapEditorClose { position: absolute; top: 10px; right: 15px; cursor: pointer; font-size: 24px; color: #666; transition: color 0.2s; } #customMapEditorClose:hover { color: #222; } #customMapEditorButton { position: fixed; bottom: 15px; right: 15px; z-index: 10000; padding: 12px 18px; background: #007bff; color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; transition: background 0.2s ease-in-out; box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2); } #customMapEditorButton:hover { background: #0056b3; }`; document.head.appendChild(style); // Create a fixed-position button that opens the modal. var editorButton = document.createElement('button'); editorButton.id = 'customMapEditorButton'; editorButton.textContent = 'Edit Map Data'; document.body.appendChild(editorButton); // Create the modal element. var modal = document.createElement('div'); modal.id = 'customMapEditorModal'; modal.innerHTML = ` <div id="customMapEditorContent"> <span id="customMapEditorClose">×</span> <h2>Custom Map Data Editor</h2> <div id="mapListContainer"></div> <textarea id="addMapTextbox" placeholder='Paste JSON object here to add (e.g., {"New Map": {"gridLines": [...], "gridTiles": [...], "imgurLink": "..."}})'></textarea> </div> `; document.body.appendChild(modal); // Function to update the list of maps. function updateMapList() { var container = document.getElementById('mapListContainer'); container.innerHTML = ''; if (combinedGridData.length === 0) { container.textContent = 'No map data available.'; return; } combinedGridData.forEach(function(item, index) { var mapName = Object.keys(item)[0]; var div = document.createElement('div'); div.className = 'mapItem'; div.innerHTML = `<span>${mapName}</span> <button data-index="${index}">x</button>`; container.appendChild(div); }); } updateMapList(); // Listen for clicks on remove (x) buttons. document.getElementById('mapListContainer').addEventListener('click', function(e) { if (e.target && e.target.tagName === 'BUTTON') { var index = parseInt(e.target.getAttribute('data-index'), 10); if (!isNaN(index)) { combinedGridData.splice(index, 1); saveCombinedGridData(); updateMapList(); } } }); // When a JSON object is pasted into the textarea, try to add it. var addMapTextbox = document.getElementById('addMapTextbox'); addMapTextbox.addEventListener('paste', function(e) { setTimeout(function() { try { var pastedText = addMapTextbox.value.trim(); if (!pastedText) return; var newMapData = JSON.parse(pastedText); if (typeof newMapData === 'object' && newMapData !== null && Object.keys(newMapData).length === 1) { combinedGridData.push(newMapData); saveCombinedGridData(); updateMapList(); addMapTextbox.value = ''; } else { alert('Invalid format. The JSON object must have exactly one key (the map name).'); } } catch (err) { alert('Error parsing JSON: ' + err); } }, 100); }); // Open the modal when clicking the editor button. editorButton.addEventListener('click', function() { modal.style.display = 'flex'; }); // Close the modal when clicking the close (×) button. document.getElementById('customMapEditorClose').addEventListener('click', function() { modal.style.display = 'none'; }); // Also close the modal if clicking outside the content. modal.addEventListener('click', function(e) { if (e.target === modal) { modal.style.display = 'none'; } }); } // End non-/game branch }); // End tagpro.ready })(); // End IIFE
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址