您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Find the fastest path in DFProfiler
- // ==UserScript==
- // @name DFProfiler Path Finder
- // @namespace http://tampermonkey.net/
- // @version 0.4
- // @description Find the fastest path in DFProfiler
- // @author Runonstof
- // @match https://*.dfprofiler.com/bossmap
- // @match https://*.dfprofiler.com/profile/view/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=dfprofiler.com
- // @grant unsafeWindow
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- // === Utility functions ===
- function GM_addStyle(css) {
- var style = document.getElementById("GM_addStyleBy8626") || (function() {
- var style = document.createElement('style');
- style.type = 'text/css';
- style.id = "GM_addStyleBy8626";
- document.head.appendChild(style);
- return style;
- })();
- var sheet = style.sheet;
- sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
- }
- function GM_addStyle_object(selector, styles) {
- var css = selector + "{";
- for (var key in styles) {
- css += key + ":" + styles[key] + ";";
- }
- css += "}";
- GM_addStyle(css);
- }
- function ready(fn) {
- if (document.readyState != 'loading'){
- fn();
- } else {
- document.addEventListener('DOMContentLoaded', fn);
- }
- }
- function AStar(emptyCells) {
- this.emptyCells == emptyCells || [];
- this.Node = function Node(x, y) {
- this.x = parseInt(x);
- this.y = parseInt(y);
- this.g = 0; // cost from start node
- this.h = 0; // heuristic (estimated cost to target)
- this.f = 0; // total cost (g + h)
- this.parent = null;
- this.withoutParent = function () {
- var node = new Node(this.x, this.y);
- node.g = this.g;
- node.h = this.h;
- node.f = this.f;
- return node;
- }
- }
- this.isInsideMap = function(x, y) {
- return x >= 1000 && x <= 1058 && y >= 981 && y <= 1035;
- };
- this.isCellEmpty = function(x, y) {
- return emptyCells.some(function(cell) {
- return cell.x == x && cell.y == y;
- });
- };
- this.heuristic = function(node, target) {
- var dx = Math.abs(node.x - target.x);
- var dy = Math.abs(node.y - target.y);
- var diagonalSteps = Math.min(dx, dy);
- var straightSteps = Math.abs(dx - dy);
- return (1.2 * diagonalSteps) + straightSteps;
- }
- this.find = function(startPos, endPos) {
- var openList = [];
- var closedList = [];
- var startNode = new this.Node(startPos.x, startPos.y);
- var endNode = new this.Node(endPos.x, endPos.y);
- var diagonalCost = 1.2;
- var neighbors = [
- { x: 1, y: 0 }, // Right
- { x: -1, y: 0 }, // Left
- { x: 0, y: 1 }, // Down
- { x: 0, y: -1 }, // Up
- { x: 1, y: 1 }, // Diagonal down-right
- { x: -1, y: -1 },// Diagonal up-left
- { x: 1, y: -1 }, // Diagonal up-right
- { x: -1, y: 1 }, // Diagonal down-left
- ];
- openList.push(startNode);
- while (openList.length > 0) {
- var currentNode = openList[0];
- var currentIndex = 0;
- for (var i = 1; i < openList.length; i++) {
- if (openList[i].f < currentNode.f) {
- currentNode = openList[i];
- currentIndex = i;
- }
- }
- openList.splice(currentIndex, 1);
- closedList.push(currentNode);
- if (currentNode.x === endNode.x && currentNode.y === endNode.y) {
- var path = [];
- var current = currentNode;
- while (current !== null) {
- path.push(current.withoutParent());
- current = current.parent;
- }
- return path.reverse();
- }
- for (var neighbourIndex in neighbors) {
- var neighborDelta = neighbors[neighbourIndex];
- var neighborX = currentNode.x + neighborDelta.x;
- var neighborY = currentNode.y + neighborDelta.y;
- if (!this.isInsideMap(neighborX, neighborY) || this.isCellEmpty(neighborX, neighborY)) {
- // console.log('cell is empty or outside map:', neighborX, neighborY);
- continue;
- }
- var neighborNode = new this.Node(neighborX, neighborY);
- var checkNeighbor = function(node) {
- return node.x === neighborX && node.y === neighborY;
- };
- if (closedList.some(checkNeighbor)) {
- continue;
- }
- var movementCost = 1;
- if (neighborDelta.x != 0 && neighborDelta.y != 0) {
- movementCost = diagonalCost;
- }
- // movementCost = diagonalCost;
- // // // console.log('diagonal');
- // // // movementCost = diagonalCost;=
- // // // This is a diagonal movement
- // // if (this.isCellEmpty(currentNode.x + neighborDelta.x, currentNode.y) ||
- // // this.isCellEmpty(currentNode.x, currentNode.y + neighborDelta.y)) {
- // // // Increase diagonal cost if there is an adjacent empty cell
- // // movementCost = 1.50; // You can adjust this value based on your preference
- // // } else {
- // // movementCost = 1;
- // // }
- // }
- var tentativeG = currentNode.g + movementCost; // Assuming each step costs 1
- if (!openList.some(checkNeighbor) || tentativeG < neighborNode.g) {
- neighborNode.g = tentativeG;
- neighborNode.h = this.heuristic(neighborNode, endNode);
- neighborNode.f = neighborNode.g + neighborNode.h;
- neighborNode.parent = currentNode;
- if (!openList.some(checkNeighbor)) {
- openList.push(neighborNode);
- }
- }
- }
- }
- // console.log(closedList);
- return null; // No path found
- }
- }
- // formats ms to hh:mm:ss
- function formatTime(ms) {
- var seconds = Math.floor(ms / 1000);
- var minutes = Math.floor(seconds / 60);
- var hours = Math.floor(minutes / 60);
- seconds -= minutes * 60;
- minutes -= hours * 60;
- var time = '';
- if (hours > 0) {
- time += hours + 'h ';
- }
- if (minutes > 0) {
- time += minutes + 'm ';
- }
- if (seconds > 0) {
- time += seconds + 's';
- }
- return time;
- }
- // === CSS styles ===
- GM_addStyle_object('#boss-data-section #mission-info, #bossmap-page #mission-info', {
- 'border-radius': '25px 25px 0 0',
- });
- GM_addStyle_object('#boss-data-section #mission-info-distance-viewer, #bossmap-page #mission-info-distance-viewer', {
- 'position': 'absolute !important',
- 'background-color': 'hsla(0,0%,5%,.8)',
- 'border-radius': '0 0 25px 25px',
- 'padding': '5px',
- 'top': '770px',
- 'left': 'calc(50% - 16pt * 20)',
- 'right': 'calc(50% - 16pt * 20)',
- });
- GM_addStyle_object('#boss-data-section #mission-info-buttons-title, #bossmap-page #mission-info-buttons-title', {
- 'color': 'white',
- 'font-size': '20px',
- });
- // GM_addStyle_object('#boss-data-section #mission-info-buttons-subtitle, #bossmap-page #mission-info-buttons-subtitle', {
- // 'color': 'white',
- // 'font-size': '14px',
- // });
- GM_addStyle_object('#boss-data-section button.mission-info-button, #bossmap-page button.mission-info-button', {
- 'background-color': 'gray',
- 'color': 'black',
- 'padding': '0.25em 0.5em',
- });
- GM_addStyle_object('#boss-data-section button.mission-info-button:hover, #bossmap-page button.mission-info-button:hover', {
- 'color': 'white',
- });
- GM_addStyle_object('#boss-data-section .dist-buttons, #bossmap-page .dist-buttons', {
- 'display': 'flex',
- 'gap': '10px',
- 'justify-content': 'center',
- 'margin-bottom': '10px',
- 'align-items': 'center',
- });
- GM_addStyle_object('#boss-data-section td.coord.path, #bossmap-page td.coord.path', {
- 'background-color': 'yellow !important',
- 'color': 'black !important',
- });
- // GM_addStyle_object('#coord-hover-tooltip', {
- // 'position': 'absolute',
- // 'background-color': 'hsla(0,0%,5%,.8)',
- // 'border-radius': '5px',
- // 'padding': '5px',
- // });
- ready(function () {
- // === Create Elements ===
- var missionHolder = document.getElementById('mission-holder');
- var container = document.createElement('div');
- container.id = 'mission-info-distance-viewer';
- container.innerHTML = '<div id="mission-info-buttons-title">Path finder</div>';
- container.innerHTML += '<div class="dist-buttons"><button id="dist-clear" style="display: none;" class="mission-info-button">Clear path</button></div>';
- container.innerHTML += '<div class="dist-buttons"><button id="dist-set-start" class="mission-info-button">Set path start</button><button id="dist-set-end" class="mission-info-button">Set path end</button></div>';
- container.innerHTML += '<div class="dist-buttons" style="display:none;">Player navigation: <button id="dist-set-goal" class="mission-info-button">Set path goal</button></div>';
- missionHolder.appendChild(container);
- var mapTopInfo = document.createElement('div');
- mapTopInfo.id = 'map-top-info';
- mapTopInfo.innerHTML = '<div id="mission-info-buttons-subtitle">No path selected, click on a cell to set a start and end point</div>';
- var bossTable = document.querySelector('#boss-table');
- if (bossTable.previousElementSibling) {
- bossTable.previousElementSibling.insertAdjacentElement('afterend', mapTopInfo);
- } else {
- bossTable.insertAdjacentElement('beforebegin', mapTopInfo);
- }
- // var tooltip = document.createElement('div');
- // tooltip.id = 'coord-hover-tooltip';
- // tooltip.style.display = 'none';
- // unsafeWindow.document.body.appendChild(tooltip);
- // function showTooltip(event, info) {
- // tooltip.style.display = 'block';
- // tooltip.style.position = 'absolute';
- // // muose position
- // tooltip.style.top = event.pageY + 10 + 'px';
- // tooltip.style.left = event.pageX + 10 + 'px';
- // tooltip.innerHTML = info;
- // }
- // function hideTooltip() {
- // tooltip.style.display = 'none';
- // }
- unsafeWindow.closeMissionHolder = function (event) {
- if (event.target.closest('#mission-info-distance-viewer')) return;
- missionHolder.style.display = 'none';
- };
- missionHolder.setAttribute('onclick', 'closeMissionHolder(event)');
- var startCellButton = document.getElementById('dist-set-start');
- var endCellButton = document.getElementById('dist-set-end');
- var clearPathButton = document.getElementById('dist-clear');
- var setGoalButton = document.getElementById('dist-set-goal');
- var subtitle = document.getElementById('mission-info-buttons-subtitle');
- // === Scan empty cells
- var emptyCells = Array.from(document.querySelectorAll('td.coord'))
- .filter(function (el) {
- return el.computedStyleMap().get('opacity').toString() == '0';
- })
- .map(function (el) {
- return {
- x: el.classList[1].replace('x', ''),
- y: el.classList[2].replace('y', ''),
- };
- });
- var startCell = null;
- var endCell = null;
- var trackingGps = false;
- var lastTrackTime = null;
- var lastRemainingCellCount = 0;
- var pathFinder = new AStar(emptyCells);
- function maybeUpdatePath() {
- if (!startCell || !endCell) return false;
- var path = pathFinder.find(startCell, endCell);
- // Clear existing path cells
- var pathCells = unsafeWindow.document.querySelectorAll('td.coord.path');
- for (var i = 0; i < pathCells.length; i++) {
- pathCells[i].classList.remove('path');
- delete pathCells[i].dataset.distanceDebug;
- delete pathCells[i].dataset.distanceIndex;
- // pathCells[i].onmouseover = null;
- }
- // console.log(path);
- if (!path) return false;
- for(var i = 0; i < path.length; i++) {
- var cellCoord = path[i];
- // console.log(cellCoord);
- var cell = unsafeWindow.document.querySelector('td.coord.x' + cellCoord.x + '.y' + cellCoord.y);
- cell.classList.add('path');
- cell.dataset.distanceDebug = JSON.stringify(cellCoord);
- cell.dataset.distanceIndex = i;
- // cell.onmouseover = function(event) {
- // if (!event.target.dataset.distanceDebug) return;
- // var info = JSON.parse(event.target.dataset.distanceDebug);
- // var index = parseInt(event.target.dataset.distanceIndex) + 1;
- // showTooltip(event, 'Index: ' + index + '<br>Cell: ' + info.x + 'x' + info.y + '<br>Total cost: ' + info.g + '<br>Heuristic: ' + info.h + '<br>Total: ' + info.f);
- // };
- }
- clearPathButton.style.display = 'initial';
- subtitle.innerHTML = 'Path length: ' + path.length + ' cells';
- if (trackingGps) {
- subtitle.innerHTML += ' (tracking player)';
- // subtitle.innerHTML += '<br>' + (lastTrackTime === null ? 'Start walking to see estimated time' : 'Estimated time: ');
- subtitle.innerHTML += '<br>';
- if (lastTrackTime === null) {
- subtitle.innerHTML += 'Start walking to see estimated time';
- } else {
- var now = new Date().getTime();
- var timeDiff = now - lastTrackTime;
- var cellsTraveled = Math.abs(lastRemainingCellCount - path.length);
- var timeRemaining;
- if (!cellsTraveled) {
- timeRemaining = 'Unknown';
- } else {
- var timePerCell = timeDiff / cellsTraveled;
- timeRemaining = formatTime(timePerCell * path.length);
- }
- subtitle.innerHTML += 'Estimated time remaining: ' + timeRemaining;
- subtitle.innerHTML += '<br>Cells traveled: ' + cellsTraveled;
- subtitle.innerHTML += '<br>Time passed: ' + formatTime(timeDiff);
- }
- }
- return true;
- }
- var playerCoords = null;
- var playerCell = unsafeWindow.document.querySelector('td.playerlocation');
- if (playerCell) {
- playerCoords = [
- playerCell.classList[1].replace('x', ''),
- playerCell.classList[2].replace('y', ''),
- ];
- }
- startCellButton.onclick = function () {
- // current pos
- var img = unsafeWindow.document.querySelector('#mission-info img');
- var matches = img.src.match(/Fairview_(\d+)x(\d+)/);
- var x = matches[1];
- var y = matches[2];
- trackingGps = false;
- lastTrackTime = null;
- startCell = { x: x, y: y };
- unsafeWindow.document.querySelector('td.coord.x' + x + '.y' + y).classList.add('path');
- missionHolder.style.display = 'none';
- maybeUpdatePath();
- };
- endCellButton.onclick = function () {
- // current pos
- var img = document.querySelector('#mission-info img');
- var matches = img.src.match(/Fairview_(\d+)x(\d+)/);
- var x = matches[1];
- var y = matches[2];
- endCell = { x: x, y: y };
- unsafeWindow.document.querySelector('td.coord.x' + x + '.y' + y).classList.add('path');
- missionHolder.style.display = 'none';
- maybeUpdatePath();
- };
- clearPathButton.onclick = function () {
- startCell = null;
- endCell = null;
- trackingGps = false;
- lastTrackTime = null;
- clearPathButton.style.display = 'none';
- // Clear existing path cells
- var pathCells = unsafeWindow.document.querySelectorAll('td.coord.path');
- for (var i = 0; i < pathCells.length; i++) {
- pathCells[i].classList.remove('path');
- }
- subtitle.innerHTML = 'No path selected, click on a cell to set a start and end point';
- missionHolder.style.display = 'none';
- };
- var updatePlayerTrackPath = function () {
- if (!trackingGps) return;
- var playerCell = unsafeWindow.document.querySelector('td.playerlocation');
- if (!playerCell) {
- setTimeout(updatePlayerTrackPath, 7500);
- return;
- };
- playerCoords = [
- playerCell.classList[1].replace('x', ''),
- playerCell.classList[2].replace('y', ''),
- ];
- startCell = { x: playerCoords[0], y: playerCoords[1] };
- maybeUpdatePath();
- setTimeout(updatePlayerTrackPath, 7500);
- };
- setGoalButton.onclick = function () {
- trackingGps = true;
- missionHolder.style.display = 'none';
- // current pos
- var img = document.querySelector('#mission-info img');
- var matches = img.src.match(/Fairview_(\d+)x(\d+)/);
- var x = matches[1];
- var y = matches[2];
- endCell = { x: x, y: y };
- if (playerCoords) {
- startCell = { x: playerCoords[0], y: playerCoords[1] };
- } else {
- startCell = { x: x, y: y };
- }
- unsafeWindow.document.querySelector('td.coord.x' + x + '.y' + y).classList.add('path');
- var result = maybeUpdatePath();
- if (!result) {
- trackingGps = false;
- return;
- }
- lastTrackTime = new Date().getTime();
- lastRemainingCellCount = pathFinder.find(startCell, endCell).length;
- updatePlayerTrackPath();
- };
- $(unsafeWindow.document).ajaxComplete(function(event, jqXHR, ajaxOptions) {
- console.log('ajaxComplete', ajaxOptions.url)
- if (ajaxOptions.url.indexOf('/profile/json/') == -1) return;
- setGoalButton.parentElement.style.display = 'initial';
- playerCoords = jqXHR.responseJSON.gpscoords;
- // if (!trackingGps) return;
- // startCell = { x: coords[0], y: coords[1] };
- // maybeUpdatePath();
- });
- });
- })();