Game logic, physics hooks and update extensions for blackhack
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.org/scripts/573606/1796986/blackhack-logic.js
// ==UserScript==
// @name blackhack-logic
// @namespace brofist.io 1st-cheat (FOR ALL MODES)
// @version 1.0
// @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.0;
// ─── 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;
}
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 =[];
}
}
};
})();