blackhack-logic

Game logic, physics hooks and update extensions for blackhack

Este script não deve ser instalado diretamente. É uma biblioteca destinada a ser incluída por outros scripts através da diretiva de metadados // @require https://update.greasyfork.org/scripts/573606/1832145/blackhack-logic.js

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

// ==UserScript==
// @name           blackhack-logic
// @namespace      brofist.io 1st-cheat (FOR ALL MODES)
// @version        1.2
// @description    Game logic, physics hooks and update extensions for blackhack
// @author         CiNoP
// @license        GPL-3.0-only
// ==/UserScript==

/* blackhack-logic.js */
(function () {
	'use strict';
	const BH = window.BH = window.BH || {};
	BH.logicVer = 1.2;

	// ─── Bind Hack (Runs immediately) ───
	const _bind = Function.prototype.bind;
	const pathname = location.pathname.toLowerCase();
	const isTwoPlayer = pathname.includes("twoplayer");
	const bm = { processOthers: false, movement: false, _allFound: false, _origPO: null, _origMV: null };

	Function.prototype.bind = function (ctx, ...args) {
		const b = _bind.apply(this, [ctx, ...args]);
		if (!bm._allFound) {
			try {
				const s = this.toString();
				if (!bm.processOthers && s.includes('othersPlayerNetworkData') && s.includes('oldPlayersIndex')) {
					b.__hk_processOthers = true; bm.processOthers = true; bm._origPO = this;
				}
				if (!bm.movement && s.includes('moveLeft') && (s.includes('raycast') || s.includes('velocity[1]'))) {
					b.__hk_movement = true; bm.movement = true; bm._origMV = this;
				}
				if (bm.processOthers && bm.movement) bm._allFound = true;
			} catch (_) {}
		} else {
			if (this === bm._origPO) b.__hk_processOthers = true;
			if (this === bm._origMV) b.__hk_movement = true;
		}
		if (isTwoPlayer && window.hack && !window.hack.mode && ctx && ctx.playerData && ctx.twoPlayerController) {
			window.hack.mode = ctx; window.hack.modeController = ctx.twoPlayerController;
			window.hack.gp = ctx.gp; window.hack.networkHandler = ctx.networkHandler;
			window.hack.client = ctx.client;
		}
		return b;
	};

	BH.Logic = {
		diff: (a, b) => a.filter(x => !b.includes(x)),

		getIdxByName: (hack, n) => {
			const r =[], o = hack.mode.otherPlayers;
			for (let i = 0; i < o.length; i++) if (o[i]?.myName === n) r.push(i);
			return r;
		},

		processBL: (hack) => {
			const bl = hack.vars.blacklisted;
			for (const n of bl.names) {
				for (const i of BH.Logic.getIdxByName(hack, n)) {
					if (!bl.data.some(e => e?.[0] === i && e?.[1] === n)) bl.data.push([i, n]);
				}
			}
			if (bl.data.length) bl.data = bl.data.filter(e => e?.[1] && bl.names.includes(e[1]));
		},

		isIdxBL: (hack, i) => hack.vars.blacklisted.data.some(e => e?.[0] === i),

		enforceBL: (hack) => {
			const bl = hack.vars.blacklisted; if (!bl.data.length) return;
			const o = hack.mode.otherPlayers;
			for (const [i] of bl.data) {
				const p = o[i]; if (!p?.gpData) continue;
				p.gpData.g.visible = false;
				if (p.gpData.p) p.gpData.p.collisionResponse = false;
			}
			const msgs = hack.mode.othersPlayerMsgNetworkData;
			if (msgs) {
				const s = new Set(bl.data.map(e => e[0]));
				for (let i = 0; i < msgs.length; i++) msgs[i] = msgs[i].filter(m => !s.has(m[0]));
			}
		},

		getEV: (hack) => {
			const u = hack.keyBinds.ARROW_UP, d = hack.keyBinds.ARROW_DOWN;
			if (u && d) {
				if (hack.vars.arrowFirstPressed === 'up') return 'down';
				if (hack.vars.arrowFirstPressed === 'down') return 'up';
				return null;
			}
			return u ? 'up' : d ? 'down' : null;
		},

		initHooks: (hack, is2pa) => {
			const customPO = function () {
				const mode = hack.mode, gp = hack.gp, nh = hack.networkHandler;
				BH.Logic.processBL(hack);
				if (!mode.othersPlayerNetworkData.length) return;
				var cur =[];
				for (var x = 0; x < mode.othersPlayerNetworkData.length; x++) {
					for (var y = 0; y < mode.othersPlayerNetworkData[x].length; y++) {
						var d = mode.othersPlayerNetworkData[x][y], idx = d[0], xV = d[1], yV = d[2], xA = d[3], yA = d[4];
						if (BH.Logic.isIdxBL(hack, idx)) continue;
						if (mode.otherPlayers[idx] == null) {
							mode.oldPlayersIndex.push(idx);
							var p = {}; p.myName = ""; p.mySkin = 0; p.reset = Infinity;
							p.gpData = is2pa ? mode.basePlayer.createPlayer(mode.playerData) : mode.createPlayer();
							p.gpData.g.myIndex = idx; mode.otherPlayers[idx] = p;
							if (nh?.gsSocket) nh.gsSocket.emit("rBio", idx);
							gp.gWorld.removeChild(p.gpData.g); gp.gWorld.mid.addChild(p.gpData.g);
						}
						if (10 < mode.otherPlayers[idx].reset) { mode.otherPlayers[idx].gpData.setX(xA); mode.otherPlayers[idx].gpData.setY(yA); mode.otherPlayers[idx].reset = 0; }
						mode.otherPlayers[idx].reset++;
						mode.otherPlayers[idx].gpData.p.velocity[0] = xV;
						mode.otherPlayers[idx].gpData.p.velocity[1] = yV;
						cur.push(idx);
					}
				}
				var r = BH.Logic.diff(mode.oldPlayersIndex, cur);
				for (var i = 0; i < r.length; i++) {
					var p2 = mode.otherPlayers[r[i]];
					if (p2 != null) {
						gp.gWorld.mid.removeChild(p2.gpData.g); gp.pWorld.removeBody(p2.gpData.p);
						gp.list[gp.list.indexOf(p2.gpData)] = null; gp.deleteCounter++; mode.otherPlayers[r[i]] = null;
					}
				}
				mode.oldPlayersIndex = cur; mode.othersPlayerNetworkData =[];
			};

			const customMV = function () {
				const ctrl = hack.modeController, p = hack.getLP();
				const mult = hack.vars.mult.value, jh = hack.vars.jumpHeight, gs = hack.vars.gravNoclipGravScale;
				if (!p || !ctrl) return;

				if (hack.vars.noclip) {
					p.p.velocity[0] = ctrl.moveRight ? 3 * mult : ctrl.moveLeft ? -3 * mult : 0;
					p.p.velocity[1] = ctrl.moveUp ? 3 * mult : ctrl.moveDown ? -3 * mult : 0;
					return;
				}

				if (hack.vars.gravNoclip) {
					p.p.velocity[0] = ctrl.moveRight ? 3 * mult : ctrl.moveLeft ? -3 * mult : 0;
					const v = BH.Logic.getEV(hack);
					if (v === 'up') { p.p.gravityScale = -gs; p.setAngle(180); }
					else if (v === 'down') { p.p.gravityScale = gs; p.setAngle(0); }
					else p.p.gravityScale = 0;
					const gd = p.p.gravityScale;
					let sj = false, jd = 0, rsY, reY;
					const ex = p.getX(), ty = p.getY(), hH = 50, rL = 50, n = ex - 15, r = 30 / 11;
					if (gd > 0 && ctrl.moveUp) { sj = true; jd = jh; rsY = ty + (hH - 1); reY = rsY + rL; }
					else if (gd < 0 && ctrl.moveDown) { sj = true; jd = -jh; rsY = ty - (hH - 1); reY = rsY - rL; }
					if (sj) {
						const ph = is2pa ? ctrl.physics : hack.gp;
						for (let i = 0; i < 12; i++) {
							const ox = n + i * r;
							p.ray.from = [ph.xAxis(ox, 0), ph.yAxis(rsY, 0)]; p.ray.to =[ph.xAxis(ox, 0), ph.yAxis(reY, 0)];
							p.ray.update(); p.ray.result.reset(); p.ray.hitPoint = [Infinity, Infinity];
							if (hack.gp.pWorld.raycast(p.ray.result, p.ray))
								if (p.ray.result.shape.ref.getCollision() && p.ray.result.getHitDistance(p.ray) < 0.15) { p.p.velocity[1] = jd; break; }
						}
					}
					return;
				}

				// ─── РАСШИРЕННОЕ ДВИЖЕНИЕ (Extended Movement) ───
				if (hack.vars.extMove) {
					const ph = is2pa ? ctrl.physics : hack.gp;
					const pmd = hack.playerMoveData;
					const currentTime = Date.now();
					const dashCooldown = 250;
					const dashDistance = 2.5;
					const dashSpeed = 25;

					// Проверка нахождения на земле через вертикальный луч вниз
					let grounded = false;
					const ex = p.getX(), ty = p.getY(), n = ex - 15, r = 30 / 11;
					for (let i = 0; i < 12; i++) {
						const ox = n + i * r, oy1 = ty + 49, oy2 = oy1 + 50;
						p.ray.from = [ph.xAxis(ox, 0), ph.yAxis(oy1, 0)];
						p.ray.to = [ph.xAxis(ox, 0), ph.yAxis(oy2, 0)];
						p.ray.update();
						p.ray.result.reset();
						p.ray.hitPoint = [Infinity, Infinity];
						if (hack.gp.pWorld.raycast(p.ray.result, p.ray)) {
							if (p.ray.result.shape.ref.getCollision() && p.ray.result.getHitDistance(p.ray) < 0.05) {
								grounded = true;
								break;
							}
						}
					}

					if (grounded) {
						pmd.airDashAvailable = true;
					}

					// Запоминание направления взгляда
					if (ctrl.moveLeft) {
						pmd.lastHorizontalDirection = -1;
					} else if (ctrl.moveRight) {
						pmd.lastHorizontalDirection = 1;
					}

					// Запуск рывка (Компактный DRY-анализ направления)
					if (hack.vars.triggerDash && (currentTime - pmd.lastDashTime >= dashCooldown) && (grounded || pmd.airDashAvailable)) {
						let dashType = null;
						if (ctrl.moveDown && !pmd.isDashingDown) {
							dashType = 'down';
						} else if (ctrl.moveUp && !pmd.isDashingUp) {
							dashType = 'up';
						} else if (!pmd.isDashing) {
							dashType = 'horizontal';
						}

						if (dashType) {
							pmd.lastDashTime = currentTime;
							pmd.dashDuration = (dashDistance / dashSpeed) * 1000;
							pmd.dashEndTime = currentTime + pmd.dashDuration;
							if (!grounded) {
								pmd.airDashAvailable = false;
							}

							if (dashType === 'down') pmd.isDashingDown = true;
							else if (dashType === 'up') pmd.isDashingUp = true;
							else {
								pmd.isDashing = true;
								pmd.dashVelocity = dashSpeed * pmd.lastHorizontalDirection;
							}
							hack.vars.triggerDash = false; // Сброс триггера
						}
					}

					// Отработка активного состояния рывка
					let currentlyDashing = false;
					let dashVelX = 0, dashVelY = 0;
					let activeDashProp = null;

					if (pmd.isDashingDown) {
						dashVelY = -dashSpeed;
						activeDashProp = 'isDashingDown';
						currentlyDashing = true;
					} else if (pmd.isDashingUp) {
						dashVelY = dashSpeed;
						activeDashProp = 'isDashingUp';
						currentlyDashing = true;
					} else if (pmd.isDashing) {
						dashVelX = pmd.dashVelocity;
						activeDashProp = 'isDashing';
						currentlyDashing = true;
					}

					if (currentlyDashing) {
						p.p.velocity[0] = dashVelX;
						p.p.velocity[1] = dashVelY;
						p.p.collisionResponse = false; // Отключаем столкновения на время рывка

						if (currentTime >= pmd.dashEndTime) {
							pmd[activeDashProp] = false;
							p.p.velocity[0] = 0;
							p.p.velocity[1] = 0;
							if (!hack.vars.noclip && !hack.vars.gravNoclip) {
								p.p.collisionResponse = true; // Возвращаем коллизию
							}
						}
						return; // Игнорируем стандартную физику в кадре рывка
					}

					// Обычное движение влево/вправо
					if (ctrl.moveRight) {
						p.p.velocity[0] = 3 * mult;
					} else if (ctrl.moveLeft) {
						p.p.velocity[0] = -3 * mult;
					}

					// Прыжки и двойные прыжки по клавише Z
					const gravityScale = p.p.gravityScale !== undefined ? p.p.gravityScale : 1;
					if (grounded) {
						pmd.isDoubleJumpAllowed = true;
						if (hack.vars.triggerJump) {
							hack.vars.triggerJump = false;
							p.p.velocity[1] = jh * gravityScale;
						}
					} else if (pmd.isDoubleJumpAllowed && hack.vars.triggerJump) {
						hack.vars.triggerJump = false;
						p.p.velocity[1] = jh * gravityScale;
						pmd.isDoubleJumpAllowed = false;
					}
					return;
				}

				// Оригинальная обработка движения (если расширенное движение выключено)
				if (ctrl.moveRight) p.p.velocity[0] = 3;
				if (ctrl.moveLeft) p.p.velocity[0] = -3;
				if (!ctrl.moveUp) return;
				const ex = p.getX(), ty = p.getY(), n = ex - 15, r = 30 / 11;
				const ph = is2pa ? ctrl.physics : hack.gp;
				for (let i = 0; i < 12; i++) {
					const ox = n + i * r, oy1 = ty + 49, oy2 = oy1 + 50;
					p.ray.from =[ph.xAxis(ox, 0), ph.yAxis(oy1, 0)]; p.ray.to =[ph.xAxis(ox, 0), ph.yAxis(oy2, 0)];
					p.ray.update(); p.ray.result.reset(); p.ray.hitPoint = [Infinity, Infinity];
					if (hack.gp.pWorld.raycast(p.ray.result, p.ray))
						if (p.ray.result.shape.ref.getCollision() && p.ray.result.getHitDistance(p.ray) < 0.05) { p.p.velocity[1] = jh; break; }
				}
			};

			let poI = false, mvI = false;
			function cih() {
				const lf = hack.client.loopFunctions;
				if (!poI) { const i = lf.findIndex(e => e?.fun?.__hk_processOthers || (e?.fun?.toString().includes('othersPlayerNetworkData') && e.fun.toString().includes('oldPlayersIndex'))); if (i !== -1) { lf[i].fun = customPO; poI = true; } }
				if (!mvI) { const i = lf.findIndex(e => e?.fun?.__hk_movement || (e?.fun?.toString().includes('moveLeft') && (e.fun.toString().includes('raycast') || e.fun.toString().includes('velocity[1]')))); if (i !== -1) { lf[i].fun = customMV; mvI = true; } else if (is2pa && lf.length > 7) { lf[7].fun = customMV; mvI = true; } }
				if (!poI || !mvI) requestAnimationFrame(cih);
			}
			cih();

			const oal = hack.client.addLoopFunction;
			hack.client.addLoopFunction = function (f, p, t, e) {
				if (f) {
					if (f.__hk_processOthers || (f.toString().includes('othersPlayerNetworkData') && f.toString().includes('oldPlayersIndex'))) return oal.call(this, customPO, p, t, e);
					if (f.__hk_movement || (f.toString().includes('moveLeft') && (f.toString().includes('raycast') || f.toString().includes('velocity[1]')))) return oal.call(this, customMV, p, t, e);
				}
				return oal.call(this, f, p, t, e);
			};
		},

		updateTP: (hack) => {
			const v = hack.vars;
			if (v.tpToPlayerEnabled && v.tpTargetName !== "Никто") {
				let target = hack.mode.otherPlayers[v.tpTargetIndex];
				if (!target || target.myName !== v.tpTargetName) {
					let foundNewIndex = -1;
					const oPlayers = hack.mode.otherPlayers;
					for (let i = 0; i < oPlayers.length; i++) {
						if (oPlayers[i] && oPlayers[i].myName === v.tpTargetName) { foundNewIndex = i; break; }
					}
					if (foundNewIndex !== -1) {
						v.tpTargetIndex = foundNewIndex;
						target = oPlayers[foundNewIndex];
					}
				}
				if (target && target.gpData && target.reset <= 10) {
					const lp = hack.getLP();
					if (lp) {
						lp.setX(target.gpData.getX());
						lp.setY(target.gpData.getY());
						if (lp.p) { lp.p.velocity[0] = 0; lp.p.velocity[1] = 0; }
					}
				}
			}
		},

		updateXray: (hack) => {
			const v = hack.vars;
			if (v.layoutMode) {
				if (v.xrayGates.length === 0 && hack.gp && hack.gp.list) {
					const pixi = window.pixi || window.PIXI;
					if (pixi && pixi.Graphics) {
						hack.gp.list.forEach(obj => {
							if (obj && obj.id && obj.id.toLowerCase().startsWith('gate')) {
								const parts = obj.id.split(':');
								const action = parts[parts.length - 1];
								obj.shapes.forEach((shape, idx) => {
									if (shape && shape.make !== 3 && shape.g && shape.g.parent) {
										const gfx = new pixi.Graphics();
										const color = action === "1" ? 0xFF00FF : 0x00FFFF;
										let baseW = 50, baseH = 50, baseR = 25;
										if (shape.p) {
											if (shape.p.width) baseW = shape.p.width * 100;
											if (shape.p.height) baseH = shape.p.height * 100;
											if (shape.p.radius) baseR = shape.p.radius * 100;
										} else if (shape.g) {
											const sx = shape.g.scale ? Math.abs(shape.g.scale.x) : 1;
											const sy = shape.g.scale ? Math.abs(shape.g.scale.y) : 1;
											if (shape.g.width) baseW = shape.g.width / sx;
											if (shape.g.height) baseH = shape.g.height / sy;
											baseR = baseW / 2;
										}
										const type = typeof shape.getType === 'function' ? shape.getType() : shape.type;
										if (type === 2) { gfx.circle(0, 0, baseR); }
										else if (type === 5) {
											const triH = baseW * Math.sqrt(3) / 2;
											const rOff = triH / 6;
											gfx.moveTo(-baseW / 2, triH / 2 - rOff); gfx.lineTo(baseW / 2, triH / 2 - rOff); gfx.lineTo(0, -triH / 2 - rOff); gfx.closePath();
										} else { gfx.rect(-baseW / 2, -baseH / 2, baseW, baseH); }

										gfx.fill({ color: color, alpha: 0.15 });
										gfx.stroke({ width: 3, color: color, alpha: 1 });
										if (hack.gp.gWorld && hack.gp.gWorld.mid) hack.gp.gWorld.mid.addChild(gfx);

										let textObj = null;
										if (idx === 0) {
											textObj = new pixi.Text(obj.id, { fontFamily: 'monospace', fontSize: 14, fill: color, stroke: 0x000000, strokeThickness: 3, fontWeight: 'bold' });
											textObj.anchor.set(0.5, 1);
											hack.gp.gWorld.mid.addChild(textObj);
										}
										v.xrayGates.push({ gfx: gfx, src: shape, text: textObj, baseH: baseH, baseR: baseR, type: type });
									}
								});
							}
						});
					}
				}
				v.xrayGates.forEach(item => {
					const { src, gfx, text, baseH, baseR, type } = item;
					if (src && gfx) {
						if (!src.g || !src.g.parent) { gfx.visible = false; if (text) text.visible = false; return; }
						gfx.visible = true; gfx.scale.set(1);
						if (typeof src.getGlobalPosition === 'function') {
							const pos = src.getGlobalPosition(); gfx.x = pos.x * 100; gfx.y = pos.y * -100; gfx.rotation = -pos.angle;
						} else { gfx.x = src.g.x; gfx.y = src.g.y; gfx.rotation = src.g.rotation || 0; }
						const hasCollision = typeof src.getCollision === 'function' ? src.getCollision() : src.collision;
						if (!hasCollision) { gfx.alpha = 1.0; if (text) text.alpha = 1.0; } else { gfx.alpha = 0.15; if (text) text.alpha = 0.5; }
						if (text) {
							text.visible = true; text.x = gfx.x;
							const objHeight = type === 2 ? baseR * 2 : baseH;
							text.y = gfx.y - (objHeight / 2) - 5; text.scale.set(1);
						}
					}
				});
			} else if (v.xrayGates.length > 0) {
				v.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();
					if (item.text && item.text.parent) item.text.parent.removeChild(item.text);
					if (item.text && typeof item.text.destroy === 'function') item.text.destroy();
				});
				v.xrayGates =[];
			}
		}
	};
})();