Skip to content
Snippets Groups Projects
Select Git revision
  • 9cfadd69f6213c4bbe4dc2dffad5a591f4780e32
  • master default
  • v1.34.0
  • v1.33.0
  • v1.32.1
  • v1.32.0
  • v1.31.2
  • v1.31.1
  • v1.31.0
  • v1.30.0
  • v1.29.0
  • v1.28.0
  • v1.27.0
  • v1.26.0
  • v1.25.0
  • v1.24.0
  • v1.23.1
  • v1.23.0
  • v1.22.0
  • v1.20.0
  • v1.19.1
  • v1.19.0
22 results

Routes.py

Blame
  • LevelController.java 32.60 KiB
    package de.bricked.ui;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.Locale;
    import java.util.Optional;
    import java.util.Random;
    import java.util.ResourceBundle;
    
    import de.bricked.game.Game;
    import de.bricked.game.GameState;
    import de.bricked.game.HitLocation;
    import de.bricked.game.balls.Ball;
    import de.bricked.game.balls.BallType;
    import de.bricked.game.board.Board;
    import de.bricked.game.bricks.Brick;
    import de.bricked.game.bricks.BrickType;
    import de.bricked.game.paddle.Paddle;
    import de.bricked.game.paddle.PaddleSize;
    import de.bricked.game.powerups.PowerUp;
    import de.bricked.game.powerups.PowerUpType;
    import de.bricked.game.sound.SoundType;
    import de.bricked.utils.CountdownTimer;
    import fontAwesome.FontIcon;
    import fontAwesome.FontIconType;
    import javafx.animation.AnimationTimer;
    import javafx.animation.FadeTransition;
    import javafx.animation.ParallelTransition;
    import javafx.animation.TranslateTransition;
    import javafx.application.Platform;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.fxml.FXML;
    import javafx.geometry.Insets;
    import javafx.geometry.Point2D;
    import javafx.geometry.Pos;
    import javafx.scene.Cursor;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.control.Alert;
    import javafx.scene.control.Alert.AlertType;
    import javafx.scene.control.Button;
    import javafx.scene.control.ButtonType;
    import javafx.scene.control.Label;
    import javafx.scene.effect.Lighting;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.input.KeyEvent;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.layout.ColumnConstraints;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.Priority;
    import javafx.scene.layout.RowConstraints;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.text.Font;
    import javafx.scene.text.Text;
    import javafx.stage.Stage;
    import javafx.stage.WindowEvent;
    import javafx.util.Duration;
    import kuusisto.tinysound.TinySound;
    import logger.LogLevel;
    import logger.Logger;
    import tools.AlertGenerator;
    import tools.Worker;
    
    public class LevelController
    {
    	@FXML private AnchorPane anchorPane;
    	@FXML private Label labelLevelName;
    	@FXML private Label labelAuthor;
    	@FXML private Label labelLevelPack;
    	@FXML private Label labelPoints;
    	@FXML private Label labelBlocksRemaining;
    	@FXML private Label labelMultiplicator;
    	@FXML private AnchorPane anchorPaneGame;
    	@FXML private Button buttonBack;
    	@FXML private VBox vboxPowerUps;
    	@FXML private VBox vboxLives;
    	@FXML private Label labelFPS;
    
    	public Stage stage;
    	public Image icon = new Image("de/bricked/resources/icon.png");
    	public final ResourceBundle bundle = ResourceBundle.getBundle("de/bricked/main/", Locale.GERMANY);
    	private LevelSelectController levelSelectController;
    	private Game game;
    	private static GridPane grid;
    	private AnimationTimer timer;
    	private double gamePaneWidth;
    	private double gamePaneHeight;	
    	private final int TARGET_FPS = 60;
    	private final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
    	private final static double BRICK_FADE_DURATION = 300.0;
    	private int fps;
    	private Paddle paddle;
    	private GameState gameState;
    	private ImageView labelPaddle;
    	private StackPane stackPaneBall;
    	private ChangeListener<Number> heightListener;
    	private ChangeListener<Number> widthListener;
    	private double oldMousePosition;
    	private static ArrayList<Label> brickLabels;
    	private ArrayList<Label> movingPowerUps;
    	private ArrayList<CountdownTimer> timedPowerUps;
    	private long previousTime = 0;
    	private float secondsElapsedSinceLastFpsUpdate = 0f;
    
    	private void startGame()
    	{
    		resetPowerUps();
    
    		anchorPaneGame.heightProperty().removeListener(heightListener);
    		anchorPaneGame.widthProperty().removeListener(widthListener);	
    		
    		double yPosition = (gamePaneHeight / Board.HEIGHT) * (20);	
    		labelMultiplicator.setLayoutX(0);	
    		labelMultiplicator.setLayoutY(0);	
    		labelMultiplicator.setTranslateX((gamePaneWidth - 100) / 2);
    		labelMultiplicator.setTranslateY(yPosition);	
    
    		// start random into left or right direction
    		int random = new Random().nextInt(2);
    		if(random == 0)
    		{
    			game.getBall().startBallToRight();
    		}
    		else
    		{
    			game.getBall().startBallToLeft();
    		}
    
    		timer.start();
    		Logger.log(LogLevel.INFO, "ball start");
    
    		gameState = GameState.RUNNING;
    	}
    
    	public void init(Stage stage, LevelSelectController levelSelectController, Game game)
    	{
    		this.stage = stage;
    		this.levelSelectController = levelSelectController;
    		this.game = game;
    		game.setBoard(new Board(game));
    		game.setLevelController(this);
    		
    		anchorPane.setStyle("-fx-base: " + bundle.getString("color.background") + ";");
    		buttonBack.setStyle("-fx-base: " + bundle.getString("color.button.light"));
    		anchorPaneGame.setStyle("-fx-background-color: " + bundle.getString("color.default"));	
    		labelMultiplicator.setStyle("-fx-text-fill: #000000;");	
    		anchorPaneGame.setCursor(Cursor.NONE);
    
    		anchorPaneGame.setOnMouseClicked(new EventHandler<MouseEvent>()
    		{
    			@Override
    			public void handle(MouseEvent event)
    			{
    				if(gameState.equals(GameState.WAITING))
    				{
    					startGame();
    				}
    				event.consume();
    				anchorPaneGame.requestFocus();
    			}
    		});
    
    		anchorPane.setOnKeyReleased(new EventHandler<KeyEvent>()
    		{
    			@Override
    			public void handle(KeyEvent event)
    			{
    				if(event.getCode().toString().equals(bundle.getString("shortcut.debug.console")))
    				{
    					showCommandLine();
    					Logger.log(LogLevel.INFO, "openend debug console");
    					event.consume();
    					return;
    				}
    
    				if(event.getCode().toString().equals("SPACE"))
    				{
    					if(gameState.equals(GameState.WAITING))
    					{
    						startGame();
    					}
    					event.consume();
    
    					anchorPaneGame.requestFocus();
    					return;
    				}
    
    				if(event.getCode().toString().equals("ESCAPE"))
    				{					
    					if(gameState.equals(GameState.STOPPED))
    					{
    						back();
    						event.consume();
    						return;
    					}			
    					
    					pause();	
    					
    					Alert alert = new Alert(AlertType.CONFIRMATION);
    					alert.setTitle("Close Level?");
    					alert.setHeaderText("");
    					alert.setContentText("Do you really want to go back?");
                        Stage dialogStage = (Stage) alert.getDialogPane().getScene().getWindow();
                        dialogStage.getIcons().add(icon);
    					dialogStage.initOwner(stage);								
    
    					Optional<ButtonType> result = alert.showAndWait();					
    					if(result.get() == ButtonType.OK)
    					{					
    						back();						
    						event.consume();						
    					}
    					else
    					{							
    						restart();
    						event.consume(); 							
    					}
    				}
    				
    				//pause
    				if(event.getCode().toString().equals("P"))
    				{
    					if(gameState.equals(GameState.RUNNING))
    					{						
    						pause();	
    						event.consume();
    						return;
    					}
    					
    					if(gameState.equals(GameState.PAUSED))
    					{						
    						restart();
    						event.consume(); 
    						return;						
    					}					
    				}
    			}
    		});
    
    		anchorPane.setOnKeyPressed(new EventHandler<KeyEvent>()
    		{
    			@Override
    			public void handle(KeyEvent event)
    			{
    				if(gameState.equals(GameState.RUNNING))
    				{
    					if(event.getCode().toString().equals("RIGHT"))
    					{
    						movePaddleRight();
    					}
    
    					if(event.getCode().toString().equals("LEFT"))
    					{
    						movePaddleLeft();
    					}
    				}
    			}
    		});
    
    		anchorPane.setOnMouseMoved(new EventHandler<MouseEvent>()
    		{
    			@Override
    			public void handle(MouseEvent event)
    			{
    				if(gameState.equals(GameState.RUNNING))
    				{
    					// --> direct follow mouse
    					double newPaddlePosition = event.getSceneX() - paddle.getWidth() / 2;
    
    					// move left
    					if(newPaddlePosition < oldMousePosition)
    					{
    						if(newPaddlePosition > 0)
    						{
    							labelPaddle.setTranslateX(newPaddlePosition);
    						}
    						else
    						{
    							labelPaddle.setTranslateX(0);
    						}
    					}
    					// move right
    					else
    					{
    						if((newPaddlePosition + paddle.getWidth()) < gamePaneWidth)
    						{
    							labelPaddle.setTranslateX(newPaddlePosition);
    						}
    						else
    						{
    							labelPaddle.setTranslateX(gamePaneWidth - paddle.getWidth());
    						}
    					}
    
    					oldMousePosition = labelPaddle.getScene().getX();
    				}
    			}
    		});
    
    		stage.setOnCloseRequest(new EventHandler<WindowEvent>()
    		{
    			public void handle(WindowEvent event)
    			{
    				if(timer != null)
    				{
    					timer.stop();
    				}
    				
    				TinySound.shutdown();
    				Worker.shutdown();
    				Platform.exit();
    				System.exit(0);
    			}
    		});
    
    		FontIcon iconBack = new FontIcon(FontIconType.ARROW_LEFT);
    		iconBack.setSize(18);
    		buttonBack.setText("");
    		buttonBack.setGraphic(iconBack);
    
    		vboxPowerUps.setStyle("-fx-border-color: #000000; -fx-border-width: 2px; -fx-background-color: #F4F4F4");
    		vboxPowerUps.setPadding(new Insets(3));
    		vboxPowerUps.setAlignment(Pos.TOP_CENTER);
    		vboxPowerUps.setSpacing(7);
    		vboxLives.setStyle("-fx-border-color: #000000; -fx-border-width: 2px; -fx-background-color: #F4F4F4");
    		vboxLives.setPadding(new Insets(3));
    		vboxLives.setAlignment(Pos.BOTTOM_CENTER);
    		vboxLives.setSpacing(9);
    
    		anchorPaneGame.setPadding(new Insets(0));
    
    		labelLevelPack.setText(game.getLevelPack().getPackageName());
    		labelAuthor.setText("by " + game.getLevel().getAuthor());
    		labelLevelName.setText(game.getLevel().getName() + " (" + game.getLevel().getPosition() + "/" + game.getLevelPack().getLevels().size() + ")");
    		labelBlocksRemaining.setText(game.getBoard().getNumberOfRemainingBricks() + " Bricks remaining");
    
    		game.setLivesRemaining(game.getLevel().getStartLives());
    
    		gamePaneWidth = game.getSettings().getGameSize().getWidth() - 100;
    		gamePaneHeight = game.getSettings().getGameSize().getHeight() - 150;
    
    		grid = new GridPane();	
    		grid.setGridLinesVisible(false);
    		grid.setStyle("-fx-border-color: #000000; -fx-border-width: 2px;");
    		grid.setHgap(0);
    		grid.setVgap(0);
    
    		anchorPaneGame.getChildren().add(grid);
    		AnchorPane.setTopAnchor(grid, 0.0);
    		AnchorPane.setRightAnchor(grid, 0.0);
    		AnchorPane.setBottomAnchor(grid, 0.0);
    		AnchorPane.setLeftAnchor(grid, 0.0);
    
    		redraw();
    
    		refreshLiveCounter();
    
    		initPaddle(game.getLevel().getInitPadSize());
    		oldMousePosition = labelPaddle.getScene().getX() - paddle.getWidth() / 2;
    
    		heightListener = new ChangeListener<Number>()
    		{
    			@Override
    			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
    			{
    				initBall(BallType.NORMAL);
    				initTimer();
    				gamePaneHeight = newValue.doubleValue();
    			}
    		};
    
    		widthListener = new ChangeListener<Number>()
    		{
    			@Override
    			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
    			{
    				gamePaneWidth = newValue.doubleValue();
    			}
    		};
    
    		anchorPaneGame.heightProperty().addListener(heightListener);
    		anchorPaneGame.widthProperty().addListener(widthListener);
    
    		anchorPaneGame.requestFocus();
    
    		showLabelFPS(levelSelectController.controller.controller.getCommandLine().getBundle().isShowFPS());
    		labelFPS.setStyle("-fx-text-fill: #FF0000");
    
    		resetMultiplicator();
    
    		resetPowerUps();
    
    		gameState = GameState.WAITING;
    	}
    
    	public Paddle getPaddle()
    	{
    		return paddle;
    	}
    
    	public ImageView getLabelPaddle()
    	{
    		return labelPaddle;
    	}
    
    	private void initTimer()
    	{
    		previousTime = 0;
    		secondsElapsedSinceLastFpsUpdate = 0f;
    		fps = 0;
    		
    		timer = new AnimationTimer()
    		{			
    			@Override
    			public void handle(long currentTime)
    			{
    				if(previousTime == 0)
    				{
    					previousTime = currentTime;
    					return;
    				}
    
    				float secondsElapsed = currentTime - previousTime;
    				previousTime = currentTime;
    				double delta = secondsElapsed / ((double)OPTIMAL_TIME);
    
    				secondsElapsedSinceLastFpsUpdate += secondsElapsed;
    				fps++;
    
    				if(secondsElapsedSinceLastFpsUpdate >= 1000000000)
    				{
    					labelFPS.setText(String.valueOf(fps));
    					Logger.log(LogLevel.DEBUG, "current FPS: " + fps);
    					secondsElapsedSinceLastFpsUpdate = 0;
    					fps = 0;
    				}
    
    				// update ball location
    				stackPaneBall.setTranslateX(stackPaneBall.getTranslateX() + game.getBall().getDirection().getX() * delta);
    				stackPaneBall.setTranslateY(stackPaneBall.getTranslateY() + game.getBall().getDirection().getY() * delta);
    
    				// hit detection
    				HitLocation hitLocation = game.hitsWall(gamePaneWidth, gamePaneHeight, stackPaneBall.getLayoutX(), stackPaneBall.getLayoutY(), stackPaneBall.getTranslateX(), stackPaneBall.getTranslateY(), game.getBall().getDirection());
    
    				Point2D ballPosition = new Point2D(stackPaneBall.getLayoutX() + stackPaneBall.getTranslateX(), stackPaneBall.getLayoutY() + stackPaneBall.getTranslateY());
    				Point2D paddlePosition = new Point2D(labelPaddle.getLayoutX() + labelPaddle.getTranslateX(), labelPaddle.getLayoutY() + labelPaddle.getTranslateY());
    				// if ball collides with border then brick collisions are irrelevant
    				if(hitLocation != null)
    				{
    					resetMultiplicator();
    
    					if(hitLocation.equals(HitLocation.LIFE_LOST))
    					{						
    						game.setLivesRemaining(game.getLivesRemaining() - 1);
    						Logger.log(LogLevel.DEBUG, "Life lost (" + game.getLivesRemaining() + " lives remaining)");
    						refreshLiveCounter();
    						resetPowerUps();
    						if(game.getLivesRemaining() <= 0)
    						{
    							// game over
    							
    							gameState = GameState.STOPPED;
    							timer.stop();		
    							
    							game.getSoundHandler().play(SoundType.GAME_OVER);								
    							
    							Platform.runLater(() -> {									
    								AlertGenerator.showAlert(Alert.AlertType.INFORMATION, "Game Over", "", "Youe have no lives left", icon, stage, bundle.getString("color.background"), false);
    							});
    						}
    						else
    						{
    							gameState = GameState.WAITING;
    							timer.stop();
    							
    							game.getSoundHandler().play(SoundType.LIFE_LOST);							
    
    							// reset paddle and ball
    							initPaddle(game.getLevel().getInitPadSize());
    							initBall(BallType.NORMAL);
    							initTimer();
    						}
    					}
    					else
    					{
    						game.getBall().setDirection(game.reflectBall(hitLocation, game.getBall().getDirection()));
    						
    						game.getSoundHandler().play(SoundType.HIT_WALL);
    
    						switch(hitLocation)
    						{
    							case BOTTOM:
    								stackPaneBall.setTranslateX(stackPaneBall.getTranslateX());
    								stackPaneBall.setTranslateY(1);
    								break;
    
    							case RIGHT:
    								stackPaneBall.setTranslateX(1);
    								stackPaneBall.setTranslateY(stackPaneBall.getTranslateY());
    								break;
    
    							case LEFT:
    								stackPaneBall.setTranslateX(gamePaneWidth - game.getBall().getBallRadius() * 2 - 1);
    								stackPaneBall.setTranslateY(stackPaneBall.getTranslateY());
    								break;
    
    							case CORNER:
    								if(ballPosition.getX() + game.getBall().getBallRadius() > gamePaneWidth / 2)
    								{
    									// ball is in top right corner
    									stackPaneBall.setTranslateX(gamePaneWidth - game.getBall().getBallRadius() * 2 - 1);
    									stackPaneBall.setTranslateY(1);
    									break;
    								}
    								else
    								{
    									// ball is in top left corner
    									stackPaneBall.setTranslateX(1);
    									stackPaneBall.setTranslateY(1);
    									break;
    								}
    
    							default:
    								break;
    						}
    					}
    				}
    				// ball doesn't collide with border --> check collision with paddle
    				else
    				{
    					hitLocation = game.collides(ballPosition, paddlePosition, paddle.getWidth(), paddle.getHeight(), true);
    					if(hitLocation != null && (hitLocation.equals(HitLocation.PADDLE) || hitLocation.equals(HitLocation.CORNER)))
    					{
    						game.getBall().setDirection(game.reflectOnPaddle(game.getBall().getDirection(), game.getDistanceToPaddleCenter(ballPosition, paddlePosition, paddle.getWidth())));
    
    						correctBallPosition(hitLocation, ballPosition, paddlePosition, paddle.getWidth(), paddle.getHeight());
    
    						resetMultiplicator();
    						
    						game.getSoundHandler().play(SoundType.HIT_PADDLE);
    					}
    					// ball doesn't collide with paddle --> check collision with bricks
    					else
    					{
    						if(game.getBall().getDirection().getX() > 0)
    						{
    							for(int i = 0; i < Board.HEIGHT; i++)
    							{
    								for(int k = 0; k < Board.WIDTH; k++)
    								{
    									brickCollisionDetection(i, k, ballPosition);
    								}
    							}
    						}
    						else
    						{
    							for(int i = (int)Board.HEIGHT - 1; i >= 0; i--)
    							{
    								for(int k = (int)Board.WIDTH - 1; k >= 0; k--)
    								{
    									brickCollisionDetection(i, k, ballPosition);
    								}
    							}
    						}
    					}
    				}
    
    				// move powerups
    				movePowerUps();
    			}
    		};
    	}
    	
    	private void pause()
    	{
    		gameState = GameState.PAUSED;
    		timer.stop();
    		anchorPaneGame.setOpacity(0.5);
    		
    		Text t = new Text("PAUSED");	
    		t.setFont(new Font(40));		
    		new Scene(new Group(t));
    		t.applyCss();	
    		
    		Label labelPause = new Label("PAUSED");
    		labelPause.setStyle("-fx-font-weight: bold; -fx-font-size: 40;");
    		labelPause.setTranslateX(((gamePaneWidth - t.getLayoutBounds().getWidth() + 10) / 2) + 22);
    		labelPause.setTranslateY(((gamePaneHeight - t.getLayoutBounds().getHeight()) / 2) + 125); 		
    		
    		anchorPane.getChildren().add(labelPause);
    		
    		//pause all timedPowerUps
    		for(CountdownTimer currentTimer : timedPowerUps)
    		{
    			currentTimer.stop();
    		}
    	}
    	
    	private void restart()
    	{
    		gameState = GameState.RUNNING;
    		previousTime = 0;
    		secondsElapsedSinceLastFpsUpdate = 0f;
    		fps = 0;
    		anchorPaneGame.setOpacity(1.0);
    		anchorPane.getChildren().remove(anchorPane.getChildren().size() - 1);
    		
    		//restart all timedPowerUps
    		for(CountdownTimer currentTimer : timedPowerUps)
    		{
    			currentTimer.start();
    		}
    				
    		timer.start();			
    	}
    
    	public void correctBallPosition(HitLocation hitLocation, Point2D ballPosition, Point2D brickPosition, double brickWidth, double brickHeight)
    	{
    		// correct ball position if inside brick
    		Point2D correctedBallPosition = game.getCorrectedBallPosition(hitLocation, ballPosition, brickPosition, brickWidth, brickHeight);
    		stackPaneBall.setTranslateX(correctedBallPosition.getX() - stackPaneBall.getLayoutX());
    		stackPaneBall.setTranslateY(correctedBallPosition.getY() - stackPaneBall.getLayoutY());
    	}
    
    	public void redraw()
    	{
    		grid.getChildren().clear();
    		brickLabels = new ArrayList<>();
    
    		grid.getColumnConstraints().clear();
    		double xPercentage = 1.0 / Board.WIDTH;
    		for(int i = 0; i < Board.WIDTH; i++)
    		{
    			ColumnConstraints c = new ColumnConstraints();
    			c.setPercentWidth(xPercentage * 100);
    			grid.getColumnConstraints().add(c);
    		}
    
    		grid.getRowConstraints().clear();
    		double yPercentage = 1.0 / Board.HEIGHT;
    		for(int i = 0; i < Board.HEIGHT; i++)
    		{
    			RowConstraints c = new RowConstraints();
    			c.setPercentHeight(yPercentage * 100);
    			grid.getRowConstraints().add(c);
    		}
    
    		for(int i = 0; i < Board.HEIGHT; i++)
    		{
    			for(int k = 0; k < Board.WIDTH; k++)
    			{
    				Brick currentBrick = game.getBoard().getBricks().get(i).get(k);
    
    				Label labelBrick = new Label();
    				labelBrick.setStyle("-fx-background-image: url(\"de/bricked/resources/textures/bricks/" + currentBrick.getCurrentTextureID() + ".png\");" + "-fx-background-position: center center;" + "-fx-background-repeat: no-repeat;" + "-fx-background-size: cover");
    				labelBrick.setAlignment(Pos.CENTER);
    
    				labelBrick.prefWidthProperty().bind(grid.getColumnConstraints().get(0).percentWidthProperty().multiply(gamePaneWidth));
    				labelBrick.prefHeightProperty().bind(grid.getRowConstraints().get(0).percentHeightProperty().multiply(gamePaneHeight));
    
    				brickLabels.add(labelBrick);
    				grid.add(labelBrick, k, i);
    			}
    		}
    	}
    
    	public static void redrawBrick(int column, int row, Brick brick, boolean fade)
    	{
    		int id = row * (int)Board.WIDTH + column;
    
    		Label labelBrick = brickLabels.get(id);
    
    		if(fade)
    		{
    			FadeTransition ft = new FadeTransition(Duration.millis(BRICK_FADE_DURATION), labelBrick);
    			ft.setFromValue(1.0);
    			ft.setToValue(0.0);
    			ft.setCycleCount(1);
    			ft.setAutoReverse(false);
    			ft.setOnFinished(new EventHandler<ActionEvent>()
    			{
    				@Override
    				public void handle(ActionEvent event)
    				{
    					labelBrick.setStyle("-fx-background-image: url(\"de/bricked/resources/textures/bricks/" + brick.getCurrentTextureID() + ".png\");" + "-fx-background-position: center center;" + "-fx-background-repeat: no-repeat;" + "-fx-background-size: cover");
    				}
    			});
    			ft.play();
    		}
    		else
    		{
    			labelBrick.setStyle("-fx-background-image: url(\"de/bricked/resources/textures/bricks/" + brick.getCurrentTextureID() + ".png\");" + "-fx-background-position: center center;" + "-fx-background-repeat: no-repeat;" + "-fx-background-size: cover");
    		}
    	}
    
    	public void refreshLiveCounter()
    	{
    		vboxLives.getChildren().clear();
    
    		for(int i = 0; i < game.getLivesRemaining() - 1; i++)
    		{
    			ImageView iv = new ImageView(new Image("de/bricked/resources/textures/paddle/paddle-small.png"));
    			iv.setFitWidth(70);
    			iv.setFitHeight(120 / game.getMaxLives());
    			vboxLives.getChildren().add(iv);
    		}
    	}
    
    	public void initPaddle(PaddleSize paddleSize)
    	{
    		anchorPaneGame.getChildren().remove(labelPaddle);
    
    		paddle = new Paddle(paddleSize, gamePaneHeight / Board.HEIGHT, gamePaneWidth);
    		labelPaddle = new ImageView(new Image("de/bricked/resources/textures/paddle/" + paddle.getPaddleSize().getTextureID() + ".png"));
    		labelPaddle.setFitWidth(paddle.getWidth());
    		labelPaddle.setFitHeight(paddle.getHeight());
    		labelPaddle.setTranslateX(gamePaneWidth / 2 - paddle.getWidth() / 2);
    		anchorPaneGame.getChildren().add(labelPaddle);
    		AnchorPane.setBottomAnchor(labelPaddle, paddle.getHeight());
    	}
    
    	private void initBall(BallType ballType)
    	{
    		anchorPaneGame.getChildren().remove(stackPaneBall);
    
    		game.setBall(new Ball(ballType));
    		
    		final Circle circle = new Circle(game.getBall().getBallRadius(), Color.web(game.getBall().getType().getColor()));
    		circle.setEffect(new Lighting());
    		stackPaneBall = new StackPane();
    		stackPaneBall.getChildren().addAll(circle);
    		stackPaneBall.setTranslateX(gamePaneWidth / 2 - game.getBall().getBallRadius());
    		stackPaneBall.setTranslateY(anchorPaneGame.getHeight() - paddle.getHeight() * 2 - game.getBall().getBallRadius() * 2);
    		anchorPaneGame.getChildren().add(stackPaneBall);
    	}
    
    	private void resetMultiplicator()
    	{
    		game.applyMultiplicator();		
    		labelPoints.setText(String.valueOf(game.getTotalPoints()));
    		labelMultiplicator.setText("");
    		if(game.getMultiplicator() > 1)
    		{
    			showAnimatedPoints(20, 8, game.getPointsSinceLastMultiplicatorReset() * game.getMultiplicator(), 25, true);
    		}
    		game.resetPointsSinceLastMultiplicatorReset();
    		game.resetMultiplicator();		
    	}
    
    	public void increaseMultiplicator(int points)
    	{
    		game.increaseMultiplicator();
    		game.increasePointsSinceLastMultiplicatorReset(points);
    		
    		if(game.getMultiplicator() > 1)
    		{		
    			labelMultiplicator.setText("x" + game.getMultiplicator());
    		}
    	}
    
    	private void movePaddleLeft()
    	{
    		if(labelPaddle.getLayoutX() + labelPaddle.getTranslateX() - paddle.getSpeed() > 0)
    		{
    			labelPaddle.setTranslateX(labelPaddle.getTranslateX() - paddle.getSpeed());
    		}
    		else
    		{
    			labelPaddle.setTranslateX(0);
    		}
    	}
    
    	private void movePaddleRight()
    	{
    		if(labelPaddle.getLayoutX() + labelPaddle.getTranslateX() + paddle.getSpeed() + paddle.getWidth() < gamePaneWidth)
    		{
    			labelPaddle.setTranslateX(labelPaddle.getTranslateX() + paddle.getSpeed());
    		}
    		else
    		{
    			labelPaddle.setTranslateX(anchorPaneGame.getWidth() - paddle.getWidth());
    		}
    	}
    
    	private void brickCollisionDetection(int i, int k, Point2D ballPosition)
    	{
    		Brick currentBrick = game.getBoard().getBricks().get(i).get(k);
    		if(!currentBrick.getType().equals(BrickType.AIR))
    		{
    			Label stackPaneBrick = (Label)grid.getChildren().get(i * (int)Board.WIDTH + k);
    
    			Point2D brickPosition = new Point2D(stackPaneBrick.getLayoutX() + stackPaneBrick.getTranslateX(), stackPaneBrick.getLayoutY() + stackPaneBrick.getTranslateY());
    
    			HitLocation hitLocation = game.collides(ballPosition, brickPosition, stackPaneBrick.getWidth(), stackPaneBrick.getHeight(), false);
    			if(hitLocation != null)
    			{
    				if(!game.getBall().getType().equals(BallType.NO_COLLISION))
    				{
    					game.getBall().setDirection(game.reflectBall(hitLocation, game.getBall().getDirection()));
    
    					correctBallPosition(hitLocation, ballPosition, brickPosition, stackPaneBrick.getWidth(), stackPaneBrick.getHeight());
    				}
    
    				int points = game.getBoard().hitBrick(i, k, game.getBall());
    				// brick has been destroyed
    				if(points > 0)
    				{				
    					game.setTotalPoints(game.getTotalPoints() + points);
    					labelPoints.setText(String.valueOf(game.getTotalPoints()));
    					labelBlocksRemaining.setText(game.getBoard().getNumberOfRemainingBricks() + " Bricks remaining");
    				}
    
    				if(game.getBoard().getNumberOfRemainingBricks() == 0)
    				{
    					// level done
    					resetPowerUps();
    					gameState = GameState.STOPPED;
    					resetMultiplicator();
    					timer.stop();
    					
    					game.getSoundHandler().play(SoundType.FINISHED_LEVEL);		
    
    					Platform.runLater(() -> {						
    						AlertGenerator.showAlert(Alert.AlertType.INFORMATION, "Congratulations!", "", "You finished Level \"" + game.getLevel().getName() + "\" with " + game.getTotalPoints() + " Points", icon, stage, bundle.getString("color.background"), false);
    					});
    				}
    			}
    		}
    	}
    
    	public void showAnimatedPoints(int row, int col, int points, double fontSize, boolean centerInPane)
    	{		
    		Text t = new Text("+" + points);	
    		t.setFont(new Font(fontSize));		
    		new Scene(new Group(t));
    		t.applyCss();		
    		
    		double xPosition = (gamePaneWidth / Board.WIDTH) * (col);
    		if(centerInPane)
    		{
    			xPosition = (gamePaneWidth - t.getLayoutBounds().getWidth() + 10) / 2;
    		}
    		double yPosition = (gamePaneHeight / Board.HEIGHT) * (row);
    
    		Label labelNotification = new Label("+" + points);
    		labelNotification.setTranslateX(xPosition);
    		labelNotification.setTranslateY(yPosition);
    		labelNotification.setStyle("-fx-font-weight: bold; -fx-font-size: " + fontSize + "; -fx-text-fill: #000000;");
    		labelNotification.setAlignment(Pos.CENTER);	
    		
    		labelNotification.setPrefWidth(t.getLayoutBounds().getWidth() + 10);
    		labelNotification.setPrefHeight(gamePaneHeight / Board.HEIGHT);
    		anchorPaneGame.getChildren().add(labelNotification);
    
    		FadeTransition fadeTransition = new FadeTransition(Duration.millis(1200), labelNotification);
    		fadeTransition.setFromValue(1.0);
    		fadeTransition.setToValue(0.0);
    		fadeTransition.setCycleCount(1);
    		fadeTransition.setAutoReverse(false);
    		TranslateTransition translateTransition = new TranslateTransition(Duration.millis(1200), labelNotification);
    		translateTransition.setFromY(yPosition);
    		translateTransition.setToY(yPosition - (gamePaneHeight / Board.HEIGHT));
    		translateTransition.setCycleCount(1);
    		translateTransition.setAutoReverse(false);
    
    		ParallelTransition parallelTransition = new ParallelTransition();
    		parallelTransition.getChildren().addAll(fadeTransition, translateTransition);
    		parallelTransition.setCycleCount(1);
    		parallelTransition.setOnFinished(new EventHandler<ActionEvent>()
    		{
    			@Override
    			public void handle(ActionEvent event)
    			{
    				anchorPaneGame.getChildren().remove(labelNotification);
    			}
    		});
    
    		parallelTransition.play();
    	}
    
    	public void addMovingPowerUp(int row, int col, PowerUp powerUp)
    	{
    		Label labelPowerUp = new Label();
    		labelPowerUp.setStyle("-fx-background-image: url(\"de/bricked/resources/textures/powerups/" + powerUp.getID() + ".png\");" + "-fx-background-position: center center;" + "-fx-background-repeat: no-repeat;" + "-fx-background-size: cover");
    		labelPowerUp.setAlignment(Pos.CENTER);
    		labelPowerUp.setUserData(powerUp);
    
    		labelPowerUp.setPrefWidth(gamePaneWidth / Board.WIDTH);
    		labelPowerUp.setPrefHeight(gamePaneHeight / Board.HEIGHT);
    
    		anchorPaneGame.getChildren().add(labelPowerUp);
    		labelPowerUp.setTranslateX(col * (gamePaneWidth / Board.WIDTH));
    		labelPowerUp.setTranslateY(row * (gamePaneHeight / Board.HEIGHT));
    
    		movingPowerUps.add(labelPowerUp);
    	}
    
    	private void movePowerUps()
    	{
    		for(Iterator<Label> iterator = movingPowerUps.iterator(); iterator.hasNext();)
    		{
    			Label currentLabel = iterator.next();
    			PowerUp currentPowerUp = (PowerUp)currentLabel.getUserData();
    			currentLabel.setTranslateY(currentLabel.getTranslateY() + currentPowerUp.getSpeed());
    
    			// check collision with paddle
    			Point2D labelPosition = new Point2D(currentLabel.getTranslateX(), currentLabel.getTranslateY());
    			Point2D paddlePosition = new Point2D(labelPaddle.getLayoutX() + labelPaddle.getTranslateX(), labelPaddle.getLayoutY() + labelPaddle.getTranslateY());
    
    			HitLocation hitLocation = game.collides(labelPosition, paddlePosition, paddle.getWidth(), paddle.getHeight(), true);
    			if(hitLocation != null && (hitLocation.equals(HitLocation.PADDLE) || hitLocation.equals(HitLocation.CORNER)))
    			{
    				Logger.log(LogLevel.DEBUG, "Collected PowerUp with ID = " + currentPowerUp.getID());
    				if(!currentPowerUp.isPermanent())
    				{
    					addTimedPowerUp(currentPowerUp);
    				}
    				currentPowerUp.activate(this, game);
    				anchorPaneGame.getChildren().remove(currentLabel);
    				iterator.remove();
    				continue;
    			}
    
    			if(currentLabel.getTranslateY() + currentLabel.getHeight() >= gamePaneHeight)
    			{
    				// power up reached bottom wall
    				anchorPaneGame.getChildren().remove(currentLabel);
    				iterator.remove();
    			}
    		}
    	}
    	
    	private void clearMovingPowerUps()
    	{
    		if(movingPowerUps != null)
    		{
    			for(Label currentLabel : movingPowerUps)
    			{
    				anchorPaneGame.getChildren().remove(currentLabel);
    			}
    		}
    	}
    
    	private void addTimedPowerUp(PowerUp powerUp)
    	{
    		boolean alreadyActivated = false;
    		
    		for(CountdownTimer currentTimer : timedPowerUps)
    		{
    			PowerUp currentPowerUp = (PowerUp)currentTimer.getHBox().getUserData();
    			if(currentPowerUp.getID() == powerUp.getID())
    			{				
    				alreadyActivated = true;
    				currentTimer.addSecondsToTimer(powerUp.getDurationInSeconds());
    				break;
    			}
    		}	
    		
    		if(!alreadyActivated)
    		{
    			deactivateAllContraryPowerUps(powerUp);
    			
    			HBox hbox = new HBox();			
    			Label labelIcon = new Label();
    			labelIcon.setStyle("-fx-background-image: url(\"de/bricked/resources/textures/powerups/" + powerUp.getID() + ".png\");" + "-fx-background-position: center center;" + "-fx-background-repeat: no-repeat;" + "-fx-background-size: contain;");				
    			labelIcon.setPrefWidth(35);
    			labelIcon.setPrefHeight(20);
    			
    			Label labelSeconds = new Label(String.valueOf(powerUp.getDurationInSeconds()));
    			labelSeconds.setStyle("-fx-font-size: 16;" + "-fx-font-weight: bold; -fx-text-fill: #000000;");
    			
    			hbox.getChildren().add(labelIcon);
    			hbox.getChildren().add(labelSeconds);
    			HBox.setHgrow(labelSeconds, Priority.ALWAYS);
    			HBox.setMargin(labelSeconds, new Insets(0, 0, 0, 7));
    			hbox.setAlignment(Pos.CENTER_LEFT);
    						
    			hbox.setUserData(powerUp);	
    			vboxPowerUps.getChildren().add(hbox);
    	
    			timedPowerUps.add(new CountdownTimer(powerUp.getDurationInSeconds(), hbox, this));
    		}	
    	}	
    	
    	private void resetPowerUps()
    	{
    		clearMovingPowerUps();
    		movingPowerUps = new ArrayList<>();
    		timedPowerUps = new ArrayList<>();
    		vboxPowerUps.getChildren().clear();
    	}
    	
    	public void deactivateAllContraryPowerUps(PowerUp powerUp)
    	{
    		ArrayList<Integer> deactiveIDs = PowerUpType.valueOf(powerUp.getID()).getDeactivatesPowerUpIDs();
    		if(deactiveIDs != null)
    		{
    			for(int currentInt : deactiveIDs)
    			{
    				for(CountdownTimer currentTimer : timedPowerUps)
    				{
    					PowerUp currentPowerUp = (PowerUp)currentTimer.getHBox().getUserData();
    					if(currentPowerUp.getID() == currentInt)
    					{
    						currentTimer.stop();
    						deactivatePowerUp(currentTimer, currentTimer.getHBox());
    						break;
    					}
    				}
    			}			
    		}
    	}
    
    	public void deactivatePowerUp(CountdownTimer timer, HBox hbox)
    	{		
    		PowerUp powerUp = (PowerUp)hbox.getUserData();		
    		vboxPowerUps.getChildren().remove(hbox);
    		timedPowerUps.remove(timer);
    		powerUp.deactivate(this, game);		
    	}
    
    	public void showLabelFPS(boolean value)
    	{
    		labelFPS.setVisible(value);
    	}
    
    	public void showCommandLine()
    	{
    		try
    		{
    			levelSelectController.controller.controller.getCommandLine().showCommandLine("Debug Console", 400, 250, 400, 200, -1, -1, true);
    		}
    		catch(IOException e)
    		{
    			// TODO: errorhandling
    			Logger.log(LogLevel.ERROR, Logger.exceptionToString(e));
    		}
    	}
    
    	public void back()
    	{
    		if(timer != null)
    		{
    			timer.stop();
    		}
    		stage.close();
    		levelSelectController.stage.show();
    		game.setTotalPoints(0);
    		game.resetMultiplicator();
    		game.resetPointsSinceLastMultiplicatorReset();
    		game.setBoard(null);
    		game.setLevelController(null);
    		game.setMovingPowerUps(new ArrayList<>());	
    		
    		game.getSoundHandler().stopAll();
    
    		anchorPaneGame.requestFocus();
    	}
    
    	/*
    	 * PowerUP-Functions
    	 */
    	public void changeBall(Ball newBall)
    	{
    		double translateX = stackPaneBall.getTranslateX();
    		double translateY = stackPaneBall.getTranslateY();
    		Point2D direction = game.getBall().getDirection();
    		game.setBall(newBall);
    
    		initBall(game.getBall().getType());
    		stackPaneBall.setTranslateX(translateX);
    		stackPaneBall.setTranslateY(translateY);
    		game.getBall().setDirection(game.getNewSpeedDirection(direction, newBall.getType().getSpeedFactor()));
    	}
    }