package toritools.entrypoint;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.image.VolatileImage;
import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import toritools.debug.Debug;
import toritools.entity.Level;
import toritools.math.Vector2;
import toritools.scripting.ScriptUtils;
/**
* This class is a good way to get started with ToriTools. Just extend it, and
* fill in what you need. Instantiate the subclass, and you're done!
*
* @author toriscope
*
*/
public abstract class Binary {
// CORE VARS
protected final float FRAMERATE;
public static Vector2 VIEWPORT = Vector2.ZERO;
private static JFrame frame;
public static final GraphicsConfiguration gc;
private ScheduledExecutorService timer;
private boolean gameRunning = false;
private final JPanel panel;
private final File splash = new File("resources/toritools_splash.png");
static {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
// Hardware accel.
if (System.getProperty("os.name").contains("Windows")) {
System.setProperty("sun.java2d.d3d", "True");
} else {
System.setProperty("sun.java2d.opengl", "True");
System.setProperty("sun.java2d.ddscale", "true");
System.setProperty("sun.java2d.translaccel", "true");
System.setProperty("sun.java2d.ddforcevram", "true");
}
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gc = frame.getGraphicsConfiguration();
}
/*
* SUBCLASS
*/
/**
* Load anything you need (besides entities), be it large background images
* or fonts. This is your time to prepare for the update logic which will
* begin ticking after this method is run.
*/
protected abstract void initialize();
/**
* The global logic loop. Poll controls here if you want, check for win
* condition, etc. Entity updating should not be done here. It is a good
* idea to package control polling for most entities with their script,
* rather than here. This is the place for global menus, state changing,
* etc. The level will update after this method is run, followed by a
* graphical repaint. Keys queued for release are also released after this.
*/
protected abstract void globalLogic(final Level level);
/**
* Configure the current level to be loaded. Set up your special entity
* types and scripts here. Spawning will be done elsewhere.
*
* @param levelBeingLoaded
*/
protected abstract void setupCurrentLevel(Level levelBeingLoaded);
/**
* Get the starting level. Feel free to spawn a blank one if you don't want
* to do this.
*
* @return a level.
*/
protected abstract Level getStartingLevel();
/**
* Render your game.
*
* @param rootCanvas
* the panel's drawing surface.
* @return true if drawing was successful, false otherwise.
*/
protected abstract boolean render(final Graphics2D rootCanvas, final Level level);
/**
* Some basic settings.
*
* @param VIEWPORT_SIZE
* the dimensions of the viewport/window.
* @param frameRate
* the frame-rate as a ratio. 60FPS would be 60, for example.
*/
@SuppressWarnings("serial")
public Binary(final Vector2 VIEWPORT_SIZE, final int frameRate, final String windowTitle) {
frame.setTitle(windowTitle);
this.FRAMERATE = 1000 / frameRate;
VIEWPORT = VIEWPORT_SIZE;
panel = new JPanel() {
public void paintComponent(final Graphics g) {
// super.paintComponent(g);
renderAll(g);
}
};
frame.add(panel, BorderLayout.CENTER);
frame.addKeyListener(ScriptUtils.getKeyHolder());
frame.setFocusable(true);
panel.setPreferredSize(new Dimension((int) VIEWPORT.x, (int) VIEWPORT.y));
initialize();
frame.pack();
frame.setVisible(true);
ScriptUtils.queueLevelSwitch(getStartingLevel());
timer = Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(new Thread() {
public void run() {
try {
coreLogic();
panel.repaint();
} catch (Exception exception) {
exception.printStackTrace();
System.exit(1);
}
}
}, 0, (int) FRAMERATE, TimeUnit.MILLISECONDS);
}
private void rebuildBuffers() {
b1 = gc.createCompatibleVolatileImage((int) VIEWPORT.x, (int) VIEWPORT.y);
b2 = gc.createCompatibleVolatileImage((int) VIEWPORT.x, (int) VIEWPORT.y);
}
private void coreLogic() {
if (ScriptUtils.isLevelQueued()) {
if (ScriptUtils.getCurrentLevel() != null) {
Debug.print("Closing level.");
ScriptUtils.getCurrentLevel().onDeath(true);
}
ScriptUtils.moveToQueuedLevel();
setupCurrentLevel(ScriptUtils.getCurrentLevel());
Debug.print("Spawning entities.");
ScriptUtils.getCurrentLevel().onSpawn(null);
} else {
globalLogic(ScriptUtils.getCurrentLevel());
ScriptUtils.getCurrentLevel().onUpdate((float) FRAMERATE);
ScriptUtils.getKeyHolder().freeQueuedKeys();
}
}
VolatileImage b1, b2;
boolean buffer1 = true;
private void renderAll(final Graphics finalCanvas) {
if (b1 == null || b2 == null || b1.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE
|| b2.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
rebuildBuffers();
}
Image drawSurface = (buffer1) ? b1 : b2;
Image renderSurface = (buffer1) ? b2 : b1;
finalCanvas.drawImage(renderSurface, 0, 0, (int) VIEWPORT.x, (int) VIEWPORT.y, null);
if (render((Graphics2D) drawSurface.getGraphics(), ScriptUtils.getCurrentLevel())) {
buffer1 = !buffer1;
gameRunning = true;
} else if (!gameRunning) {
finalCanvas
.drawImage(ScriptUtils.fetchImage(splash), 0, 0, VIEWPORT.getWidth(), VIEWPORT.getHeight(), null);
}
}
/**
* Get the core application frame.
*
* @return the JFrame the whole thing is running in.
*/
protected JFrame getApplicationFrame() {
return frame;
}
/**
* Get the core application panel.
*
* @return the JFrame the whole thing is running in.
*/
protected JPanel getApplicationPanel() {
return panel;
}
}