node-haxball
Version:
The most powerful and lightweight API that allows you to develop your original Haxball(www.haxball.com) host, client, and standalone applications both on node.js and browser environments and also includes every possible hack and functionality that you can
279 lines (261 loc) • 9.85 kB
JavaScript
module.exports = function(API){
const { OperationType, VariableType, ConnectionState, AllowFlags, Direction, CollisionFlags, CameraFollow, BackgroundType, GamePlayState, BanEntryType, Callback, Utils, Room, Replay, Query, Library, RoomConfig, Plugin, Renderer, Errors, Language, EventFactory, Impl } = API;
Object.setPrototypeOf(this, Library.prototype);
Library.call(this, "aimbot", { // "aimbot" is library's name. Every library should have a unique name.
version: 0.3,
author: "abc",
description: `An aimbot`
});
this.defineVariable({
name: "mode",
description: "Aimbot mode (0: off, 1: default, 2: advanced)",
type: VariableType.Integer,
value: 0,
range: {
min: 0,
max: 2,
step: 1
}
});
this.defineVariable({
name: "minDistToBall",
description: "Minimum distance to show aimbot line",
type: VariableType.Number,
value: 25
});
this.defineVariable({
name: "distThreshold",
description: "Aimbot may trace rays until this distance is reached",
type: VariableType.Number,
value: 600
});
this.defineVariable({
name: "maxIter",
description: "Aimbot may trace rays until this iteration count is reached",
type: VariableType.Number,
value: 1000
});
this.defineVariable({
name: "color",
description: "Color of aimbot's line (in css format)",
type: VariableType.String,
value: "rgba(255,255,255,0.35)"
});
this.defineVariable({
name: "followPlayerId",
description: "Id of the player that the aimbot will follow (if >=0)",
type: VariableType.PlayerId,
value: -1
});
this.defineVariable({
name: "advancedVirtualKick",
description: "Whether virtually kicking the ball is enabled",
type: VariableType.Boolean,
value: true
});
this.defineVariable({
name: "advancedEngineFrameNum",
description: "Number of frames to run the physics engine",
type: VariableType.Integer,
value: 100,
range: {
min: 1,
max: 1000,
step: 1
}
});
var thisLibrary = this;
function onSegment(a, b, x, y){
var segmentLengthSqr = (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y);
var r = ((x - a.x) * (b.x - a.x) + (y - a.y) * (b.y - a.y)) / segmentLengthSqr;
if (r<0 || r>1) return false;
var sl = ((a.y - y) * (b.x - a.x) - (a.x - x) * (b.y - a.y)) / Math.sqrt(segmentLengthSqr);
return Math.abs(sl) <= 0.00000001;
}
function interceptCircleLineSeg(circle, line){
var b, c, d, u1, u2, ret, v1, v2;
var v1 = {
x: line.p2.x - line.p1.x,
y: line.p2.y - line.p1.y
};
var v2 = {
x: line.p1.x - circle.arcCenter.x,
y: line.p1.y - circle.arcCenter.y
};
b = (v1.x * v2.x + v1.y * v2.y);
c = 2 * (v1.x * v1.x + v1.y * v1.y);
b *= -2;
d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - circle.arcRadius * circle.arcRadius));
if(isNaN(d)) // no intercept
return;
u1 = (b - d) / c; // these represent the unit distance of point one and two on the line
u2 = (b + d) / c;
if (u1<0 && u2<0)
return;
ret = []; // return array
//if(u1 <= 1 && u1 >= 0){ // add point if on the line segment
if (u1>0)
ret.push({
x: line.p1.x + v1.x * u1,
y: line.p1.y + v1.y * u1
});
//}
//if(u2 <= 1 && u2 >= 0){ // second add point if on the line segment
if (u2>0)
ret.push({
x: line.p1.x + v1.x * u2,
y: line.p1.y + v1.y * u2
});
//}
return ret;
}
this.calculateAndDraw = function(followDisc, gameState, ctx){
if (!thisLibrary.mode)
return;
if (thisLibrary.mode==1){
var mapObjects = gameState.physicsState;
var ball = mapObjects.discs[0], fpId = thisLibrary.followPlayerId;
if (fpId>=0){
var d = thisLibrary.room.players.find((x)=>x.id==fpId)?.disc;
d && (followDisc = d);
}
if (ball && followDisc){
var coord = ball.pos, baseCoord = followDisc.pos, remainingDist = thisLibrary.distThreshold;
var xb = coord.x, yb = coord.y;
var dx = xb-baseCoord.x, dy = yb-baseCoord.y, dd = Math.sqrt(dx*dx+dy*dy);
if (dd<ball.radius+followDisc.radius+thisLibrary.minDistToBall){
var iter = 0;
while(remainingDist>0 && iter<thisLibrary.maxIter){
dx/=dd, dy/=dd;
var f = null, coll = null, N = null, minSqrDist = Infinity;
for (var i = 0, e = mapObjects.segments; i < e.length; i++)
if (((f = e[i]), 0 != (f.cMask & ball.cGroup) && 0 != (f.cGroup & ball.cMask))) {
if (0*f.curveF!=0){ // line
var Ua = f.normal.x*f.v0.pos.x+f.normal.y*f.v0.pos.y;
var k = (Ua-f.normal.y*yb-f.normal.x*xb)/(f.normal.y*dy+f.normal.x*dx);
if (k>0){
var x = xb+k*dx, y = yb+k*dy;
if (onSegment(f.v0.pos, f.v1.pos, x, y)){
var p = {x:x,y:y};
var sqrDist = (p.x-xb)*(p.x-xb)+(p.y-yb)*(p.y-yb);
if (sqrDist<minSqrDist){
coll = p;
minSqrDist = sqrDist;
N = f.normal;
}
}
}
}
else{ // arc
var pts = interceptCircleLineSeg(f, {p1: baseCoord, p2: coord});
if (pts){
var pos1 = f.v0.pos, pos2 = f.v1.pos, center = f.arcCenter;
var a1 = Math.atan2(pos1.y-center.y, pos1.x-center.x)*180/Math.PI;
if (a1<0)
a1+=360;
var a2 = Math.atan2(pos2.y-center.y, pos2.x-center.x)*180/Math.PI;
if (a2<=0)
a2+=360;
pts.forEach((p)=>{
var a = Math.atan2(p.y-center.y, p.x-center.x)*180/Math.PI;
if (a<0)
a+=360;
if (a1<=a && a<=a2){
var sqrDist = (p.x-xb)*(p.x-xb)+(p.y-yb)*(p.y-yb);
if (sqrDist<minSqrDist){
coll = p;
minSqrDist = sqrDist;
// let's calculate the normal for this point:
var xd = p.x-center.x, yd = p.y-center.y, dist = Math.sqrt(xd*xd+yd*yd);
N = {
x: xd/dist,
y: yd/dist
};
}
}
});
}
}
}
for (var i = 0, e = mapObjects.planes; i < e.length; i++)
if (((f = e[i]), 0 != (f.cMask & ball.cGroup) && 0 != (f.cGroup & ball.cMask))) {
/*
plane: px+qy=r (p = f.normal.x, q = f.normal.y, r = f.dist)
ray: direction: c, s; point: x', y' (c = dx, s = dy, x' = xb, y' = yb)
y=y'+k.s
x=x'+k.c
p(y'+k.s)+q(x'+k.c)=r
p.y'+k.p.s+q.x'+k.q.c=r
k=(r-p.y'-q.x')/(p.s+q.c)
*/
var k = (f.dist-f.normal.y*yb-f.normal.x*xb)/(f.normal.y*dy+f.normal.x*dx);
if (k>0){
var p = {
x: xb+k*dx,
y: yb+k*dy
};
var sqrDist = (p.x-xb)*(p.x-xb)+(p.y-yb)*(p.y-yb);
if (sqrDist<minSqrDist){
coll = p;
minSqrDist = sqrDist;
N = f.normal;
}
}
}
if (coll){
ctx.save();
ctx.strokeStyle = thisLibrary.color;
ctx.beginPath();
ctx.moveTo(xb, yb);
ctx.lineTo(coll.x, coll.y);
ctx.stroke();
ctx.restore();
remainingDist -= Math.sqrt(minSqrDist);
if (remainingDist<=0)
break;
// R: (dx, dy), N: N, reflection vector = R-2(R.N)N
var coeff = 2*(dx*N.x+dy*N.y), dx2 = dx-coeff*N.x, dy2 = dy-coeff*N.y;
coord = {
x: coll.x+dx2*0.0001,
y: coll.y+dy2*0.0001,
};
baseCoord = coll;
xb = coord.x;
yb = coord.y;
dx = xb-baseCoord.x;
dy = yb-baseCoord.y;
dd = Math.sqrt(dx*dx+dy*dy);
}
else
break;
iter++;
}
}
}
}
else{
var ball = gameState.physicsState.discs[0], fpId = thisLibrary.followPlayerId;
if (!ball || !(fpId>=0))
return;
var newState = gameState.copy();
var followDisc = newState.physicsState.discs.find((x)=>x.playerId==fpId);
if (!followDisc)
return;
ball = newState.physicsState.discs[0];
if (thisLibrary.advancedVirtualKick)
newState.physicsState.kickBall(followDisc, ball, newState.stadium.playerPhysics);
ctx.save();
ctx.strokeStyle = thisLibrary.color;
ctx.beginPath();
var p = ball.pos;
ctx.moveTo(p.x, p.y);
for (var i=1;i<=thisLibrary.advancedEngineFrameNum;i++){
newState.runSteps(1);
p = ball.pos;
ctx.lineTo(p.x, p.y);
}
ctx.stroke();
ctx.restore();
}
};
};