Well, I don't really want to post a tar.xz or tar.gz...and those are the only commands I remember by heart for tar. The easiest way for me to distribute things from Linux is to just package it up as an .spk. And unless I've been lied to for years, FBN did write the SPK Unpacker (it existed even before 1.5, I thought? Or maybe the one floating around isn't the first one to be written?).

OK, so I guess I should explain. And post a little code. The demo shows two bodies that have mass, velocity, and position. It can handle any arbitrary number of bodies--severe lag with more than 16 in my tests, though--and models their gravitational force on each other.

const G = 6.667;

function Velocity(angle, speed){

this.angle = angle;

this.speed = speed;

}

function Distance(a, b){

var dx = a.x-b.x;

var dy = a.y-b.y;

return Math.sqrt((dx*dx)+(dy*dy));

}

function AddVector(a, b){

var ax = a.speed*Math.cos(a.angle);

var ay = a.speed*Math.sin(a.angle);

var bx = b.speed*Math.cos(b.angle);

var by = b.speed*Math.sin(b.angle);

var nx = ax+bx;

var ny = ay+by;

var nspeed = Math.sqrt((nx*nx)+(ny*ny));

var nangle = Math.atan2(ny,nx);

return new Velocity(nangle, nspeed);

}

function Body(mass, x, y, angle, speed){

this.mass = mass;

this.x = x;

this.y = y;

this.angle = angle;

this.speed = speed;

}

function GeoSet(bodies){

this.bodies = bodies;

this.Calculate = function(){

var b = this.bodies;

for(var i in b){

for(var e in b){

if(i==e)

continue;

var dx = b[i].x-b[e].x;

var dy = b[i].y-b[e].y;

if(dx==0)

continue;

var angle = Math.atan2(dy, dx);

if(isNaN(angle))

Abort("lol");

var d = Math.sqrt((dx*dx)+(dy*dy));

var accel = ((G*b[e].mass)/(d*d));

var gv = AddVector(new Velocity(angle, accel), b[i]);

b[i].speed = gv.speed;

b[i].angle = gv.angle;

}

b[i].x-=Math.cos(b[i].angle)*b[i].speed;

b[i].y-=Math.sin(b[i].angle)*b[i].speed;

}

}

}

That handles actual gravity calculations.

function DrawModelView(GS, scale, x, y){

GS.Calculate();

for(var i in GS.bodies){

var b = GS.bodies[i];

var r = Math.max(b.mass*scale, 1);

if((((b.x*scale)+x)-(b.mass*scale))>GetScreenWidth())

continue;

if((((b.y*scale)+y)-(b.mass*scale))>GetScreenHeight())

continue;

if((((b.x*scale)+x)+(b.mass*scale))<0)

continue;

if((((b.y*scale)+y)+(b.mass*scale))<0)

continue;

FilledCircle(((b.x)*scale)+x, ((b.y)*scale)+y, Math.max(b.mass*scale, 1), White);

}

}

function game(){

var x = 0;

var y = 0;

var scale = 0.5;

var GS = new GeoSet([new Body(56, 300, 240, 0, 0.04), new Body(0.1, 300, 620, 0, 1), new Body(0.001, 300, 50, Math.PI, Math.sqrt(2)), new Body(1, 1100, 240, Math.PI/2, 0.7)]);

while(!IsKeyPressed(KEY_Q)){

var t = GetTime();

DrawModelView(GS, scale, (GetScreenWidth()/2-(GS.bodies[0].x*scale))+(x*scale), (GetScreenHeight()/2-(GS.bodies[0].y*scale))+(y*scale));

FlipScreen();

while(AreKeysLeft()){

var k = GetKey();

if(k==KEY_LEFT)

x+=10;

if(k==KEY_RIGHT)

x-=10;

if(k==KEY_UP)

y+=10;

if(k==KEY_DOWN)

y-=10;

if(k==KEY_O)

scale*=2;

if(k==KEY_L)

scale/=2;

}

while(GetTime()<t+2){};

}

}

That sets up a GeoSet, and fills it with some bodies. If you add a new body (or you can try it out with the set I posted right there), you can see much more complex interactions.

I only had a relatively good orbit included because, based on all my previous gravity experiments, and this one too, it's really hard to get stable orbits.

The demo is mostly just mechanical. I added more interactive features in the code I posted above. Also, since it focuses on a single body, you can't see the counter-force of the smaller bodies pulling on it., but that does happen.

EDIT:

SPK specificationI should add support for that to TurboSphere. Actually, I also kind of want to make an SPK editor (more than just an unpacker, like a full SPK archiver), too.