/*
* Copyright (C) 2013-2015 2048FX
* Jose Pereda, Bruno Borges & Jens Deters
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jpereda.game2048;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javafx.application.Application;
import javafx.application.ConditionalFeature;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Screen;
import javafx.stage.Stage;
import com.gluonhq.charm.down.common.PlatformFactory;
/**
* The game 2048 built using JavaFX and Java 8.
* This is a fork based on the Javascript version: https://github.com/gabrielecirulli/2048
*
* @author Bruno Borges bruno.borges@oracle.com & Jose Pereda jperedadnr@gmail.com
* Based on https://github.com/brunoborges/fx2048
*
* Android Platform: Jose Pereda jperedadnr@gmail.com
*
* iOS Platform: Jens Deters jens.deters@codecentric.de
*/
public class Game2048 extends Application {
public static final String VERSION = "1.0.4";
private GameManager gameManager;
private Bounds gameBounds;
private final static int MARGIN = 36;
private StackPane root;
@Override
public void init() {
// Downloaded from https://01.org/clear-sans/blogs
// The font may be used and redistributed under the terms of the Apache License, Version 2.0.
Font.loadFont(Game2048.class.getResource("ClearSans-Bold.ttf").toExternalForm(), 10.0);
System.setOut(new PrintStream(new DevNull()));
}
private class DevNull extends OutputStream {
@Override
public void write(int b) throws IOException {
}
}
@Override
public void start(Stage primaryStage) {
gameManager = new GameManager();
gameManager.setToolBar(createToolBar());
gameBounds = gameManager.getLayoutBounds();
root = new StackPane(gameManager);
root.getStyleClass().addAll("game-root");
ChangeListener<Number> resize = (ov, v, v1) -> gameResize();
root.widthProperty().addListener(resize);
root.heightProperty().addListener(resize);
Scene scene;
if(PlatformFactory.getPlatform().getName().equals(PlatformFactory.ANDROID)){
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
scene = new Scene(root, visualBounds.getWidth(), visualBounds.getHeight());
} else {
scene = new Scene(root);
}
scene.getStylesheets().add(getClass().getResource("game.css").toExternalForm());
addKeyHandler(scene);
addSwipeHandlers(scene);
if (PlatformFactory.getPlatform().getName().equals(PlatformFactory.DESKTOP) && isARMDevice()) {
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitHint("");
}
if (Platform.isSupported(ConditionalFeature.INPUT_TOUCH)) {
scene.setCursor(Cursor.NONE);
}
if(PlatformFactory.getPlatform().getName().equals(PlatformFactory.DESKTOP)){
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double factor = Math.min(visualBounds.getWidth() / (gameBounds.getWidth() + MARGIN),
visualBounds.getHeight() / (gameBounds.getHeight() + MARGIN));
primaryStage.setMinWidth(gameBounds.getWidth() / 2d);
primaryStage.setMinHeight(gameBounds.getHeight() / 2d);
primaryStage.setWidth((gameBounds.getWidth() + MARGIN) * factor);
primaryStage.setHeight((gameBounds.getHeight() + MARGIN) * factor);
}
PlatformService.getInstance().stopProperty().addListener((obs,b,b1)->{
if(b1){
gameManager.saveRecord();
}
});
PlatformService.getInstance().pauseProperty().addListener((obs,b,b1)->gameManager.externalPause(b,b1));
primaryStage.setTitle("2048FX");
primaryStage.setScene(scene);
primaryStage.getIcons().addAll(PlatformService.getInstance().getIcons());
primaryStage.show();
}
private void gameResize(){
gameBounds = gameManager.getLayoutBounds();
double scale = Math.min((root.getWidth() - MARGIN) / gameBounds.getWidth(), (root.getHeight() - MARGIN) / gameBounds.getHeight());
gameManager.setScale(scale);
gameManager.setLayoutX((root.getWidth() - gameBounds.getWidth()) / 2d);
gameManager.setLayoutY((root.getHeight() - gameBounds.getHeight()) / 2d);
}
private boolean isARMDevice() {
return System.getProperty("os.arch").toUpperCase().contains("ARM");
}
private void addKeyHandler(Scene scene) {
scene.setOnKeyPressed(ke -> {
KeyCode keyCode = ke.getCode();
if (keyCode.equals(KeyCode.S)) {
gameManager.saveSession();
return;
}
if (keyCode.equals(KeyCode.R)) {
gameManager.restoreSession();
return;
}
if (keyCode.equals(KeyCode.P)) {
gameManager.pauseGame();
return;
}
if (keyCode.equals(KeyCode.Q) || keyCode.equals(KeyCode.ESCAPE)) {
gameManager.quitGame();
return;
}
if (keyCode.isArrowKey()) {
Direction direction = Direction.valueFor(keyCode);
gameManager.move(direction);
}
});
}
private void addSwipeHandlers(Scene scene) {
scene.setOnSwipeUp(e -> move(Direction.UP));
scene.setOnSwipeRight(e -> move(Direction.RIGHT));
scene.setOnSwipeLeft(e -> move(Direction.LEFT));
scene.setOnSwipeDown(e -> move(Direction.DOWN));
}
private void move(Direction direction){
gameManager.move(direction);
}
private Button createButtonItem(String symbol, String text, EventHandler<ActionEvent> t){
Button g=new Button();
g.setPrefSize(40, 40);
g.setId(symbol);
g.setOnAction(t);
g.setTooltip(new Tooltip(text));
return g;
}
private HBox createToolBar(){
HBox toolbar=new HBox();
toolbar.setAlignment(Pos.CENTER);
toolbar.setPadding(new Insets(10.0));
Button btItem1 = createButtonItem("mSave", "Save Session", t->gameManager.saveSession());
Button btItem2 = createButtonItem("mRestore", "Restore Session", t->gameManager.restoreSession());
Button btItem3 = createButtonItem("mPause", "Pause Game", t->gameManager.pauseGame());
Button btItem4 = createButtonItem("mReplay", "Try Again", t->gameManager.tryAgain());
Button btItem5 = createButtonItem("mInfo", "About the Game", t->gameManager.aboutGame());
toolbar.getChildren().setAll(btItem1, btItem2, btItem3, btItem4, btItem5);
if(PlatformFactory.DESKTOP.equals(PlatformFactory.getPlatform().getName())){
Button btItem6 = createButtonItem("mQuit", "Quit Game", t->gameManager.quitGame());
toolbar.getChildren().add(btItem6);
}
return toolbar;
}
@Override
public void stop() {
gameManager.saveRecord();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}