package space.physics;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;
public class Sandbox implements Runnable, Iterable<Entity> {
private Thread _thread;
private boolean _running;
private long _interval;
private int _ticksPerSecond = 60;
private int _tps = 60;
private ConcurrentLinkedDeque<Entity> _obj = new ConcurrentLinkedDeque<>();
private ConcurrentLinkedDeque<CollisionTracker<? extends Entity, ? extends Entity>> _collision = new ConcurrentLinkedDeque<>();
public int tps() { return _tps; }
public void addToSandbox(Entity m) {
_obj.add(m);
}
public void removeFromSandbox(Entity m) {
_obj.remove(m);
}
public <T extends Entity, U extends Entity> void trackCollision(Class<T> entity, Class<U> hitBy, CollisionCallback<T, U> callback) {
_collision.add(new CollisionTracker<T, U>(entity, hitBy, callback));
}
public Entity getEntity(int id) {
for(Entity e : _obj) {
if(e.id == id) {
return e;
}
}
return null;
}
public void run() {
_interval = 1000000000l / _ticksPerSecond;
_running = true;
long time, timeDelta = _interval;
int ticks = 0;
long tickTime = System.nanoTime() + 1000000000l;
while(_running) {
time = System.nanoTime();
for(Entity e : _obj) {
e.update(timeDelta / _interval);
for(CollisionTracker<? extends Entity, ? extends Entity> c : _collision) {
c.check(e);
}
}
if(tickTime <= System.nanoTime()) {
_tps = ticks;
ticks = 0;
tickTime = System.nanoTime() + 1000000000l;
//System.out.println(_tps + " ticks per second");
}
ticks++;
// Sleep each loop if we have extra time
timeDelta = System.nanoTime() - time;
long timeSleep = _interval - timeDelta;
long timeDeltaMS = timeSleep / 1000000l;
int timeDeltaNS = (int)(timeSleep - timeDeltaMS * 1000000l);
if(timeSleep > 0) {
try { Thread.sleep(timeDeltaMS, timeDeltaNS); } catch(InterruptedException e) { e.printStackTrace(); }
}
}
}
private boolean checkCollisions(Entity a, Entity b) {
// Can't collide with yourself, or bullets you've fired
if(a.spawnID == b.spawnID) return false;
double dist = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
if(dist < a.size + b.size) {
b.x -= b.vx;
b.y -= b.vy;
double mv = b.maxVel;
b.maxVel = dist - b.size;
b.clampVels();
b.displace();
b.maxVel = mv;
return true;
}
return false;
}
public void startSandbox() {
if(_thread != null) return;
_running = true;
_thread = new Thread(this);
_thread.start();
}
public void stopSandbox() {
_running = false;
}
public void join() throws InterruptedException {
_thread.join();
}
public boolean isAlive() {
return _thread != null && _thread.isAlive();
}
private class CollisionTracker<T extends Entity, U extends Entity> {
public Class<T> e1;
public Class<U> e2;
public CollisionCallback<T, U> cb;
public CollisionTracker(Class<T> e1, Class<U> e2, CollisionCallback<T, U> cb) {
this.e1 = e1;
this.e2 = e2;
this.cb = cb;
}
@SuppressWarnings("unchecked")
public void check(Entity e) {
if(e1.isInstance(e)) {
for(Entity hitBy : _obj) {
if(e2.isInstance(hitBy)) {
if(checkCollisions(e, hitBy)) {
cb.hit((T)e, (U)hitBy);
}
}
}
}
}
}
public static interface CollisionCallback<T extends Entity, U extends Entity> {
public void hit(T entity, U hitBy);
}
@Override
public Iterator<Entity> iterator() {
return _obj.iterator();
}
}