blackhack

Cheat for brofist.io

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name           blackhack
// @version        20.9
// @description    Cheat for brofist.io
// @author         CiNoP
// @match          *://*.brofist.io/*

// @require        https://update.greasyfork.org/scripts/571913/1787411/blackhack-utils.js
// @require        https://update.greasyfork.org/scripts/571914/1789017/blackhack-map.js
// @require        https://update.greasyfork.org/scripts/571915/1796552/blackhack-ui.js
// @require        https://update.greasyfork.org/scripts/573606/1796986/blackhack-logic.js

// @icon           https://www.google.com/s2/favicons?sz=64&domain=brofist.io
// @grant          none
// @license        GPL-3.0-only
// @namespace      brofist.io 1st-cheat (FOR ALL MODES)
// ==/UserScript==
/* jshint esversion: 11 */
/* jshint asi: true */

(() => {
	'use strict';
	const BH = window.BH;

	console.log(`utilsVer: ${BH.utilsVer}, uiVer: ${BH.uiVer}, mapVer: ${BH.mapVer}, logicVer: ${BH.logicVer}`);

	const pathname = location.pathname.toLowerCase();
	const isTwoPlayer = pathname.includes("twoplayer");

	window.hack = {
		mode: null, modeController: null, gp: null, networkHandler: null, client: null,
		originalMakeMeGhost: null, originalSocketEmit: null,
		_isMapReloading: false, _mapSocket: null
	};

	const hackReady = new Promise(resolve => {
		if (isTwoPlayer) {
			const c = () => { if (window.hack.mode) resolve(window.hack); else requestAnimationFrame(c); };
			requestAnimationFrame(c);
		} else {
			const traps = { pingCheckCount: ['networkHandler'], othersPlayerNetworkData: ['mode', 'modeController'] };
			let cap = 0; const tot = Object.keys(traps).length;
			for (const[p, tgts] of Object.entries(traps))
				Object.defineProperty(Object.prototype, p, {
					set(v) {
						delete Object.prototype[p]; this[p] = v;
						for (const t of tgts) window.hack[t] = this;
						if (++cap >= tot) { window.hack.gp = window.gp; window.hack.client = window.client; resolve(window.hack); }
					}, configurable: true
				});
		}
	});

	Promise.all([hackReady, BH.lzmaReady]).then(() => main(isTwoPlayer));

	function main(is2pa) {
		const hack = window.hack;
		Object.defineProperty(hack.client, 'runPhysics', { get: () => true, set: () => {} });
		const st = document.createElement('style'); st.textContent = '#startTime{display:none!important}'; document.head.appendChild(st);

		hack.getLP = () => is2pa ? hack.modeController.localPlayer : hack.modeController.player?.gpData ?? null;
		hack.getGPD = () => is2pa ? hack.mode.player?.gpData ?? null : hack.getLP();

		BH.Logic.initHooks(hack, is2pa);

		function patchSocket(sock) {
			if (!sock || sock.__patched) return; sock.__patched = true;
			hack.originalSocketEmit = sock.emit;
			sock.emit = function (ev, ...a) { if (ev === "rGho" && hack.vars.imm) return; return hack.originalSocketEmit.apply(this, [ev, ...a]); };
			const oo = sock.onevent;
			sock.onevent = function (pkt) {
				if (pkt?.data?.[0] === 'changeMap') {
					const raw = pkt.data[1];
					if (!hack._isMapReloading) hack.vars.mapDataBackup = Array.isArray(raw) ? raw.slice() : raw;
					if (hack.vars.xrayGates) {
						hack.vars.xrayGates.forEach(item => {
							if (item.gfx && item.gfx.parent) item.gfx.parent.removeChild(item.gfx);
							if (item.gfx && typeof item.gfx.destroy === 'function') item.gfx.destroy();
						});
						hack.vars.xrayGates =[];
					}
					try {
						let d = raw;
						if (typeof d === 'string') d = JSON.parse(d);
						if (Array.isArray(d) && typeof d[0] === 'number' && window.LZMA) d = JSON.parse(window.LZMA.decompress(d));
						window.mapData = d;
					} catch (_) { window.mapData = raw; }
					if (hack.vars.layoutMode) {
						try {
							const cl = Array.isArray(raw) ? raw.slice() : raw;
							const clean = BH.parseMapToLayout(cl);
							const j = JSON.stringify(clean);
							pkt.data[1] = window.LZMA?.compress ? LZMA.compress(j, 1) : j;
						} catch (_) {}
					}
				}
				return oo.call(this, pkt);
			};
			hack._mapSocket = sock;
		}

		function reloadMap() {
			if (!hack.vars.mapDataBackup || !hack._mapSocket?.onevent) return false;
			hack._isMapReloading = true;
			const bk = Array.isArray(hack.vars.mapDataBackup) ? hack.vars.mapDataBackup.slice() : hack.vars.mapDataBackup;
			hack._mapSocket.onevent({ type: 2, data: ['changeMap', bk] });
			hack._isMapReloading = false; return true;
		}

		function applyNoclipState(en) {
			const lp = hack.getLP();
			if (lp?.p) { lp.p.collisionResponse = !en; lp.p.massMultiplier[1] = en ? 0 : hack.vars.playerMass; }
		}
		function applyGravNoclipState(en) {
			const g = hack.getGPD(), lp = hack.getLP();
			if (lp?.p) { lp.p.collisionResponse = !en; lp.p.gravityScale = en ? 0 : 1; if (!en) lp.p.massMultiplier[1] = hack.vars.playerMass; }
			if (g?.p) g.p.gravityScale = en ? 0 : 1;
			if (!en) { g?.setAngle(0); if (lp && lp !== g) lp.setAngle(0); }
		}

		if (hack.networkHandler) {
			patchSocket(hack.networkHandler.gsSocket);
			const oc = hack.networkHandler.connectToGs;
			if (oc) hack.networkHandler.connectToGs = function (...a) { oc.apply(this, a); patchSocket(this.gsSocket); };
		}

		const cfg = BH.loadSettings();
		hack.vars = {
			imm: false, noclip: false, gravNoclip: false,
			mult: { enabled: false, value: 1, uiValue: cfg.mult },
			gravNoclipGravScale: cfg.gravScale, jumpHeight: cfg.jumpHeight,
			playerMass: cfg.mass, playerDamping: cfg.damping,
			blacklisted: { names: [], data:[] },
			shiftUsedForBlacklist: false, arrowFirstPressed: null,
			layoutMode: false, mapDataBackup: null, layoutSavedPos: null,
			layoutAlpha: { collision: cfg.alphaCollision, poison: cfg.alphaPoison, functional: cfg.alphaFunctional, background: 0xCCCCCC },
			numpadMode: false,
			xrayGates:[],
			tpTargetIndex: null,
			tpToPlayerEnabled: false,
			tpTargetName: "Никто",
			lastDoorSavedPos: null,
			homeCycleIndex: 0,
			// Кеш игрового чекпоинта (массив [x, y] из логики игры)
			lastCheckpointPos: null
		};

		hack.keyBinds = { F1:false,F2:false,F3:false,F5:false,SHIFT:false,HOME:false,END:false,ARROW_UP:false,ARROW_DOWN:false,NUMPAD_MINUS:false,DIGIT_1:false,DIGIT_2:false,DIGIT_3:false,DIGIT_5:false };
		hack.prevKeys = {};
		const keyMap = { F1:'F1',F2:'F2',F3:'F3',F5:'F5',Shift:'SHIFT',Home:'HOME',End:'END',ArrowUp:'ARROW_UP',ArrowDown:'ARROW_DOWN' };
		const codeMap = { NumpadSubtract:'NUMPAD_MINUS',Digit1:'DIGIT_1',Numpad1:'DIGIT_1',Digit2:'DIGIT_2',Numpad2:'DIGIT_2',Digit3:'DIGIT_3',Numpad3:'DIGIT_3',Digit5:'DIGIT_5',Numpad5:'DIGIT_5' };

		document.addEventListener('keydown', e => {
			const cm = codeMap[e.code];
			if (cm) { if (cm.startsWith('DIGIT_') && hack.vars.numpadMode) { const t = (e.target.tagName || '').toLowerCase(); if (t !== 'input' && t !== 'textarea' && !e.target.isContentEditable) e.preventDefault(); } if (cm === 'NUMPAD_MINUS') e.preventDefault(); hack.keyBinds[cm] = true; }
			const m = keyMap[e.key]; if (!m) return; e.preventDefault();
			if (m === 'ARROW_UP' && !hack.keyBinds.ARROW_UP) hack.vars.arrowFirstPressed = 'up';
			if (m === 'ARROW_DOWN' && !hack.keyBinds.ARROW_DOWN) hack.vars.arrowFirstPressed = 'down';
			hack.keyBinds[m] = true;
		});
		document.addEventListener('keyup', e => {
			const cm = codeMap[e.code]; if (cm) hack.keyBinds[cm] = false;
			const m = keyMap[e.key]; if (!m) return; hack.keyBinds[m] = false;
			if (m === 'ARROW_UP' || m === 'ARROW_DOWN') {
				if (!hack.keyBinds.ARROW_UP && !hack.keyBinds.ARROW_DOWN) hack.vars.arrowFirstPressed = null;
				else hack.vars.arrowFirstPressed = hack.keyBinds.ARROW_UP ? 'up' : 'down';
			}
		});
		window.addEventListener('blur', () => { for (const k in hack.keyBinds) hack.keyBinds[k] = false; hack.vars.arrowFirstPressed = null; });

		function isPlayerOnline(n) { const o = hack.mode.otherPlayers; if (!o) return false; for (let i = 0; i < o.length; i++) if (o[i]?.myName === n) return true; return false; }

		if (hack.mode.createKickGui) {
			const _o = hack.mode.createKickGui;
			hack.mode.createKickGui = function (e) {
				const _on = e.on.bind(e);
				e.on = function (ev, fn) {
					return _on(ev, function (...a) {
						if (hack.keyBinds.SHIFT) {
							const nm = this.ref.refP.name.getText();
							if (!hack.vars.blacklisted.names.includes(nm)) {
								hack.vars.blacklisted.names.push(nm);
								hack.vars.blacklisted.data.push([this.ref.refP.g.myIndex, nm]);
								this.ref.refP.g.visible = false;
								if (this.ref.refP.p) this.ref.refP.p.collisionResponse = false;
							}
							hack.vars.shiftUsedForBlacklist = true;
							if (hack._refreshBL) hack._refreshBL();
							return;
						}
						if (this.ref && this.ref.refP && this.ref.refP.g) {
							hack.vars.tpTargetIndex = this.ref.refP.g.myIndex;
							hack.vars.tpTargetName = this.ref.refP.name.getText();
						}
						return fn.apply(this, a);
					});
				};
				_o.call(this, e);
				e.on = _on;
			};
		}

		function restoreAfterReload() {
			let f = 0;
			const r = () => {
				if (++f < 8) { requestAnimationFrame(r); return; } // Увеличиваем задержку для гарантии инициализации объекта lp игрой
				const lp = hack.getLP();
				if (!lp) return;

				// 1. Принудительно восстанавливаем IMM, если он был включен
				if (hack.vars.imm) {
					const g = hack.getGPD();
					if (g) g.me = void 0;
					if (is2pa && hack.mode) hack.mode.makeMeGhost = function() {};
				}

				// 2. Восстанавливаем физические состояния
				if (hack.vars.noclip) applyNoclipState(true);
				if (hack.vars.gravNoclip) applyGravNoclipState(true);

				// 3. Сначала телепортируем игрока в точку, где он стоял до перезагрузки
				const pos = hack.vars.layoutSavedPos;
				if (pos) {
					lp.setX(pos.x);
					lp.setY(pos.y);
					if (lp.p) { lp.p.velocity[0] = 0; lp.p.velocity[1] = 0; }
				}

				// 4. Восстанавливаем игровой чекпоинт ПОСЛЕ телепортации
				// Это предотвращает перезапись чекпоинта координатами спавна
				if (hack.vars.lastCheckpointPos) {
					lp.lastCheckPoint = [...hack.vars.lastCheckpointPos];
				}
			};
			requestAnimationFrame(r);
		}

		hack.funcs = { handlers: {
			toggleImm: en => {
				hack.vars.imm = en; const g = hack.getGPD(); if (g) g.me = en ? void 0 : true;
				if (en) { if (hack.mode.makeMeGhost && !hack.originalMakeMeGhost) hack.originalMakeMeGhost = hack.mode.makeMeGhost; if (hack.mode.makeMeGhost) hack.mode.makeMeGhost = function () {}; }
				else if (hack.originalMakeMeGhost) hack.mode.makeMeGhost = hack.originalMakeMeGhost;
			},
			toggleNoclip: en => {
				hack.vars.noclip = en; applyNoclipState(en);
				if (hack.vars.mult.enabled) hack.vars.mult.value = en ? hack.vars.mult.uiValue : 1;
				if (en && hack.vars.gravNoclip) { hack.vars.gravNoclip = false; applyGravNoclipState(false); }
			},
			toggleGravNoclip: en => {
				if (en && hack.vars.noclip) hack.funcs.handlers.toggleNoclip(false);
				hack.vars.gravNoclip = en; applyGravNoclipState(en);
				if (hack.vars.mult.enabled) hack.vars.mult.value = en ? hack.vars.mult.uiValue : 1;
			},
			toggleMult: en => {
				if (!hack.vars.noclip && !hack.vars.gravNoclip) return;
				hack.vars.mult.enabled = en; hack.vars.mult.value = en ? hack.vars.mult.uiValue : 1;
			},
			toggleLayout: en => {
				// Включаем IMM принудительно на время перезагрузки
				const wasImm = hack.vars.imm;
				hack.funcs.handlers.toggleImm(true);

				hack.vars.layoutMode = en;
				const lp = hack.getLP();
				if (lp) {
					hack.vars.layoutSavedPos = { x: lp.getX(), y: lp.getY() };
					// Перед перезагрузкой сохраняем текущий реальный чекпоинт
					if (lp.lastCheckPoint) hack.vars.lastCheckpointPos = [...lp.lastCheckPoint];
				}

				if (reloadMap()) {
					restoreAfterReload();
				}

				try {
					const r = hack.gp.renderer || hack.gp.app?.renderer;
					if (r) { if (en) { if (hack._origBg === undefined) hack._origBg = r.backgroundColor; r.backgroundColor = hack.vars.layoutAlpha.background; } else if (hack._origBg !== undefined) r.backgroundColor = hack._origBg; }
				} catch (_) {}

				// Возвращаем IMM в пользовательское состояние (restoreAfterReload применит его)
				hack.vars.imm = wasImm;
			},
			tpPlayer: (x, y) => { const lp = hack.getLP(); if (lp) { lp.setX(x); lp.setY(y); } },
			toggleNumpadMode: en => { hack.vars.numpadMode = en; },
			blacklistAdd: n => {
				n = (n || '').trim(); if (!n || hack.vars.blacklisted.names.includes(n)) return;
				hack.vars.blacklisted.names.push(n);
				for (const i of BH.Logic.getIdxByName(hack, n)) { hack.vars.blacklisted.data.push([i, n]); const p = hack.mode.otherPlayers[i]; if (p?.gpData) { p.gpData.g.visible = false; if (p.gpData.p) p.gpData.p.collisionResponse = false; } }
				if (hack._refreshBL) hack._refreshBL();
			},
			blacklistRemove: n => {
				const ni = hack.vars.blacklisted.names.indexOf(n); if (ni === -1) return;
				hack.vars.blacklisted.names.splice(ni, 1);
				for (const [i] of hack.vars.blacklisted.data.filter(e => e?.[1] === n)) { const p = hack.mode.otherPlayers[i]; if (p?.gpData) { p.gpData.g.visible = true; if (p.gpData.p) p.gpData.p.collisionResponse = true; } }
				hack.vars.blacklisted.data = hack.vars.blacklisted.data.filter(e => e[1] !== n);
				if (hack._refreshBL) hack._refreshBL();
			}
		}};
		window.hackFunctions = hack.funcs.handlers;

		hack.reloadMap = reloadMap;
		hack.restoreAfterReload = restoreAfterReload;
		hack.isPlayerOnline = isPlayerOnline;

		function updateLoop() {
			const binds = hack.keyBinds, prev = hack.prevKeys, fn = hack.funcs.handlers, v = hack.vars;
			const lp = hack.getLP();
			const jp = k => binds[k] && !prev[k], jr = k => !binds[k] && prev[k];

			// Постоянно кешируем актуальный чекпоинт из свойств игрока
			// Важно: если lp.lastCheckPoint содержит [null, null], значит чекпоинт не взят
			if (lp?.lastCheckPoint && lp.lastCheckPoint[0] !== null) {
				v.lastCheckpointPos = [...lp.lastCheckPoint];
			}

			if (jp('NUMPAD_MINUS')) fn.toggleNumpadMode(!v.numpadMode);
			if (is2pa) {
				if (jp('HOME')) {
					const spawn = hack.gp.list.find(i => i?.id?.includes('spawn'));
					if (v.lastDoorSavedPos && v.homeCycleIndex === 0) {
						fn.tpPlayer(v.lastDoorSavedPos.x, v.lastDoorSavedPos.y);
						v.homeCycleIndex = 1;
					} else if (spawn) {
						fn.tpPlayer(spawn.getX(), spawn.getY());
						v.homeCycleIndex = 0;
					}
				}
				if (jp('END')) {
					const door = hack.gp.list.find(i => i?.id?.includes('door'));
					if (door && lp) {
						v.lastDoorSavedPos = { x: lp.getX(), y: lp.getY() };
						v.homeCycleIndex = 0;
						fn.tpPlayer(door.getX(), door.getY());
					}
				}
			}
			if (jp('F1') || (v.numpadMode && jp('DIGIT_1'))) fn.toggleNoclip(!v.noclip);
			if (jp('F2') || (v.numpadMode && jp('DIGIT_2'))) fn.toggleImm(!v.imm);
			if (jp('F3') || (v.numpadMode && jp('DIGIT_3'))) fn.toggleGravNoclip(!v.gravNoclip);
			if (jp('F5') || (v.numpadMode && jp('DIGIT_5'))) fn.toggleLayout(!v.layoutMode);
			if (jr('SHIFT')) { if (v.shiftUsedForBlacklist) v.shiftUsedForBlacklist = false; else fn.toggleMult(!v.mult.enabled); }

			if (v.noclip) applyNoclipState(true);
			if (v.gravNoclip) { if (lp?.p) lp.p.collisionResponse = false; }
			if (is2pa && v.imm) { const g = hack.getGPD(); if (g?.me !== undefined) g.me = void 0; }
			if (!v.noclip && !v.gravNoclip) { if (lp?.p) { lp.p.massMultiplier[1] = v.playerMass; lp.p.damping = v.playerDamping; } }

			try {
				BH.Logic.enforceBL(hack);
				BH.Logic.updateTP(hack);
				BH.Logic.updateXray(hack);
			} catch (e) { console.error("[BH] Loop Logic Error: ", e); }

			const dNc = document.getElementById('hk-d-nc'), dIm = document.getElementById('hk-d-im');
			const dGn = document.getElementById('hk-d-gn'), dLm = document.getElementById('hk-d-lm');
			if (dNc) dNc.style.background = v.noclip ? '#0f0' : '#f44';
			if (dIm) dIm.style.background = v.imm ? '#0f0' : '#f44';
			if (dGn) dGn.style.background = v.gravNoclip ? '#0f0' : '#f44';
			if (dLm) dLm.style.background = v.layoutMode ? '#0f0' : '#f44';

			for (const k in binds) prev[k] = binds[k];
			requestAnimationFrame(updateLoop);
		}
		requestAnimationFrame(updateLoop);

		const cSS = () => { const lf = hack.client.loopFunctions; for (let i = 0; i < lf.length; i++) if (lf[i] && (lf[i].timeOut === 200 || lf[i].timeOut === 150)) { lf[i].timeOut = 10; return; } requestAnimationFrame(cSS); };
		cSS();

		BH.createZoom();
		BH.createUI();
		console.log("blackhack v20.9 loaded");
	}
})();