实践之路加速
// ==UserScript==// @name 实践之路加速// @namespace http://tampermonkey.net/// @version 4.0// @description 通用网页时间加速器 | 可折叠界面 | 上限5000x | 拖拽移动// @match https://pathofpraxis...
// ==UserScript==
// @name 实践之路加速
// @namespace http://tampermonkey.net/
// @version 4.0
// @description 通用网页时间加速器 | 可折叠界面 | 上限5000x | 拖拽移动
// @match https://pathofpraxis.com/
// @grant none
// @run-at document-start
// @downloadURL https://update.greasyfork.org/scripts/562470/pokechill%E5%8A%A9%E6%89%8B.user.js
// @updateURL https://update.greasyfork.org/scripts/562470/pokechill%E5%8A%A9%E6%89%8B.meta.js
// ==/UserScript==
(function() {
'use strict';
// ================= 配置 =================
const CONFIG = {
MIN_SPEED: 0.1,
MAX_SPEED: 5000.0, // 提升至 5000
DEFAULT_SPEED: 1.0,
STEP_SIZE: 0.5,
UI_ZINDEX: 2147483647
};
// ================= 核心状态 =================
const state = {
speed: CONFIG.DEFAULT_SPEED,
isActive: false,
startTime: { real: 0, virtual: 0 },
originals: {
raf: null,
date: null,
dateNow: null,
perfNow: null,
setTimeout: null,
setInterval: null
},
ui: null,
uiCollapsed: false // 折叠状态
};
// ================= 时间算法 =================
function getVirtualTime(realTimeNow) {
if (!state.isActive) return realTimeNow;
const realDelta = realTimeNow - state.startTime.real;
return state.startTime.virtual + (realDelta * state.speed);
}
function getRealNow() {
if (state.originals.perfNow) {
return state.originals.perfNow.call(window.performance);
}
return state.originals.dateNow.call(state.originals.date);
}
function updateTimeAnchor() {
const realNow = getRealNow();
const currentVirtual = state.isActive ? getVirtualTime(realNow) : realNow;
state.startTime.real = realNow;
state.startTime.virtual = currentVirtual;
state.isActive = true;
}
// ================= 函数劫持 =================
function saveOriginals() {
if (state.originals.date) return;
const rafName = window.requestAnimationFrame ? 'requestAnimationFrame' :
window.webkitRequestAnimationFrame ? 'webkitRequestAnimationFrame' : null;
if (rafName) state.originals.raf = window[rafName];
state.originals.date = window.Date;
state.originals.dateNow = Date.now;
if (window.performance && window.performance.now) {
state.originals.perfNow = window.performance.now;
}
state.originals.setTimeout = window.setTimeout;
state.originals.setInterval = window.setInterval;
}
function hijackRAF() {
if (!state.originals.raf) return;
const rafPolyfill = (callback) => {
return state.originals.raf.call(window, (realTimestamp) => {
const virtualTimestamp = state.isActive ? getVirtualTime(realTimestamp) : realTimestamp;
callback(virtualTimestamp);
});
};
if (window.requestAnimationFrame) window.requestAnimationFrame = rafPolyfill;
if (window.webkitRequestAnimationFrame) window.webkitRequestAnimationFrame = rafPolyfill;
}
function hijackPerformance() {
if (!state.originals.perfNow) return;
window.performance.now = () => {
const realNow = state.originals.perfNow.call(window.performance);
return state.isActive ? getVirtualTime(realNow) : realNow;
};
}
function hijackDate() {
const OriginalDate = state.originals.date;
const MockDate = function(...args) {
if (args.length === 0 && state.isActive) {
const realNow = state.originals.dateNow.call(OriginalDate);
const offset = getVirtualTime(getRealNow()) - getRealNow();
return new OriginalDate(realNow + offset);
}
return new OriginalDate(...args);
};
MockDate.prototype = OriginalDate.prototype;
MockDate.UTC = OriginalDate.UTC;
MockDate.parse = OriginalDate.parse;
MockDate.now = () => {
const realNow = state.originals.dateNow.call(OriginalDate);
if (!state.isActive) return realNow;
const offset = getVirtualTime(getRealNow()) - getRealNow();
return realNow + offset;
};
window.Date = MockDate;
}
function hijackTimers() {
window.setTimeout = (cb, delay, ...args) => {
const scaledDelay = state.isActive ? (delay / state.speed) : delay;
return state.originals.setTimeout.call(window, cb, scaledDelay, ...args);
};
window.setInterval = (cb, delay, ...args) => {
const scaledDelay = state.isActive ? (delay / state.speed) : delay;
return state.originals.setInterval.call(window, cb, scaledDelay, ...args);
};
}
// ================= 控制 =================
function setSpeed(targetSpeed) {
targetSpeed = Math.max(CONFIG.MIN_SPEED, Math.min(CONFIG.MAX_SPEED, targetSpeed));
if (state.speed === targetSpeed && state.isActive) return;
updateTimeAnchor();
state.speed = targetSpeed;
updateUI();
}
// ================= UI 界面(可折叠 + 拖拽) =================
function createUI() {
if (state.ui) return;
const ui = document.createElement('div');
ui.id = 'speed-boost-ui';
ui.style.cssText = `
position: fixed; top: 50px; right: 50px; width: 230px;
background: rgba(16, 20, 25, 0.95); color: #fff;
border-radius: 8px;
font-family: 'Segoe UI', Arial, sans-serif; font-size: 12px;
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
z-index: ${CONFIG.UI_ZINDEX}; backdrop-filter: blur(5px);
user-select: none; border: 1px solid rgba(255,255,255,0.1);
overflow: hidden;
`;
// ----- 标题栏(始终可见,用于拖拽和折叠) -----
const titleBar = document.createElement('div');
titleBar.style.cssText = `
display: flex; justify-content: space-between; align-items: center;
padding: 8px 12px; cursor: grab; background: rgba(0,0,0,0.3);
border-bottom: 1px solid rgba(255,255,255,0.1);
`;
titleBar.innerHTML = `
<span style="font-weight:bold; color:#f1c40f; font-size:14px;">⚡ 加速助手</span>
<span style="display:flex; align-items:center; gap:10px;">
<span id="speed-display" style="font-family:monospace; font-size:14px; color:#fff;">1.00x</span>
<span id="collapse-toggle" style="cursor:pointer; font-size:16px; color:#aaa;">▼</span>
</span>
`;
ui.appendChild(titleBar);
// ----- 内容区(可折叠) -----
const content = document.createElement('div');
content.id = 'speed-content';
content.style.cssText = 'padding: 12px; transition: all 0.2s;';
content.innerHTML = `
<input type="range" id="speed-slider" min="${CONFIG.MIN_SPEED * 10}" max="${CONFIG.MAX_SPEED * 10}" value="10"
style="width:100%; margin-bottom:12px; cursor:pointer; height:6px; accent-color:#f1c40f;">
<div style="display:grid; grid-template-columns: repeat(5, 1fr); gap: 5px; margin-bottom: 8px;">
<button data-speed="1.0">1x</button>
<button data-speed="5.0">5x</button>
<button data-speed="100.0">100x</button>
<button data-speed="1000.0">1000x</button>
<button data-speed="5000.0">5000x</button>
</div>
<div style="display:flex; gap:5px;">
<button id="reset-speed" style="flex:1; background:#d35400;">重置 1x</button>
<button id="hide-panel" style="flex:1; background:#34495e;">隐藏</button>
</div>
<div style="margin-top:8px; color:#666; font-size:10px; text-align:center;">
Ctrl+Shift+↑↓ 微调 | R 重置 | H 隐藏
</div>
`;
ui.appendChild(content);
document.body.appendChild(ui);
state.ui = ui;
// ----- 事件绑定 -----
const slider = content.querySelector('#speed-slider');
const speedDisplay = titleBar.querySelector('#speed-display');
const collapseToggle = titleBar.querySelector('#collapse-toggle');
// 滑块
slider.oninput = (e) => {
const val = parseFloat(e.target.value) / 10;
setSpeed(val);
};
// 预设按钮
content.querySelectorAll('button[data-speed]').forEach(btn => {
btn.onclick = () => {
const val = parseFloat(btn.getAttribute('data-speed'));
setSpeed(val);
slider.value = val * 10;
};
});
// 重置
content.querySelector('#reset-speed').onclick = () => {
setSpeed(1.0);
slider.value = 10;
};
// 隐藏整个面板(通过快捷键也可恢复)
content.querySelector('#hide-panel').onclick = () => {
ui.style.display = 'none';
};
// 折叠切换
collapseToggle.onclick = () => {
state.uiCollapsed = !state.uiCollapsed;
content.style.display = state.uiCollapsed ? 'none' : 'block';
collapseToggle.textContent = state.uiCollapsed ? '▶' : '▼';
};
// ---------- 拖拽(仅标题栏) ----------
let isDragging = false;
let startX, startY, initLeft, initTop;
const onMouseMove = (e) => {
if (!isDragging) return;
e.preventDefault();
ui.style.left = (initLeft + (e.clientX - startX)) + 'px';
ui.style.top = (initTop + (e.clientY - startY)) + 'px';
};
const onMouseUp = () => {
if (isDragging) {
isDragging = false;
titleBar.style.cursor = 'grab';
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
};
titleBar.addEventListener('mousedown', (e) => {
// 点击折叠按钮不触发拖拽
if (e.target.closest('#collapse-toggle')) return;
isDragging = true;
startX = e.clientX;
startY = e.clientY;
initLeft = ui.offsetLeft;
initTop = ui.offsetTop;
titleBar.style.cursor = 'grabbing';
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
// 更新显示
window.updateUI = function() {
const display = titleBar.querySelector('#speed-display');
if (display) display.textContent = state.speed.toFixed(2) + 'x';
if (slider && document.activeElement !== slider) {
slider.value = state.speed * 10;
}
};
updateUI();
}
function updateUI() {
if (typeof window.updateUI === 'function') window.updateUI();
}
// ================= 键盘快捷键 =================
function setupHotkeys() {
document.addEventListener('keydown', (e) => {
if (!e.ctrlKey || !e.shiftKey) return;
const key = e.key.toLowerCase();
if (['arrowup', 'arrowdown', 'r', 'h'].includes(key)) e.preventDefault();
if (e.key === 'ArrowUp') {
const newSpeed = Math.min(state.speed + CONFIG.STEP_SIZE, CONFIG.MAX_SPEED);
setSpeed(newSpeed);
if (state.ui) {
const slider = state.ui.querySelector('#speed-slider');
if (slider) slider.value = newSpeed * 10;
}
}
if (e.key === 'ArrowDown') {
const newSpeed = Math.max(state.speed - CONFIG.STEP_SIZE, CONFIG.MIN_SPEED);
setSpeed(newSpeed);
if (state.ui) {
const slider = state.ui.querySelector('#speed-slider');
if (slider) slider.value = newSpeed * 10;
}
}
if (key === 'r') {
setSpeed(1.0);
if (state.ui) {
const slider = state.ui.querySelector('#speed-slider');
if (slider) slider.value = 10;
}
}
if (key === 'h') {
if (state.ui) {
state.ui.style.display = state.ui.style.display === 'none' ? 'block' : 'none';
}
}
});
}
// ================= 初始化 =================
function init() {
saveOriginals();
hijackRAF();
hijackPerformance();
hijackDate();
hijackTimers();
createUI();
setupHotkeys();
setSpeed(1.0);
console.log('[加速助手] 已加载,上限 5000x,可折叠拖拽');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// 暴露控制接口
window.SpeedBoost = { set: setSpeed, reset: () => setSpeed(1.0) };
})();
- 上一篇
宝可梦: 岩纱之境修改
// 查看队伍中的所有宝可梦window.main.team.pokemon// 获取第一只宝可梦let p = window.main.team.pokemon[0];// 修改攻击 (atk) — 改成 999p.stat.atk = 999;// 修改特攻 (spa)p.stat.spa = 999;// 修改防御 (def...
- 下一篇
数字扭蛋修改
(function() { const WANT = { diamonds: 999999,//货币 可自行选择修改数量 hearts: 999, spades: 999999, clubs: 999999, timeMultiplier: 0.001, attackLength: 10.0, // 敌人前...
微信打赏支持
支付宝打赏支持