package engine;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JPanel;
public class Board extends JPanel implements Runnable, MouseListener,
MouseMotionListener, MouseWheelListener, KeyListener {
private Thread animator;
public GMObjectIterable objects;
private final int DELAY = 20;
public static int mouseX = 0;
public static int mouseY = 0;
public int pause = 0;
private int size = 0;
private LinkedList<InputEvent> inputEvents;
private final static int TARGET_FPS = 60;
private final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
private long lastFpsTime;
private int fps;
static int lastFPS = TARGET_FPS;
double slowMo = 1;
public int cursor;
public static boolean isRunning = true;
public BufferedImage img;
private Cursor blankCursor;
public Board() {
GMObject.global = this;
GMObject.list = objects;
addKeyListener(this);
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setBackground(Color.BLACK);
setFocusable(true);
setDoubleBuffered(true);
objects = new GMObjectIterable();
inputEvents = new LinkedList<InputEvent>();
updateGlobalVars();
BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
// Create a new blank cursor.
blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
cursorImg, new Point(0, 0), "blank cursor");
// Construct initial objects here.
// init();
}
public void init() {
}
@Override
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
@Override
public synchronized void paint(Graphics g) {
super.paint(g);
Graphics2D g2d2 = (Graphics2D) g;
render();
double scale = Skeleton.SCALE;
g2d2.drawImage(img, 0, 0, (int) Math.round(Skeleton.WIDTH * scale),
(int) Math.round(Skeleton.HEIGHT * scale), 0, 0,
Skeleton.WIDTH, Skeleton.HEIGHT, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
repaint();
}
public List<GMObject> reverse(GMObjectIterable obj) {
LinkedList<GMObject> list = new LinkedList<GMObject>();
for (GMObject e : obj) {
list.addFirst(e);
}
return list;
}
public synchronized void render() {
if (img == null)
img = new BufferedImage(Skeleton.WIDTH, Skeleton.HEIGHT,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
g2d.clearRect(0, 0, img.getWidth(), img.getHeight());
for (GMObject obj : reverse(objects))
if (obj.visible && !obj.isDeactivated())
obj.preDraw(g2d);
for (GMObject obj : objects)
if (obj.visible && !obj.isDeactivated()) {
// System.out.println("\tDraw: " + obj.getClass());
obj.draw(g2d);
}
for (GMObject obj : objects)
if (obj.visible && !obj.isDeactivated())
obj.postDraw(g2d);
}
@Override
public void run() {
long lastLoopTime = System.nanoTime();
while (isRunning) {
// work out how long its been since the last update, this
// will be used to calculate how far the entities should
// move this loop
// System.out.println("Begin system loop");
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double) OPTIMAL_TIME) * slowMo;
// update the frame counter
lastFpsTime += updateLength;
fps++;
// update our FPS counter if a second has passed since
// we last recorded
if (lastFpsTime >= 1000000000) {
// System.out.println("(FPS: "+fps+")");
lastFpsTime = 0;
lastFPS = fps;
fps = 0;
}
if (delta > 2)
delta = 2;
// System.out.println("Begin game loop");
gameUpdates(delta);
// we want each frame to take 10 milliseconds, to do this
// we've recorded when we started the frame. We add 10 milliseconds
// to this and then factor in the current time to give
// us our final value to wait for
// remember this is in ms, whereas our lastLoopTime etc. vars are in
// ns.
try {
// System.out.println("Loop waiting for " +(lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000);
// if((lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000 < 0)
// System.out.println(lastLoopTime + " " + System.nanoTime());
// else
Thread.sleep((lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000);
} catch (Exception e) {
}
}
}
public synchronized void setCursor(Cursor c) {
if(!c.equals(getCursor()))
super.setCursor(c);
}
public synchronized void gameUpdates(double delta) {
// System.out.println("\tSelect Cursor");
// if(cursor != Cursor.CUSTOM_CURSOR)
// setCursor(Cursor.getPredefinedCursor(cursor));
// else
// setCursor(blankCursor);
// System.out.println("\tUpdate Cursor");
cursor = Cursor.DEFAULT_CURSOR;
// System.out.println("\tUpdate Global Variables");
updateGlobalVars();
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0)) {
// System.out.println("\tBeginstep: " + obj.getClass());
obj.beginStep(delta);
}
synchronized (this) {
while (!inputEvents.isEmpty()) {
InputEvent ev = inputEvents.poll();
if (ev instanceof KeyEvent) {
if (ev.getID() == KeyEvent.KEY_PRESSED) {
if (((KeyEvent) ev).getKeyCode() == KeyEvent.VK_BACK_QUOTE
&& !GMObject.keys[KeyEvent.VK_BACK_QUOTE])
GMObject.DEBUG = !GMObject.DEBUG;
GMObject.keys[((KeyEvent) ev).getKeyCode()] = true;
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0))
obj.keyPressed((KeyEvent) ev);
} else if (ev.getID() == KeyEvent.KEY_RELEASED) {
GMObject.keys[((KeyEvent) ev).getKeyCode()] = false;
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0))
obj.keyReleased((KeyEvent) ev);
}
} else if (ev instanceof MouseWheelEvent) {
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0))
obj.mouseWheel((MouseWheelEvent) ev);
} else if (ev instanceof MouseEvent) {
if (ev.getID() == MouseEvent.MOUSE_PRESSED) {
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0))
obj.mousePressed((MouseEvent) ev);
} else if (ev.getID() == MouseEvent.MOUSE_RELEASED) {
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0))
obj.mouseReleased((MouseEvent) ev);
}
}
}
}
for (GMObject obj : objects)
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0)) {
// System.out.println("\tStep: " + obj.getClass());
obj.step(delta);
}
for (GMObject obj : objects) {
if ((obj._xprev == obj.x && obj._yprev == obj.y) || !obj.solid
|| obj.isDeactivated() || !obj.isPrecise())
continue;
AffineTransform at = new AffineTransform();
at.setToTranslation(obj.x - obj._xprev, obj.y - obj._yprev);
obj.bounds.transform(at);
obj._xprev = obj.x;
obj._yprev = obj.y;
}
for (GMObject obj : objects) {
if (!obj.solid)
continue;
GMObjectIterable rest = new GMObjectIterable(obj._next);
for (GMObject col : rest) {
if (col.solid && !obj.isDeactivated() && !col.isDeactivated()) {
if (obj.intersects(col)
&& (pause == 0 || (obj.pausable & pause) != 0)) {
obj.collision(col);
col.collision(obj);
}
}
}
}
repaint();
// render();
for (GMObject obj : objects) {
if (obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0))
obj.deac();
if (!obj.isDeactivated()
&& (pause == 0 || (obj.pausable & pause) != 0)) {
// System.out.println("\tEndstep: " + obj.getClass());
obj.endStep(delta);
}
}
}
public void handleInputEvent(InputEvent ev) {
for (InputEvent ch : inputEvents) {
if (ch instanceof KeyEvent && ev instanceof KeyEvent) {
KeyEvent event = (KeyEvent) ev;
KeyEvent check = (KeyEvent) ch;
if (check.getID() == event.getID()
&& check.getKeyCode() == event.getKeyCode())
return;
} else if (ch instanceof MouseWheelEvent
&& ev instanceof MouseWheelEvent) {
MouseWheelEvent event = (MouseWheelEvent) ev;
MouseWheelEvent check = (MouseWheelEvent) ch;
if (check.getID() == event.getID()
&& check.getScrollAmount() == event.getScrollAmount())
return;
} else if (ch instanceof MouseEvent && ev instanceof MouseEvent) {
MouseEvent event = (MouseEvent) ev;
MouseEvent check = (MouseEvent) ch;
if (check.getID() == event.getID()
&& check.getButton() == event.getButton())
return;
} else
return;
}
inputEvents.add(ev);
}
@Override
public synchronized void keyPressed(KeyEvent e) {
handleInputEvent(e);
}
@Override
public synchronized void keyReleased(KeyEvent e) {
handleInputEvent(e);
}
@Override
public synchronized void keyTyped(KeyEvent e) {
// not currently implemented
}
@Override
public synchronized void mouseClicked(MouseEvent e) {
// not currently implemented
}
@Override
public synchronized void mouseWheelMoved(MouseWheelEvent e) {
handleInputEvent(e);
}
@Override
public synchronized void mousePressed(MouseEvent e) {
handleInputEvent(e);
}
@Override
public synchronized void mouseReleased(MouseEvent e) {
handleInputEvent(e);
}
@Override
public synchronized void mouseMoved(MouseEvent e) {
mouseX = (int) Math.round(e.getX() / Skeleton.SCALE);
mouseY = (int) Math.round(e.getY() / Skeleton.SCALE);
}
@Override
public synchronized void mouseDragged(MouseEvent e) {
mouseX = (int) Math.round(e.getX() / Skeleton.SCALE);
mouseY = (int) Math.round(e.getY() / Skeleton.SCALE);
}
@Override
public synchronized void mouseEntered(MouseEvent e) {
// not currently implemented
}
@Override
public synchronized void mouseExited(MouseEvent e) {
// not currently implemented
}
public void updateGlobalVars() {
// Point p = MouseInfo.getPointerInfo().getLocation();
// mouseX = p.x;
// mouseY = p.y;
}
public void addObject(GMObject insert) {
size++;
if (objects.head == null) {
objects.head = insert;
return;
}
for (GMObject obj : objects) {
if (insert.getDepth() < obj.getDepth()) {
if (obj._prev == null) {
insert._next = obj;
obj._prev = insert;
objects.head = insert;
} else {
insert._prev = obj._prev;
insert._next = obj;
obj._prev._next = insert;
obj._prev = insert;
}
return;
}
if (obj._next == null) {
obj._next = insert;
insert._prev = obj;
return;
}
}
}
public void removeObject(GMObject remove) {
if (remove._prev != null)
remove._prev._next = remove._next;
else
objects.head = remove._next;
if (remove._next != null)
remove._next._prev = remove._prev;
size--;
}
public void reset() {
objects = new GMObjectIterable();
size = 0;
}
public int instanceCount() {
return size;
}
}