public Engine(String fileName, boolean load, JFrame menu){
try{
Display.setDisplayMode(new DisplayMode(WIDTH,HEIGHT));
Display.setTitle("Minecraft 2D X");
Display.create();
}catch(LWJGLException e){
e.printStackTrace();
}
init(fileName, menu);
if(load){
load();
}
//Initialisation code Open GL
glMatrixMode(GL_PROJECTION); //state
glLoadIdentity();
glOrtho(0,WIDTH,HEIGHT,0,1,-1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
while(Display.isCreated() && !Display.isCloseRequested()){
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glTranslatef(translate_x,0,0);
input();
render();
if(Display.isCreated()){
Display.update();
}
Display.sync(60);
if(display){
AL.destroy();
Display.destroy();
}
}
AL.destroy();
Display.destroy();
if(inGameMenu != null){
inGameMenu.destroy();
}
}
/**
* Init.
* Initialises the private variables required by the engine.
* Begins playing the background music for the game.
*
* @param fileName the fileName of the game in string format lacking the directory as well as file identifier.
*/
private void init(String fileName, JFrame menu){
grid = new BlockGrid();
sky = new Sky();
player = new Player(384,0,0,0,grid);
selection = Items.STONE;
translate_x = 0;
toolbar = new Toolbar();
toolbar.selection(selection);
this.fileName = fileName;
state = State.GAME;
inGameMenu = null;
display = false;
this.menu = menu;
try {
step = AudioLoader.getAudio("OGG", ResourceLoader.getResourceAsStream("sounds/step.ogg"));
music = AudioLoader.getStreamingAudio("OGG", ResourceLoader.getResource("sounds/music/music1.ogg"));
} catch (IOException e) {
e.printStackTrace();
}
music.playAsMusic(1.0f, 1.0f, true);
}
/**
* Input.
* This deals with the input provided by the keyboard and the mouse taking into account the game state, dealing with the data generated by the input devices appropriately.
*/
private void input(){
switch(state){
case GAME:
int mX = (int) (Mouse.getX()-translate_x);
int mY = HEIGHT - Mouse.getY();
if(Mouse.isButtonDown(0)){
int grid_x = 0;
int grid_y = 0;
switch(selection){
case SWORD:
break;
case PICKAXE:
grid_x = Math.round(mX/ World.BLOCK_SIZE);
grid_y = Math.round(mY/ World.BLOCK_SIZE);
grid.setAtNull(grid_x, grid_y);
break;
default:
grid_x = Math.round(mX/ World.BLOCK_SIZE);
grid_y = Math.round(mY/ World.BLOCK_SIZE);
if(!player.blockIntersects(grid_x,grid_y,translate_x)){
grid.setAt(grid_x, grid_y, selection);
}
}
}
if(Keyboard.isKeyDown(Keyboard.KEY_1)){ selection = Items.SWORD; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_2)){ selection = Items.PICKAXE; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_3)){ selection = Items.STONE; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_4)){ selection = Items.DIRT; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_5)){ selection = Items.GRASS; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_6)){ selection = Items.COAL; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_7)){ selection = Items.PLANK; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_8)){ selection = Items.WOOD; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_9)){ selection = Items.FIRE; toolbar.selection(selection);}
if(Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) && Keyboard.isKeyDown(Keyboard.KEY_S)){save();}
if(Keyboard.isKeyDown(Keyboard.KEY_A) || Keyboard.isKeyDown(Keyboard.KEY_LEFT)){if(translate_x == 0 && !player.intersects(-2558)){translate_x = -2558;} if(!player.intersectsX(-2, translate_x) && translate_x < 0){player.move(0, false, translate_x); translate_x+=2; if(!step.isPlaying()){step.playAsSoundEffect(1.0f, 0.1f, false);}}}
if(Keyboard.isKeyDown(Keyboard.KEY_D) || Keyboard.isKeyDown(Keyboard.KEY_RIGHT)){if(translate_x == -2560 && !player.intersects(2)){translate_x = 2;} if(!player.intersectsX(2, translate_x) && translate_x > -2560){player.move(0, true, translate_x); translate_x+=-2; if(!step.isPlaying()){step.playAsSoundEffect(1.0f, 0.1f, false);}}}
if(Keyboard.isKeyDown(Keyboard.KEY_SPACE)){if(!player.isGrounded()){ player.space();}}
if(!Keyboard.isKeyDown(Keyboard.KEY_LEFT) && !Keyboard.isKeyDown(Keyboard.KEY_RIGHT) && !Keyboard.isKeyDown(Keyboard.KEY_A) && !Keyboard.isKeyDown(Keyboard.KEY_D)){player.stop();}
if((Keyboard.isKeyDown(Keyboard.KEY_LEFT) && Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) || (Keyboard.isKeyDown(Keyboard.KEY_A) && Keyboard.isKeyDown(Keyboard.KEY_D))){player.stop();}
if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){save(); setScreen(); inGameMenu = new InGameMenu(); state = State.IN_GAME_MENU;}
break;
case IN_GAME_MENU:
break;
}
}
/**
* Sets the screen.
* This takes a screenshot of the current LWJGL display and saves it to an Image file within the res folder.
* The image generated is use for the in game menu background.
*/
private void setScreen(){
GL11.glReadBuffer(GL11.GL_FRONT);
int width = Display.getDisplayMode().getWidth();
int height= Display.getDisplayMode().getHeight();
int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer );
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int x = 0; x < WIDTH; x++){
for(int y = 0; y < HEIGHT; y++){
int i = (x + (width * y)) * bpp;
int r = buffer.get(i) & 0xFF;
int g = buffer.get(i + 1) & 0xFF;
int b = buffer.get(i + 2) & 0xFF;
image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
File file = new File("res/screen.png");
String format = "PNG";
try {
ImageIO.write(image, format, file);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Render.
* This renders the image displayed to the user on the LWJGL Display, taking into account what the game state is.
*/
private void render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
switch(state){
case GAME:
drawGame();
SoundStore.get().poll(0);
break;
case IN_GAME_MENU:
if(inGameMenu != null){
inGameMenu.redraw();
}
}
}
/**
* Draws the main game when the state of game is in the game state.
*/
private void drawGame() {
sky.draw(translate_x);
grid.draw(translate_x);
player.draw();
toolbar.draw();
}
/**
* Load.
* Loads the information contained within the saved .xml file and set the private variables accordingly.
*/
private void load() {
Block[][] blocks = new Block[BLOCKS_WIDTH][BLOCKS_HEIGHT];
SAXBuilder builder = new SAXBuilder();
try {
Document document = builder.build("saves/"+fileName+".xml");
Element root = document.getRootElement();
for (Element block : root.getChild("Blocks").getChildren()) {
int x = Integer.parseInt(block.getAttributeValue("x"));
int y = Integer.parseInt(block.getAttributeValue("y"));
if(!block.getAttributeValue("type").equals("null")){
blocks[x][y] = new Block(Items.valueOf(block.getAttributeValue("type")), x * BLOCK_SIZE, y* BLOCK_SIZE);
}else {
blocks[x][y] = null;
}
}
player.setX(Integer.parseInt(root.getChild("Character").getChild("Position").getAttributeValue("x")));
player.setY(Integer.parseInt(root.getChild("Character").getChild("Position").getAttributeValue("y")));
translate_x = Float.parseFloat(root.getChild("Character").getChild("Position").getAttributeValue("shift"));
fileName = root.getChild("Game").getChild("CheckSum").getAttributeValue("filename");
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
grid.setBlocks(blocks);
}
/**
* Save.
* Saves the games variable's information in the file name's .xml file.
*/
private void save() {
Block[][] blocks = grid.getBlocks();
Document document = new Document();
Element root = new Element("Minecraft2D");
document.setRootElement(root);
Element blockgrid = new Element("Blocks");
for (int x = 0; x < BLOCKS_WIDTH - 1; x++) {
for (int y = 0; y < BLOCKS_HEIGHT - 1; y++) {
Element block = new Element("block");
if (blocks[x][y] != null) {
block.setAttribute("x", String.valueOf((int) (blocks[x][y].getX() / BLOCK_SIZE)));
block.setAttribute("y", String.valueOf((int) (blocks[x][y].getY() / BLOCK_SIZE)));
block.setAttribute("type",String.valueOf(blocks[x][y].getType()));
} else {
block.setAttribute("x", String.valueOf(x));
block.setAttribute("y", String.valueOf(y));
block.setAttribute("type", "null");
}
blockgrid.addContent(block);
}
}
root.addContent(blockgrid);
Element character = new Element("Character");
Element position = new Element("Position");
position.setAttribute("x", String.valueOf(player.getX()));
position.setAttribute("y", String.valueOf(player.getY()));
position.setAttribute("shift", String.valueOf(translate_x));
character.addContent(position);
root.addContent(character);
Element game = new Element("Game");
Element checksum = new Element("CheckSum");
checksum.setAttribute("md5",md5());
checksum.setAttribute("filename", fileName);
game.addContent(checksum);
root.addContent(game);
XMLOutputter output = new XMLOutputter();
try {
output.output(document, new FileOutputStream("saves/"+fileName+".xml",false));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Md5 generates an md5 encrypted string of the filename to help ensure that the saved file selected is indeed valid.
*
* @return the string of the filename in md5 format.
*/
private String md5(){
MessageDigest m = null;
try {
m = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
m.update(fileName.getBytes(), 0, fileName.length());
return new BigInteger(1, m.digest()).toString(16);
}
/*private void time() {
time += System.currentTimeMillis() - timeOld;
timeOld = System.currentTimeMillis();
}*/
/**
* {TESTING METHOD}
* The main method, used for testing purposed so as to bypass the Menu and other classes.
*
* @param args the arguments
*/
public static void main(String[] args) {
new Engine("Test", false,null);
}
/**
* The Class InGameMenu, which takes hold of the in game menu functions and its derivatives.
*/
private class InGameMenu{
/** The frame. */
private JFrame inGameMenu;
/** The content pane. */
private JPanel contentPane;
/**
* Instantiates a new in game menu.
* This draws a JFrame over the menu taking into account the position of where the LWJGL Display is displayed.
*/
public InGameMenu(){
inGameMenu = new JFrame();
contentPane = new JPanel();
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
//contentPane.setLayout(new FlowLayout());
inGameMenu.setSize(WIDTH, HEIGHT);
inGameMenu.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
inGameMenu.setUndecorated(true);
inGameMenu.setResizable(false);
inGameMenu.setContentPane(contentPane);
inGameMenu.setLocation(Display.getX(), Display.getY());
inGameMenu.setVisible(true);
setup();
}
/**
* Setup.
* This setups the base in game menu, whereby four options are displayed.
* 1. Return to Game
* 2. Main Menu
* 3. Tutorial
* 4. Exit
*/
private void setup() {
String path = Items.IN_GAME_MENU.location;
JLabel label = new JLabel(new ImageIcon(path));
contentPane.add(label);
label.setSize(800, 640);
JLabel screen = new JLabel(new ImageIcon(Items.SCREEN.location));
contentPane.add(screen);
screen.setSize(800, 640);
JButton returnToGame = new JButton();
returnToGame.setToolTipText("Return to the Game");
returnToGame.setBorder(null);
returnToGame.setContentAreaFilled(false);
returnToGame.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
destroy();
state = State.GAME;
}
});
contentPane.add(returnToGame);
returnToGame.setBounds(310,255,180,35);
JButton mainMenu = new JButton();
mainMenu.setToolTipText("Go to the Main Menu");
mainMenu.setBorder(null);
mainMenu.setContentAreaFilled(false);
mainMenu.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
display = true;
destroy();
}
});
contentPane.add(mainMenu);
mainMenu.setBounds(310,295,180,35);
JButton tutorial = new JButton();
tutorial.setToolTipText("View the tutorial");
tutorial.setBorder(null);
tutorial.setContentAreaFilled(false);
tutorial.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
tutorial();
}
});
contentPane.add(tutorial);
tutorial.setBounds(310,335,180,35);
JButton exit = new JButton();
exit.setToolTipText("Exit the Program");
exit.setBorder(null);
exit.setContentAreaFilled(false);
exit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
destroy();
display = true;
}
});
contentPane.add(exit);
exit.setBounds(310,375,180,35);
inGameMenu.getRootPane().revalidate();
}
/**
* Tutorial.
* This clears the current content pane and setups the tutorial section of the menu.
*/
private void tutorial() {
contentPane.removeAll();
contentPane.setLayout(null);
String path = Items.TUTORIAL.location;
JLabel label = new JLabel(new ImageIcon(path));
contentPane.add(label);
label.setSize(800, 640);
JLabel screen = new JLabel(new ImageIcon(Items.SCREEN.location));
contentPane.add(screen);
screen.setSize(800, 640);
JTextArea textArea = new JTextArea();
textArea.setEditable(false);
textArea.setOpaque(false);
textArea.setFont(new Font("Tahoma", 0, 13));
contentPane.add(textArea);
textArea.setBounds(50,50,700,540);
JButton back = new JButton();
back.setToolTipText("Back to in game menu");
back.setBorder(null);
back.setText("Back");
back.setFont(new Font("Tahoma", 0, 13));
back.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
contentPane.removeAll();
setup();
}
});
contentPane.add(back);
back.setBounds(700,580,50,21);
inGameMenu.getRootPane().revalidate();
}
/**
* Redraw.
* This redraws the JFrame according the where the LWJGL is, taking into account the movement of the display.
*/
public void redraw(){
inGameMenu.setLocation(Display.getX(), Display.getY());
inGameMenu.getRootPane().revalidate();
}
/**
* Destroy.
* This clears and destroys the in game menu so as to ensure that resources used are correctly terminated when the game is terminated.
*/
public void destroy(){
contentPane.removeAll();
inGameMenu.dispose();
}
}
}