Greasy Fork is available in English.
ZoomHack, AimBow, AutoHeal, ESP, Tracers, Scout, WalkUnlock, KillMessage, GoldBot, AutoReconnect, AntiAFK, Macros
// ==UserScript==
// @name TakeMineClient[1.1]
// @namespace Violentmonkey Scripts
// @icon https://takemine.io/favicon-32x32.png
// @version 1.1
// @match *://takemine.io/*
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_info
// @author Drik
// @description ZoomHack, AimBow, AutoHeal, ESP, Tracers, Scout, WalkUnlock, KillMessage, GoldBot, AutoReconnect, AntiAFK, Macros
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const _$gi = (typeof GM_info !== 'undefined') ? GM_info : null;
const _$raw = _$gi && _$gi.script ? (_$gi.script.updateURL || '') : null;
if (_$raw === null) {
alert('Install the original TakeMineClient from GreasyFork');
throw new Error('TMC');
}
const CFG_KEY = 'TakeMineClient';
let menuKey = 'F4';
let currentTheme = 'dark';
let currentLang = 'en';
let trapBind = '';
let ballistaBind = '';
const I18N = {
en: {
autoheal: ['Auto Heal', 'Auto heal when HP < 60% and food available'],
aimbow: ['Aim Bow', 'Aim with a bow toward the nearest enemy'],
autoreconnect: ['Auto Reconnect', 'Instantly respawn on death'],
killmessage: ['Kill Message', 'Send a chat message when you kill a player'],
zoom: ['Zoom', 'Zoom hack (CTRL + mouse wheel)'],
esp: ['ESP', 'Draws precise hitboxes on players'],
tracers: ['Tracers', 'Draws lines from you to each enemy'],
hud: ['HUD', 'Shows FPS, ping and player count'],
skinunlock: ['Skin Unlock', 'Unlocks Orc, Elf, Undead without sharing'],
walkunlock: ['Walk Unlock', 'Move even with chat or menus open'],
scout: ['Scout', 'Second account marks players on minimap'],
Gold_Bot: ['Gold Bot', 'Bots come toward you, kill them for gold'],
antiafk: ['Anti-AFK', 'Prevents server kick for inactivity'],
macros: ['Macros', 'PvP macros list'],
trapMacro: ['Place Trap', 'Place trap on bind key'],
ballistaMacro: ['Place Ballista', 'Place ballista on bind key'],
settings: ['Settings', ''],
theme: ['Theme', ''],
language: ['Language', ''],
resetCfg: ['Reset Config', ''],
escClear: ['Esc - clear bind', ''],
},
ru: {
autoheal: ['Авто-хил', 'Хил при HP < 60% если есть еда'],
aimbow: ['Прицел лука', 'Прицеливается к ближайшему врагу'],
autoreconnect: ['Авто-реконнект', 'Мгновенный респавн при смерти'],
killmessage: ['Сообщение о киле', 'Сообщение в чат при убийстве игрока'],
zoom: ['Зум', 'Зум хак (CTRL + колесо мыши)'],
esp: ['ESP', 'Рисует хитбоксы игроков'],
tracers: ['Трейсеры', 'Линии от тебя до врагов'],
hud: ['HUD', 'Показывает FPS, пинг, кол-во игроков'],
skinunlock: ['Скины', 'Открывает орка, эльфа, нежить без шера'],
walkunlock: ['Движение', 'Бег при открытом чате или меню'],
scout: ['Скаут', 'Второй акк помечает игроков на минимапе'],
Gold_Bot: ['Голд бот', 'Боты идут к тебе, убивай за золото'],
antiafk: ['Анти-АФК', 'Защита от кика за неактивность'],
macros: ['Макросы', 'Список макросов для пвп'],
trapMacro: ['Капкан', 'Ставит капкан по кнопке'],
ballistaMacro: ['Баллиста', 'Ставит баллисту по кнопке'],
settings: ['Настройки', ''],
theme: ['Тема', ''],
language: ['Язык', ''],
resetCfg: ['Сбросить конфиг', ''],
escClear: ['Esc - очистить бинд', ''],
}
};
function t(key, idx) {
return (I18N[currentLang] || I18N.en)[key]?.[idx || 0] || key;
}
const _defaults = {
zoom: {
enabled: false
},
autoheal: {
enabled: false
},
skinunlock: {
enabled: false
},
esp: {
enabled: false
},
tracers: {
enabled: false
},
scout: {
enabled: false
},
walkunlock: {
enabled: false
},
killmessage: {
enabled: false
},
aimbow: {
enabled: false
},
Gold_Bot: {
enabled: false
},
hud: {
enabled: false
},
autoreconnect: {
enabled: false
},
antiafk: {
enabled: false
},
};
function loadState() {
try {
const sv = JSON.parse(localStorage.getItem(CFG_KEY) || '{}');
const st = {};
for (const k in _defaults) st[k] = {
enabled: sv[k] !== undefined ? !!sv[k] : _defaults[k].enabled
};
if (sv._mk) menuKey = sv._mk;
if (sv._th) currentTheme = sv._th;
if (sv._ln) currentLang = sv._ln;
if (sv._tb) trapBind = sv._tb;
if (sv._bb) ballistaBind = sv._bb;
return st;
} catch {
return JSON.parse(JSON.stringify(_defaults));
}
}
function saveState() {
const o = {};
for (const k in state) o[k] = state[k].enabled;
o._mk = menuKey;
o._th = currentTheme;
o._ln = currentLang;
o._tb = trapBind;
o._bb = ballistaBind;
localStorage.setItem(CFG_KEY, JSON.stringify(o));
}
const state = loadState();
if (state.skinunlock.enabled)['elf', 'orc', 'undead'].forEach(h => {
unsafeWindow.localStorage[h] = true;
});
let gameWS = null,
ahEnabled = state.autoheal.enabled,
ahHealing = false,
ahProto = false,
ahPrevTool = 1,
ahFrames = 0;
const ZOOM_BASE = 1.06;
let zoomFactor = ZOOM_BASE;
const wuKeys = {
attack: false,
up: false,
right: false,
down: false,
left: false
};
let wuLast = null;
let scoutGhost = null,
scoutGhostId = null,
scoutRespawnT = null;
const scoutMarks = [],
MAP_SIZE = 8160;
const dgBots = [];
let dgRunning = false;
let kmKills = 0;
const kmQueue = [];
const BOW_ID = 4,
ARROW_SPD = 0.55;
let hudPing = 0,
hudFps = 0,
hudLast = performance.now(),
hudF = 0;
let macroMove = 0,
trapBusy = false,
ballistaBusy = false;
const TRAP_TOOL = 9,
BALLISTA_TOOL = 23;
const AR_BTN = '#game_scene > div > div > div > div.modal-body > div > div:nth-child(6) > button';
if (typeof _$raw === 'string' && _$raw.length === 0) {
alert('Install the original TakeMineClient from GreasyFork');
throw new Error('TMC');
}
const _nativeSend = unsafeWindow.WebSocket.prototype.send;
unsafeWindow.WebSocket.prototype.send = function(data) {
if (typeof data === 'string' && data.startsWith('42[1,') && !gameWS) gameWS = this;
return _nativeSend.call(this, data);
};
function rawSend(s) {
if (gameWS && gameWS.readyState === 1) _nativeSend.call(gameWS, s);
}
const _omDesc = Object.getOwnPropertyDescriptor(unsafeWindow.WebSocket.prototype, 'onmessage');
Object.defineProperty(unsafeWindow.WebSocket.prototype, 'onmessage', {
get() {
return this._tmc_om || null;
},
set(fn) {
this._tmc_om = fn;
_omDesc.set.call(this, function(ev) {
if (typeof ev.data === 'string' && ev.data.startsWith('42')) {
try {
const p = JSON.parse(ev.data.slice(2));
const id = p[0],
pl = p[1];
if (state.killmessage.enabled) {
const c = unsafeWindow.CLIENT;
if (id === 11) {
kmQueue.push(c?.playerNames?.[pl] || ('Player#' + pl));
}
if (id === 6 && Array.isArray(pl)) {
for (let i = 0; i < pl.length; i += 2) {
if (pl[i] === 0 && pl[i + 1] > kmKills) {
kmKills = pl[i + 1];
const name = kmQueue.shift();
if (name) c?.socket?.emit(10, name + ' killed by ' + c?.player?.name);
}
}
}
if (id === 9) {
kmKills = 0;
kmQueue.length = 0;
}
}
if (id === 9 && state.autoreconnect.enabled) _arWaitForBtn();
} catch {}
}
if (fn) fn.call(this, ev);
});
},
configurable: true
});
Object.defineProperty(unsafeWindow, 'Vue', {
configurable: true,
set(V) {
Object.defineProperty(unsafeWindow, 'Vue', {
value: V,
writable: true,
configurable: true
});
const _O = V;
function PV(opts) {
const i = new _O(opts);
if (opts?.el === '#ui') unsafeWindow.UI = i;
return i;
}
PV.prototype = _O.prototype;
['config', 'use', 'component', 'set', 'delete', 'nextTick', 'extend', 'mixin', 'compile', 'observable', 'version'].forEach(k => {
if (_O[k] !== undefined) PV[k] = typeof _O[k] === 'function' ? (...a) => _O[k].apply(_O, a) : _O[k];
});
Object.defineProperty(unsafeWindow, 'Vue', {
value: PV,
writable: true,
configurable: true
});
}
});
const _origBind = unsafeWindow.Function.prototype.bind;
unsafeWindow.Function.prototype.bind = function(thisArg, ...args) {
if (!unsafeWindow.CLIENT && thisArg !== null && thisArg !== undefined && typeof thisArg === 'object' &&
thisArg.camera !== undefined && thisArg.mainCanvas !== undefined && thisArg.mainCtx !== undefined && thisArg.screenWidth !== undefined) {
unsafeWindow.CLIENT = thisArg;
unsafeWindow.Function.prototype.bind = _origBind;
_initFeatures(thisArg);
}
return _origBind.apply(this, [thisArg, ...args]);
};
function _initFeatures(c) {
_initAutoHeal(c);
_initRender(c);
_initWalkUnlock(c);
if (c.socket?.io) c.socket.io.on('pong', ms => {
hudPing = ms;
});
_startAntiAFK();
}
function _initAutoHeal(c) {
let _p = null;
Object.defineProperty(c, 'player', {
get() {
return _p;
},
set(val) {
_p = val;
if (val && !ahProto) {
ahProto = true;
_hookSetHP(Object.getPrototypeOf(val));
}
},
configurable: true
});
}
function _hookSetHP(proto) {
const _orig = proto.setHP;
proto.setHP = function(hp) {
const res = _orig.call(this, hp);
const c = unsafeWindow.CLIENT;
if (!c || !c.player || this !== c.player) return res;
if (ahEnabled && !ahHealing && hp > 0 && hp < this.maxHP * 0.6 && c.player.food >= 10) {
ahPrevTool = c.player.tool ? c.player.tool.id : 1;
ahHealing = true;
ahFrames = 0;
_doHeal();
}
return res;
};
}
function _doHeal() {
rawSend('42[3,2]');
requestAnimationFrame(() => {
rawSend('42[2,16]');
requestAnimationFrame(() => {
rawSend('42[2,0]');
ahFrames = 0;
});
});
}
function _initRender(c) {
const _orig = c.render.bind(c);
c.render = function() {
_orig();
const ctx = c.mainCtx,
cam = c.camera,
me = c.player,
pl = c.players;
if (!me || !pl) return;
if (state.esp.enabled) {
ctx.save();
ctx.globalAlpha = 1;
ctx.shadowColor = 'transparent';
ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
for (const id in pl) {
if (!pl.hasOwnProperty(id)) continue;
const p = pl[id];
if (!p || !p.isVisible || p.id === me.id) continue;
const cx = cam.calculateCameraX(p.x),
cy = cam.calculateCameraY(p.y);
const r = p.radius,
rs = p.skin ? p.skin.radiusShift : 0;
ctx.beginPath();
ctx.arc(cx, cy, r + rs, 0, 6.2832);
ctx.strokeStyle = 'rgba(255,200,0,1)';
ctx.lineWidth = 1;
ctx.stroke();
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 6.2832);
ctx.strokeStyle = 'rgba(255,50,50,0.9)';
ctx.lineWidth = 1.5;
ctx.stroke();
}
ctx.beginPath();
ctx.restore();
}
if (state.tracers.enabled) {
const mx = cam.calculateCameraX(me.x),
my = cam.calculateCameraY(me.y);
const sorted = [];
for (const id in pl) {
if (!pl.hasOwnProperty(id)) continue;
const p = pl[id];
if (!p || p.id === me.id) continue;
const dx = p.x - me.x,
dy = p.y - me.y;
sorted.push({
p,
d: dx * dx + dy * dy
});
}
sorted.sort((a, b) => a.d - b.d);
ctx.save();
sorted.forEach(({
p,
d
}, i) => {
const px = cam.calculateCameraX(p.x),
py = cam.calculateCameraY(p.y);
const ratio = Math.min(d / (3000 * 3000), 1),
rv = Math.round(255 * ratio),
gv = Math.round(255 * (1 - ratio));
ctx.beginPath();
ctx.moveTo(mx, my);
ctx.lineTo(px, py);
ctx.strokeStyle = `rgba(${rv},${gv},0,0.75)`;
ctx.lineWidth = i === 0 ? 2.5 / cam.scale : 1.5 / cam.scale;
ctx.stroke();
ctx.fillStyle = `rgba(${rv},${gv},0,0.9)`;
ctx.beginPath();
ctx.arc(px, py, 4 / cam.scale, 0, Math.PI * 2);
ctx.fill();
});
ctx.restore();
}
};
}
function _initWalkUnlock(c) {
const s = c.socket,
_oe = s.emit.bind(s);
s.emit = function(op, val) {
if (op === 2 && state.walkunlock.enabled) {
const oc = _wuCode();
if (val === 0 && oc !== 0) {
wuLast = oc;
return _oe(2, oc);
}
if (val === oc) {
wuLast = val;
return _oe.apply(this, arguments);
}
}
return _oe.apply(this, arguments);
};
}
function _wuCode() {
return parseInt('' + +wuKeys.attack + +wuKeys.up + +wuKeys.right + +wuKeys.down + +wuKeys.left, 2);
}
function _wuSend() {
const c = unsafeWindow.CLIENT;
if (!c || !c.socket || !state.walkunlock.enabled) return;
const code = _wuCode();
if (code === wuLast) return;
wuLast = code;
c.socket.emit(2, code);
}
function _startAntiAFK() {
setInterval(() => {
if (!state.antiafk.enabled) return;
const C = unsafeWindow.CLIENT;
if (!C?.socket || !C.player) return;
C.socket.emit(1, C.angle || 0);
C.socket.emit(2, 0);
}, 15000);
}
unsafeWindow.addEventListener('keydown', e => {
switch (e.keyCode) {
case 38:
case 87:
wuKeys.up = true;
macroMove |= 8;
break;
case 39:
case 68:
wuKeys.right = true;
macroMove |= 4;
break;
case 40:
case 83:
wuKeys.down = true;
macroMove |= 2;
break;
case 37:
case 65:
wuKeys.left = true;
macroMove |= 1;
break;
case 32:
wuKeys.attack = true;
break;
case 69:
wuKeys.attack = !wuKeys.attack;
break;
}
_wuSend();
if (trapBind && e.key === trapBind && !trapBusy) _doTrap();
if (ballistaBind && e.key === ballistaBind && !ballistaBusy) _doBallista();
}, true);
unsafeWindow.addEventListener('keyup', e => {
switch (e.keyCode) {
case 38:
case 87:
wuKeys.up = false;
macroMove &= ~8;
break;
case 39:
case 68:
wuKeys.right = false;
macroMove &= ~4;
break;
case 40:
case 83:
wuKeys.down = false;
macroMove &= ~2;
break;
case 37:
case 65:
wuKeys.left = false;
macroMove &= ~1;
break;
case 32:
wuKeys.attack = false;
break;
}
_wuSend();
}, true);
unsafeWindow.addEventListener('blur', () => {
wuKeys.attack = wuKeys.up = wuKeys.right = wuKeys.down = wuKeys.left = false;
wuLast = null;
});
function _doTrap() {
const C = unsafeWindow.CLIENT;
if (!C?.socket?.emit || !C.player) return;
const prev = C.player.tool.id;
if (prev === TRAP_TOOL) return;
trapBusy = true;
const s = C.socket;
s.emit(3, TRAP_TOOL);
requestAnimationFrame(() => {
s.emit(2, macroMove | 0x10);
requestAnimationFrame(() => {
s.emit(2, macroMove);
s.emit(3, prev);
trapBusy = false;
});
});
}
function _doBallista() {
const C = unsafeWindow.CLIENT;
if (!C?.socket?.emit || !C.player) return;
const prev = C.player.tool.id;
if (prev === BALLISTA_TOOL) return;
ballistaBusy = true;
const s = C.socket;
s.emit(3, BALLISTA_TOOL);
requestAnimationFrame(() => {
s.emit(2, macroMove | 0x10);
requestAnimationFrame(() => {
s.emit(2, macroMove);
s.emit(3, prev);
ballistaBusy = false;
});
});
}
function _arIsVisible(el) {
if (!el) return false;
const r = el.getBoundingClientRect();
if (!r.width || !r.height || r.top < 0 || r.left < 0) return false;
const st = unsafeWindow.getComputedStyle(el);
return st.display !== 'none' && st.visibility !== 'hidden' && st.opacity !== '0';
}
function _arWaitForBtn() {
if (!state.autoreconnect.enabled) return;
const btn = document.querySelector(AR_BTN);
if (!_arIsVisible(btn)) {
requestAnimationFrame(_arWaitForBtn);
return;
}
btn.dispatchEvent(new MouseEvent('click', {
bubbles: true,
cancelable: true
}));
const UI = unsafeWindow.UI;
if (UI?.$emit) UI.$emit('login');
requestAnimationFrame(() => {
if (_arIsVisible(document.querySelector(AR_BTN))) _arWaitForBtn();
});
}
function _applyZoom() {
const c = unsafeWindow.CLIENT;
if (!c) return;
const w = c.screenWidth,
h = c.screenHeight,
base = Math.round(100 * Math.max(w / 1440, h / 900)) / 100;
const ns = parseFloat((base * zoomFactor).toFixed(4)),
pr = unsafeWindow.PIXEL_RATIO;
c.camera.scale = ns;
c.camera.width = parseInt(w / ns);
c.camera.height = parseInt(h / ns);
c.camera.widthHalf = c.camera.width / 2;
c.camera.heightHalf = c.camera.height / 2;
c.camera.isChanged = true;
c.mainCtx.setTransform(pr, 0, 0, pr, 0, 0);
c.mainCtx.scale(ns, ns);
c.backgroundCtx.setTransform(pr, 0, 0, pr, 0, 0);
c.backgroundCtx.scale(ns, ns);
}
unsafeWindow.addEventListener('wheel', e => {
if (!state.zoom.enabled || !e.ctrlKey) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
zoomFactor = parseFloat((e.deltaY < 0 ? Math.min(3, zoomFactor + 0.1) : Math.max(0.1, zoomFactor - 0.1)).toFixed(2));
_applyZoom();
}, {
passive: false,
capture: true
});
const _origRAF = unsafeWindow.requestAnimationFrame;
unsafeWindow.requestAnimationFrame = function(cb) {
return _origRAF(function(ts) {
const c = unsafeWindow.CLIENT;
if (ahHealing && c?.player) {
if (c.player.hp >= c.player.maxHP || c.player.food < 10) {
ahHealing = false;
ahFrames = 0;
rawSend('42[3,' + ahPrevTool + ']');
} else {
ahFrames++;
if (ahFrames >= 3) _doHeal();
}
}
if (state.aimbow.enabled && c?.player?.tool?.id === BOW_ID && c.socket) {
const tg = _findNearest(c);
if (tg) {
const {
x,
y
} = _predictPos(tg, c.player);
const ang = _bowAngle(c.player, x, y);
if (ang !== c.prevAngle) {
c.socket.emit(1, ang);
c.angle = ang;
c.prevAngle = ang;
}
}
}
cb(ts);
});
};
function _findNearest(c) {
const me = c.player;
let cl = null,
md = Infinity;
for (const id in c.players) {
if (!c.players.hasOwnProperty(id)) continue;
const p = c.players[id];
if (!p || p.id === me.id) continue;
const d = (p.x - me.x) ** 2 + (p.y - me.y) ** 2;
if (d < md) {
md = d;
cl = p;
}
}
return cl;
}
function _predictPos(tg, me) {
const buf = tg.positionBuffer;
let px = tg.x,
py = tg.y;
if (buf && buf.length >= 2) {
const b0 = buf[buf.length - 2],
b1 = buf[buf.length - 1],
dt = b1.t - b0.t;
if (dt > 0) {
const vx = (b1.x - b0.x) / dt,
vy = (b1.y - b0.y) / dt,
d = Math.sqrt((tg.x - me.x) ** 2 + (tg.y - me.y) ** 2);
px = tg.x + vx * (d / ARROW_SPD);
py = tg.y + vy * (d / ARROW_SPD);
}
}
return {
x: px,
y: py
};
}
function _bowAngle(me, tx, ty) {
const cam = unsafeWindow.CLIENT.camera;
return Math.round(100 * (Math.atan2(cam.calculateCameraY(ty) - cam.calculateCameraY(me.y), cam.calculateCameraX(tx) - cam.calculateCameraX(me.x)) + Math.PI / 2));
}
function scoutStart() {
if (scoutGhost) return;
if (!unsafeWindow.CLIENT || !unsafeWindow.UI) return;
const sv = document.getElementById('serverList')?.value;
if (!sv) return;
scoutGhost = unsafeWindow.io.connect(sv, {
forceNew: true
});
scoutGhost.on('connect', () => _scoutSpawn());
scoutGhost.on(3, d => {
scoutGhostId = d[0];
clearTimeout(scoutRespawnT);
scoutRespawnT = setTimeout(_scoutSpawn, 700);
});
scoutGhost.on(4, d => {
const UI = unsafeWindow.UI,
C = unsafeWindow.CLIENT;
if (!UI?.minimap) return;
const mn = C?.player?.name;
for (let i = 0; i < d.length; i += 11) {
const r = d.slice(i, i + 11);
if (r[0] === scoutGhostId) continue;
if (mn && C?.playerNames?.[r[0]] === mn) continue;
const key = 'scout_' + r[0];
UI.minimap.setMarker(key, MAP_SIZE, MAP_SIZE, r[2], r[3], '#ffff00');
if (!scoutMarks.includes(key)) scoutMarks.push(key);
}
});
scoutGhost.on(9, () => {
clearTimeout(scoutRespawnT);
_scoutSpawn();
});
}
function scoutStop() {
clearTimeout(scoutRespawnT);
if (scoutGhost) {
scoutGhost.disconnect();
scoutGhost = null;
scoutGhostId = null;
}
}
function _scoutSpawn() {
if (scoutGhost) scoutGhost.emit(0, 'Drik_Scout', 1);
}
function scoutClear() {
const UI = unsafeWindow.UI;
if (!UI) return;
scoutMarks.forEach(k => UI.minimap.removeMarker(k));
scoutMarks.length = 0;
}
function dgStart() {
dgRunning = true;
for (let i = 0; i < 15; i++) _dgCreateBot(i);
}
function dgStop() {
dgRunning = false;
const UI = unsafeWindow.UI;
dgBots.forEach(b => {
if (b.moveInterval) clearInterval(b.moveInterval);
if (b.socket) b.socket.disconnect();
if (UI?.minimap && b.id) UI.minimap.removeMarker('gp_' + b.id);
});
dgBots.length = 0;
}
function _dgCreateBot(idx) {
const sv = document.getElementById('serverList')?.value;
if (!sv) return;
const bot = {
socket: null,
id: null,
x: 0,
y: 0,
moveInterval: null
};
dgBots[idx] = bot;
bot.socket = unsafeWindow.io.connect(sv, {
forceNew: true
});
bot.socket.on('connect', () => bot.socket.emit(0, 'Drik_Gold', 1));
bot.socket.on(3, d => {
bot.id = d[0];
_dgMove(bot);
});
bot.socket.on(4, d => {
for (let i = 0; i < d.length; i += 11) {
const r = d.slice(i, i + 11);
if (r[0] === bot.id) {
bot.x = r[2];
bot.y = r[3];
_dgMark(bot);
}
}
});
bot.socket.on(9, () => {
clearInterval(bot.moveInterval);
if (dgRunning) setTimeout(() => bot.socket.emit(0, 'Drik_Gold', 1), 500);
});
}
function _dgMove(bot) {
clearInterval(bot.moveInterval);
bot.moveInterval = setInterval(() => {
const c = unsafeWindow.CLIENT;
if (!c?.player) return;
const dx = c.player.x - bot.x,
dy = c.player.y - bot.y,
d = Math.sqrt(dx * dx + dy * dy);
if (d < 80) {
bot.socket.emit(2, 0);
return;
}
const kc = parseInt('0' + (dy < -40 ? '1' : '0') + (dx > 40 ? '1' : '0') + (dy > 40 ? '1' : '0') + (dx < -40 ? '1' : '0'), 2);
bot.socket.emit(1, Math.round(100 * (Math.atan2(dy, dx) + Math.PI / 2)));
bot.socket.emit(2, kc);
}, 50);
}
function _dgMark(bot) {
const UI = unsafeWindow.UI;
if (UI?.minimap) UI.minimap.setMarker('gp_' + bot.id, MAP_SIZE, MAP_SIZE, bot.x, bot.y, '#ffd700');
}
(function _hudLoop(now) {
hudF++;
const dt = now - hudLast;
if (dt >= 1000) {
hudFps = Math.round(hudF * 1000 / dt);
hudF = 0;
hudLast = now;
}
if (state.hud.enabled) {
const el = document.getElementById('pfhud');
if (el) {
const c = unsafeWindow.CLIENT;
const pl = c?.playerNames ? Object.keys(c.playerNames).length : '-';
el.innerHTML = 'FPS: ' + (hudFps || '-') + '<br>PING: ' + (hudPing ? hudPing + 'ms' : '-') + '<br>PLR: ' + pl;
}
}
_origRAF(_hudLoop);
})(performance.now());
(function() {
let _u;
try {
_u = decodeURIComponent(_$raw).toLowerCase();
} catch {
_u = (_$raw || '').toLowerCase();
}
const _valid = _u.length > 0 && _u.startsWith('https://update.greasyfork.org/scripts/') && _u.endsWith('.meta.js') && /takemineclient(\[\d+(\.\d+)?\])?/.test(_u);
if (!_valid) {
alert('Invalid source. Download TakeMineClient from official page');
throw new Error('TMC');
}
})();
function onToggle(key, on) {
switch (key) {
case 'skinunlock':
if (on)['elf', 'orc', 'undead'].forEach(h => {
unsafeWindow.localStorage[h] = true;
});
break;
case 'autoheal':
ahEnabled = on;
if (!on && ahHealing) {
ahHealing = false;
ahFrames = 0;
rawSend('42[3,' + ahPrevTool + ']');
}
break;
case 'Gold_Bot':
on ? dgStart() : dgStop();
break;
case 'hud': {
const el = document.getElementById('pfhud');
if (el) el.style.display = on ? '' : 'none';
break;
}
}
saveState();
}
function applyTheme(theme) {
currentTheme = theme;
const menu = document.getElementById('tm-menu');
if (menu) {
menu.classList.remove('theme-dark', 'theme-white');
menu.classList.add('theme-' + theme);
}
}
GM_addStyle(`@import url("https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Rajdhani:wght@400;500;600;700&display=swap");
#tm-menu* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
#tm-menu {
position: fixed;
top: 14px;
right: 14px;
z-index: 999999;
width: 320px;
background: #161b27;
border: 1px solid #252d3d;
border-radius: 12px;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.04);
font-family: "Rajdhani", sans-serif;
overflow: hidden;
user-select: none;
}
#tm-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
background: #1a2030;
border-bottom: 1px solid #252d3d;
cursor: move;
}
#tm-title {
font-size: 16px;
font-weight: 700;
letter-spacing: 3px;
text-transform: uppercase;
color: #e2e8f0;
font-family: "Space Mono", monospace;
}
#tm-title span {
color: #4ade80;
}
#tm-gear {
background: none;
border: none;
cursor: pointer;
color: #4a5568;
padding: 0;
line-height: 1;
transition: color 0.2s;
font-size: 16px;
}
#tm-gear:hover {
color: #4ade80;
}
#tm-tabs {
display: flex;
border-bottom: 1px solid #252d3d;
background: #161b27;
}
.tm-tab {
flex: 1;
padding: 11px 0;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
font-family: "Rajdhani", sans-serif;
font-size: 11px;
font-weight: 600;
letter-spacing: 1.5px;
text-transform: uppercase;
color: #4a5568;
transition: color 0.2s;
border-bottom: 2px solid transparent;
position: relative;
top: 1px;
}
.tm-tab svg {
width: 13px;
height: 13px;
flex-shrink: 0;
}
.tm-tab.active {
color: #4ade80;
border-bottom: 2px solid #4ade80;
}
.tm-tab:hover:not(.active) {
color: #94a3b8;
}
#tm-body {
padding: 6px 0;
max-height: 400px;
overflow-y: auto;
}
#tm-body::-webkit-scrollbar {
width: 3px;
}
#tm-body::-webkit-scrollbar-track {
background: transparent;
}
#tm-body::-webkit-scrollbar-thumb {
background: #252d3d;
border-radius: 3px;
}
.tm-panel {
display: none;
}
.tm-panel.active {
display: block;
}
.tm-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 11px 18px;
gap: 14px;
transition: background 0.15s;
}
.tm-row:hover {
background: rgba(255, 255, 255, 0.025);
}
.tm-row-info {
flex: 1;
min-width: 0;
}
.tm-row-name {
font-size: 14px;
font-weight: 700;
color: #e2e8f0;
letter-spacing: 0.4px;
line-height: 1.2;
}
.tm-row-desc {
font-size: 11px;
color: #4a5568;
margin-top: 3px;
line-height: 1.45;
font-weight: 400;
font-family: "Space Mono", monospace;
}
.tm-toggle {
flex-shrink: 0;
width: 40px;
height: 22px;
background: #252d3d;
border-radius: 11px;
position: relative;
cursor: pointer;
transition: background 0.25s;
margin-top: 2px;
}
.tm-toggle.on {
background: #16a34a;
}
.tm-toggle::after {
content: "";
position: absolute;
top: 3px;
left: 3px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #fff;
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.tm-toggle.on::after {
transform: translateX(18px);
}
.tm-divider {
height: 1px;
background: #1e2636;
margin: 0 18px;
}
#tm-footer {
padding: 9px 18px;
border-top: 1px solid #252d3d;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
}
#tm-footer-label {
font-size: 9px;
letter-spacing: 1.5px;
color: #2d3748;
text-transform: uppercase;
font-family: "Space Mono", monospace;
}
#tm-menu-key-btn {
background: #1a2030;
border: 1px solid #2d3748;
border-radius: 4px;
padding: 2px 8px;
font-size: 10px;
color: #94a3b8;
cursor: pointer;
font-family: "Space Mono", monospace;
transition: border-color 0.2s, color 0.2s;
}
#tm-menu-key-btn:hover {
border-color: #4ade80;
color: #4ade80;
}
#tm-menu-key-btn.listening {
border-color: #f59e0b;
color: #f59e0b;
animation: tm-pulse 0.8s ease infinite;
}
@keyframes tm-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
.tm-scout-btns {
display: flex;
gap: 8px;
padding: 6px 18px 14px;
}
.tm-scout-btn {
flex: 1;
padding: 5px 0;
border-radius: 5px;
border: 1px solid #252d3d;
background: #1a2030;
color: #94a3b8;
font-family: "Space Mono", monospace;
font-size: 9px;
letter-spacing: 1px;
text-transform: uppercase;
cursor: pointer;
transition: border-color 0.2s, color 0.2s;
}
.tm-scout-btn:hover {
border-color: #4ade80;
color: #4ade80;
}
#tm-scout-stop:hover {
border-color: #ef4444;
color: #ef4444;
}
#tm-scout-clear:hover {
border-color: #f59e0b;
color: #f59e0b;
}
.tm-changelog {
padding: 10px 18px 14px;
}
.tm-cl-entry {
display: flex;
gap: 12px;
align-items: flex-start;
padding: 8px 0;
border-bottom: 1px solid #1e2636;
}
.tm-cl-entry:last-child {
border-bottom: none;
}
.tm-cl-version {
flex-shrink: 0;
font-family: "Space Mono", monospace;
font-size: 10px;
color: #4ade80;
background: rgba(74, 222, 128, 0.08);
border: 1px solid rgba(74, 222, 128, 0.2);
border-radius: 4px;
padding: 2px 7px;
margin-top: 1px;
}
.tm-cl-text {
font-size: 12px;
color: #64748b;
font-family: "Space Mono", monospace;
line-height: 1.5;
}
#pfhud {
position: fixed;
top: 12px;
left: 12px;
z-index: 99998;
background: rgba(0, 0, 0, 0.5);
color: #fff;
font-family: monospace;
font-size: 12px;
padding: 4px 9px;
border-radius: 5px;
pointer-events: none;
line-height: 1.6;
border-left: 2px solid #4af;
}
.tm-macros-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 11px 18px;
gap: 14px;
cursor: pointer;
transition: background 0.15s;
}
.tm-macros-header:hover {
background: rgba(255, 255, 255, 0.025);
}
.tm-macros-arrow {
color: #4a5568;
font-size: 12px;
margin-top: 3px;
transition: transform 0.2s;
flex-shrink: 0;
}
.tm-macros-arrow.open {
transform: rotate(180deg);
}
.tm-macros-body {
padding: 0 18px 10px;
}
.tm-macro-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 7px 0;
gap: 10px;
}
.tm-macro-name {
font-size: 13px;
font-weight: 600;
color: #e2e8f0;
}
.tm-macro-desc {
font-size: 10px;
color: #4a5568;
margin-top: 2px;
font-family: "Space Mono", monospace;
}
.tm-bind-btn {
flex-shrink: 0;
background: #1a2030;
border: 1px solid #2d3748;
border-radius: 4px;
padding: 2px 10px;
font-size: 10px;
color: #94a3b8;
cursor: pointer;
font-family: "Space Mono", monospace;
min-width: 36px;
text-align: center;
transition: border-color 0.2s, color 0.2s;
user-select: none;
}
.tm-bind-btn:hover {
border-color: #4ade80;
color: #4ade80;
}
.tm-bind-btn.listening {
border-color: #f59e0b;
color: #f59e0b;
animation: tm-pulse 0.8s ease infinite;
}
.tm-macro-hint {
font-size: 9px;
color: #2d3748;
font-family: "Space Mono", monospace;
text-align: center;
padding: 4px 0 2px;
}
#tm-settings {
display: none;
}
#tm-settings-header {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 18px;
background: #1a2030;
border-bottom: 1px solid #252d3d;
}
#tm-settings-back {
background: none;
border: none;
cursor: pointer;
color: #4a5568;
font-size: 16px;
padding: 0;
line-height: 1;
transition: color 0.2s;
}
#tm-settings-back:hover {
color: #4ade80;
}
#tm-settings-title {
font-size: 13px;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
color: #94a3b8;
font-family: "Space Mono", monospace;
}
.tm-setting-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 18px;
border-bottom: 1px solid #1e2636;
}
.tm-setting-label {
font-size: 13px;
font-weight: 600;
color: #e2e8f0;
}
.tm-setting-select {
background: #1a2030;
border: 1px solid #2d3748;
border-radius: 4px;
padding: 3px 8px;
font-size: 11px;
color: #94a3b8;
cursor: pointer;
font-family: "Space Mono", monospace;
outline: none;
transition: border-color 0.2s, color 0.2s;
}
.tm-setting-select:hover,
.tm-setting-select:focus {
border-color: #4ade80;
color: #4ade80;
}
.tm-setting-reset {
background: #1a2030;
border: 1px solid #2d3748;
border-radius: 4px;
padding: 4px 14px;
font-size: 10px;
color: #94a3b8;
cursor: pointer;
font-family: "Space Mono", monospace;
transition: border-color 0.2s, color 0.2s;
letter-spacing: 1px;
text-transform: uppercase;
}
.tm-setting-reset:hover {
border-color: #ef4444;
color: #ef4444;
}
#tm-menu.theme-white {
background: #f4f7ff;
border-color: #d4dcf0;
box-shadow: 0 8px 40px rgba(80, 100, 180, 0.12),
0 0 0 1px rgba(80, 100, 180, 0.08);
}
#tm-menu.theme-white #tm-header {
background: linear-gradient(135deg, #eef2ff, #e6ebff);
border-bottom-color: #d4dcf0;
}
#tm-menu.theme-white #tm-title {
color: #1e2a4a;
}
#tm-menu.theme-white #tm-gear {
color: #94a3b8;
}
#tm-menu.theme-white #tm-gear:hover {
color: #4ade80;
}
#tm-menu.theme-white #tm-tabs {
background: #f4f7ff;
border-bottom-color: #d4dcf0;
}
#tm-menu.theme-white .tm-tab {
color: #94a3b8;
background: #f4f7ff;
}
#tm-menu.theme-white .tm-tab.active {
color: #16a34a;
border-bottom-color: #16a34a;
}
#tm-menu.theme-white .tm-tab:hover:not(.active) {
color: #4a5568;
}
#tm-menu.theme-white #tm-body {
background: #f4f7ff;
}
#tm-menu.theme-white #tm-body::-webkit-scrollbar-thumb {
background: #d4dcf0;
}
#tm-menu.theme-white .tm-row:hover {
background: rgba(80, 100, 180, 0.05);
}
#tm-menu.theme-white .tm-row-name {
color: #1e2a4a;
}
#tm-menu.theme-white .tm-row-desc {
color: #6b7fa8;
}
#tm-menu.theme-white .tm-toggle {
background: #c8d4ec;
}
#tm-menu.theme-white .tm-toggle.on {
background: #16a34a;
}
#tm-menu.theme-white .tm-toggle::after {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
#tm-menu.theme-white .tm-divider {
background: #d4dcf0;
}
#tm-menu.theme-white #tm-footer {
background: #eef2ff;
border-top-color: #d4dcf0;
}
#tm-menu.theme-white #tm-footer-label {
color: #94a3b8;
}
#tm-menu.theme-white #tm-menu-key-btn {
background: #e6ebff;
border-color: #b8c4e0;
color: #4a5878;
}
#tm-menu.theme-white .tm-scout-btns {
background: #f4f7ff;
}
#tm-menu.theme-white .tm-scout-btn {
background: #e6ebff;
border-color: #b8c4e0;
color: #4a5878;
}
#tm-menu.theme-white .tm-macros-header:hover {
background: rgba(80, 100, 180, 0.05);
}
#tm-menu.theme-white .tm-macros-arrow {
color: #94a3b8;
}
#tm-menu.theme-white .tm-macro-name {
color: #1e2a4a;
}
#tm-menu.theme-white .tm-macro-desc {
color: #6b7fa8;
}
#tm-menu.theme-white .tm-bind-btn {
background: #e6ebff;
border-color: #b8c4e0;
color: #4a5878;
}
#tm-menu.theme-white .tm-macro-hint {
color: #94a3b8;
}
#tm-menu.theme-white #tm-settings-header {
background: linear-gradient(135deg, #eef2ff, #e6ebff);
border-bottom-color: #d4dcf0;
}
#tm-menu.theme-white #tm-settings-title {
color: #6b7fa8;
}
#tm-menu.theme-white .tm-setting-row {
border-bottom-color: #d4dcf0;
}
#tm-menu.theme-white .tm-setting-label {
color: #1e2a4a;
}
#tm-menu.theme-white .tm-setting-select {
background: #e6ebff;
border-color: #b8c4e0;
color: #4a5878;
}
#tm-menu.theme-white .tm-setting-reset {
background: #e6ebff;
border-color: #b8c4e0;
color: #4a5878;
}
#tm-menu.theme-white .tm-cl-entry {
border-bottom-color: #d4dcf0;
}
#tm-menu.theme-white .tm-cl-text {
color: #6b7fa8;
}
#tm-menu.theme-white .tm-changelog {
background: #f4f7ff;
}
`);
document.addEventListener('DOMContentLoaded', () => {
const pfhud = document.createElement('div');
pfhud.id = 'pfhud';
pfhud.innerHTML = 'FPS: -<br>PING: -<br>PLR: -';
pfhud.style.display = state.hud.enabled ? '' : 'none';
document.body.appendChild(pfhud);
const menu = document.createElement('div');
menu.id = 'tm-menu';
menu.innerHTML = `
<div id="tm-header">
<div id="tm-title">TakeMine<span>Client</span></div>
<button id="tm-gear" title="Settings">⚙</button>
</div>
<div id="tm-settings">
<div id="tm-settings-header">
<button id="tm-settings-back">←</button>
<div id="tm-settings-title">SETTINGS</div>
</div>
<div class="tm-setting-row">
<span class="tm-setting-label">Theme</span>
<select id="tm-theme-select" class="tm-setting-select">
<option value="dark">Dark</option>
<option value="white">White</option>
</select>
</div>
<div class="tm-setting-row">
<span class="tm-setting-label">Language</span>
<select id="tm-lang-select" class="tm-setting-select">
<option value="en">EN</option>
<option value="ru">RU</option>
</select>
</div>
<div class="tm-setting-row" style="border-bottom:none;">
<span class="tm-setting-label"></span>
<button id="tm-cfg-reset" class="tm-setting-reset">Reset Config</button>
</div>
</div>
<div id="tm-main">
<div id="tm-tabs">
<button class="tm-tab active" data-tab="combat">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="5" x2="5" y2="19"/><polyline points="16 5 19 5 19 8"/><polyline points="5 8 5 5 8 5"/></svg>Combat
</button>
<button class="tm-tab" data-tab="visual">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7z"/></svg>Visual
</button>
<button class="tm-tab" data-tab="misc">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/></svg>Misc
</button>
<button class="tm-tab" data-tab="changelog">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><line x1="10" y1="9" x2="8" y2="9"/></svg>Log
</button>
</div>
<div id="tm-body">
<div class="tm-panel active" data-panel="combat">
${_row('autoheal','Auto Heal','Auto heal when HP < 60% and food available')}
<div class="tm-divider"></div>
${_row('aimbow','Aim Bow','Aim with a bow toward the nearest enemy')}
<div class="tm-divider"></div>
${_row('autoreconnect','Auto Reconnect','Instantly respawn on death')}
<div class="tm-divider"></div>
${_row('killmessage','Kill Message','Send a chat message when you kill a player')}
<div class="tm-divider"></div>
<div class="tm-macros-header" id="tm-macros-toggle">
<div class="tm-row-info">
<div class="tm-row-name" id="tm-macros-name">Macros</div>
<div class="tm-row-desc" id="tm-macros-desc">PvP macros list</div>
</div>
<div class="tm-macros-arrow" id="tm-macros-arrow">▼</div>
</div>
<div class="tm-macros-body" id="tm-macros-body" style="display:none;">
<div class="tm-macro-row">
<div><div class="tm-macro-name" id="tm-macro-trap-name">Place Trap</div><div class="tm-macro-desc" id="tm-macro-trap-desc">Place trap on bind key</div></div>
<div class="tm-bind-btn" id="tm-bind-trap">${trapBind || '-'}</div>
</div>
<div class="tm-divider" style="margin:4px 0;"></div>
<div class="tm-macro-row">
<div><div class="tm-macro-name" id="tm-macro-ball-name">Place Ballista</div><div class="tm-macro-desc" id="tm-macro-ball-desc">Place ballista on bind key</div></div>
<div class="tm-bind-btn" id="tm-bind-ballista">${ballistaBind || '-'}</div>
</div>
<div class="tm-macro-hint" id="tm-macro-hint">Esc - clear bind</div>
</div>
</div>
<div class="tm-panel" data-panel="visual">
${_row('zoom','Zoom','Zoom hack (CTRL + mouse wheel)')}
<div class="tm-divider"></div>
${_row('esp','ESP','Draws precise hitboxes on players')}
<div class="tm-divider"></div>
${_row('tracers','Tracers','Draws lines from you to each enemy')}
<div class="tm-divider"></div>
${_row('hud','HUD','Shows FPS, ping and player count')}
</div>
<div class="tm-panel" data-panel="misc">
${_row('skinunlock','Skin Unlock','Unlocks Orc, Elf, Undead without sharing')}
<div class="tm-divider"></div>
${_row('walkunlock','Walk Unlock','Move even with chat or menus open')}
<div class="tm-divider"></div>
${_row('antiafk','Anti-AFK','Prevents server kick for inactivity')}
<div class="tm-divider"></div>
${_row('Gold_Bot','Gold Bot','Bots come toward you, kill them for gold')}
<div class="tm-divider"></div>
<div class="tm-row" style="flex-direction:column;gap:6px;">
<div class="tm-row-info"><div class="tm-row-name">Scout</div><div class="tm-row-desc" id="tm-scout-desc">Second account marks players on minimap</div></div>
<div class="tm-scout-btns" style="padding:0;">
<button class="tm-scout-btn" id="tm-scout-start">Start</button>
<button class="tm-scout-btn" id="tm-scout-stop">Stop</button>
<button class="tm-scout-btn" id="tm-scout-clear">Clear</button>
</div>
</div>
</div>
<div class="tm-panel" data-panel="changelog">
<div class="tm-changelog">
<div class="tm-cl-entry">
<div class="tm-cl-version">v1.1</div>
<div class="tm-cl-text">Fixed bug with scout. UI upgraded: settings panel, themes, language. New: Auto Reconnect, Anti-AFK, Macros list (PlaceTrap, PlaceBallista)</div>
</div>
<div class="tm-cl-entry">
<div class="tm-cl-version">v1.0</div>
<div class="tm-cl-text">Client released. First public build. Zoom, ESP, Tracers, AutoHeal, AimBow, Scout, WalkUnlock, KillMessage, Gold Bot, HUD</div>
</div>
</div>
</div>
</div>
<div id="tm-footer">
<div id="tm-footer-label">Menu key:</div>
<button id="tm-menu-key-btn">${menuKey}</button>
</div>
</div>
`;
document.body.appendChild(menu);
applyTheme(currentTheme);
document.getElementById('tm-theme-select').value = currentTheme;
document.getElementById('tm-lang-select').value = currentLang;
function showSettings() {
document.getElementById('tm-settings').style.display = 'block';
document.getElementById('tm-main').style.display = 'none';
}
function showMain() {
document.getElementById('tm-settings').style.display = 'none';
document.getElementById('tm-main').style.display = 'block';
}
document.getElementById('tm-gear').addEventListener('click', e => {
e.stopPropagation();
showSettings();
});
document.getElementById('tm-settings-back').addEventListener('click', () => showMain());
document.getElementById('tm-theme-select').addEventListener('change', function() {
currentTheme = this.value;
applyTheme(currentTheme);
saveState();
});
document.getElementById('tm-lang-select').addEventListener('change', function() {
currentLang = this.value;
updateLang();
saveState();
});
document.getElementById('tm-cfg-reset').addEventListener('click', () => {
localStorage.removeItem(CFG_KEY);
location.reload();
});
menu.querySelectorAll('.tm-tab').forEach(tab => {
tab.addEventListener('click', () => {
menu.querySelectorAll('.tm-tab').forEach(t => t.classList.remove('active'));
menu.querySelectorAll('.tm-panel').forEach(p => p.classList.remove('active'));
tab.classList.add('active');
menu.querySelector('[data-panel="' + tab.dataset.tab + '"]')?.classList.add('active');
document.getElementById('tm-footer').style.display = tab.dataset.tab === 'changelog' ? 'none' : '';
});
});
menu.querySelectorAll('.tm-toggle').forEach(tog => {
tog.addEventListener('click', () => {
const k = tog.dataset.key;
state[k].enabled = !state[k].enabled;
tog.classList.toggle('on', state[k].enabled);
onToggle(k, state[k].enabled);
});
});
document.getElementById('tm-scout-start').addEventListener('click', scoutStart);
document.getElementById('tm-scout-stop').addEventListener('click', scoutStop);
document.getElementById('tm-scout-clear').addEventListener('click', scoutClear);
const macrosToggle = document.getElementById('tm-macros-toggle');
const macrosBody = document.getElementById('tm-macros-body');
const macrosArrow = document.getElementById('tm-macros-arrow');
macrosToggle.addEventListener('click', () => {
const open = macrosBody.style.display === 'none';
macrosBody.style.display = open ? '' : 'none';
macrosArrow.classList.toggle('open', open);
});
function setupBind(btnId, getVal, setVal) {
const btn = document.getElementById(btnId);
btn.addEventListener('click', () => {
btn.classList.add('listening');
btn.textContent = '...';
const h = e => {
e.preventDefault();
e.stopPropagation();
if (e.key === 'Escape') {
setVal('');
btn.textContent = '-';
} else {
setVal(e.key);
btn.textContent = e.key;
}
btn.classList.remove('listening');
window.removeEventListener('keydown', h, true);
saveState();
};
window.addEventListener('keydown', h, {
capture: true,
once: true
});
});
}
setupBind('tm-bind-trap', () => trapBind, v => {
trapBind = v;
});
setupBind('tm-bind-ballista', () => ballistaBind, v => {
ballistaBind = v;
});
const mkBtn = document.getElementById('tm-menu-key-btn');
const BLOCKED = new Set(['KeyW', 'KeyA', 'KeyS', 'KeyD', 'Escape', 'Space', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']);
mkBtn.addEventListener('click', () => {
mkBtn.classList.add('listening');
mkBtn.textContent = '...';
const h = e => {
e.preventDefault();
if (BLOCKED.has(e.code)) {
mkBtn.textContent = 'blocked!';
setTimeout(() => {
mkBtn.textContent = menuKey;
mkBtn.classList.remove('listening');
window.removeEventListener('keydown', h, true);
}, 800);
return;
}
menuKey = e.key;
mkBtn.textContent = menuKey;
mkBtn.classList.remove('listening');
window.removeEventListener('keydown', h, true);
saveState();
};
window.addEventListener('keydown', h, {
capture: true
});
});
window.addEventListener('keydown', e => {
if (e.key === menuKey && !_isInput()) {
e.preventDefault();
const visible = menu.style.display !== 'none';
menu.style.display = visible ? 'none' : '';
if (!visible) showMain();
}
}, true);
_makeDraggable(menu, document.getElementById('tm-header'));
Object.keys(state).forEach(k => {
const tog = menu.querySelector('[data-key="' + k + '"]');
if (tog && state[k]?.enabled) tog.classList.add('on');
});
updateLang();
});
function updateLang() {
const L = I18N[currentLang] || I18N.en;
const pairs = [
['autoheal', 'Auto Heal'],
['aimbow', 'Aim Bow'],
['autoreconnect', 'Auto Reconnect'],
['killmessage', 'Kill Message'],
['zoom', 'Zoom'],
['esp', 'ESP'],
['tracers', 'Tracers'],
['hud', 'HUD'],
['skinunlock', 'Skin Unlock'],
['walkunlock', 'Walk Unlock'],
['Gold_Bot', 'Gold Bot'],
['antiafk', 'Anti-AFK'],
];
pairs.forEach(([key]) => {
const row = document.querySelector('[data-key="' + key + '"]');
if (!row) return;
const p = row.parentElement;
const ds = p.querySelector('.tm-row-desc');
if (ds && L[key]) ds.textContent = L[key][1];
});
const set = (id, v) => {
const el = document.getElementById(id);
if (el) el.textContent = v;
};
set('tm-macros-desc', (L.macros || ['', 'PvP macros list'])[1]);
set('tm-macro-trap-desc', (L.trapMacro || ['', 'Place trap on bind key'])[1]);
set('tm-macro-ball-desc', (L.ballistaMacro || ['', 'Place ballista on bind key'])[1]);
set('tm-scout-desc', (L.scout || ['Scout', 'Second account marks players on minimap'])[1]);
set('tm-macro-hint', (L.escClear || ['Esc - clear bind'])[0]);
set('tm-settings-title', (L.settings || ['Settings'])[0].toUpperCase());
const thLbl = document.querySelector('.tm-setting-row .tm-setting-label');
const thLabels = document.querySelectorAll('.tm-setting-label');
if (thLabels[0]) thLabels[0].textContent = (L.theme || ['Theme'])[0];
if (thLabels[1]) thLabels[1].textContent = (L.language || ['Language'])[0];
const resetBtn = document.getElementById('tm-cfg-reset');
if (resetBtn) resetBtn.textContent = (L.resetCfg || ['Reset Config'])[0];
}
function _row(key, name, desc) {
return `<div class="tm-row"><div class="tm-row-info"><div class="tm-row-name">${name}</div><div class="tm-row-desc">${desc}</div></div><div class="tm-toggle${state[key]?.enabled?' on':''}" data-key="${key}"></div></div>`;
}
function _isInput() {
const el = document.activeElement;
if (!el) return false;
const tag = el.tagName.toLowerCase();
if (tag === 'input' || tag === 'textarea' || el.isContentEditable) return true;
try {
const ui = unsafeWindow.UI;
if (ui && (ui.chatVisible || ui.teamVisible)) return true;
} catch {}
return false;
}
function _makeDraggable(el, handle) {
let ox = 0,
oy = 0;
handle.addEventListener('mousedown', e => {
if (e.target.id === 'tm-gear') return;
e.preventDefault();
ox = e.clientX - el.offsetLeft;
oy = e.clientY - el.offsetTop;
const mv = e => {
el.style.left = (e.clientX - ox) + 'px';
el.style.top = (e.clientY - oy) + 'px';
el.style.right = 'auto';
};
document.addEventListener('mousemove', mv);
document.addEventListener('mouseup', () => document.removeEventListener('mousemove', mv), {
once: true
});
});
}
})();