Commits

zonski  committed 347cc31

Added Level choosing logic.

  • Participants
  • Parent commits 689cb5b

Comments (0)

Files changed (22)

File defender/pom.xml

                 <version>1.3</version>
                 <configuration>
                     <mainClass>com.fxexperience.games.defender.DefenderApp</mainClass>
+
+                    <keyStoreAlias>fxeperience</keyStoreAlias>
+                    <keyStorePassword>fxexperience</keyStorePassword>
+                    <permissions>
+                        <permission>all-permissions</permission>
+                    </permissions>
+
                 </configuration>
             </plugin>
             <plugin>

File defender/src/main/deploy/keystore.jks

Binary file added.

File defender/src/main/java/com/fxexperience/games/defender/games/simple/Enemy.java

      * The Path that the enemy must follow. If the enemy reaches the end of the Path without being destroyed then
      * the player loses a life and the enemy destroys itself.
      */
-    private final ObjectProperty<LevelPanel> level = new SimpleObjectProperty<>(this, "level");
-    public final LevelPanel getLevel() { return level.get(); }
-    public final void setLevel(LevelPanel level) { this.level.set(level); }
-    public final ObjectProperty<LevelPanel> levelProperty() { return level; }
+    private final ObjectProperty<Level> level = new SimpleObjectProperty<>(this, "level");
+    public final Level getLevel() { return level.get(); }
+    public final void setLevel(Level level) { this.level.set(level); }
+    public final ObjectProperty<Level> levelProperty() { return level; }
 
     /**
      * When life reaches 0 (or goes negative), the enemy is destroyed.

File defender/src/main/java/com/fxexperience/games/defender/games/simple/Game.java

     public final DoubleProperty money = new SimpleDoubleProperty(this, "money", 100);
     public final IntegerProperty lives = new SimpleIntegerProperty(this, "lives", 10);
 
-    private final ObjectProperty<LevelPanel> currentLevel = new SimpleObjectProperty<>(this, "currentLevel");
+    private final ObjectProperty<Level> currentLevel = new SimpleObjectProperty<>(this, "currentLevel");
 
-    public ObjectProperty<LevelPanel> currentLevelProperty() { return currentLevel; }
-    public LevelPanel getCurrentLevel() { return this.currentLevel.get(); }
-    public void setCurrentLevel(LevelPanel level) { this.currentLevel.set(level); }
+    public ObjectProperty<Level> currentLevelProperty() { return currentLevel; }
+    public Level getCurrentLevel() { return this.currentLevel.get(); }
+    public void setCurrentLevel(Level level) { this.currentLevel.set(level); }
 
     private AnimationTimer animationTimer;
     private List<GamePulseListener> gamePulseListeners;
             }
         };
 
-        currentLevel.addListener(new ChangeListener<LevelPanel>() {
+        currentLevel.addListener(new ChangeListener<Level>() {
             @Override
-            public void changed(ObservableValue<? extends LevelPanel> source, LevelPanel oldLevel, LevelPanel newLevel) {
+            public void changed(ObservableValue<? extends Level> source, Level oldLevel, Level newLevel) {
                 if (oldLevel != null) {
                     gamePulseListeners.remove(oldLevel);
                     gamePanel.setCenter(null);
     }
 
     public void showNewGamePanel() {
+        paused.set(true);
         newGamePanel.reset();
         getChildren().setAll(gamePanel, newGamePanel);
         newGamePanel.playEntryAnimation();

File defender/src/main/java/com/fxexperience/games/defender/games/simple/Level.java

  * (or set of paths) on which the enemies roam. Multiple waves of enemies will come following one or
  * more of those paths.
  */
-public class LevelPanel extends Region implements GamePulseListener {
-    private static final Logger log = LoggerFactory.getLogger(LevelPanel.class);
+public class Level extends Region implements GamePulseListener {
+    private static final Logger log = LoggerFactory.getLogger(Level.class);
 
     private static final double LEVEL_HEIGHT = 600;
     private static final double LEVEL_WIDTH = 800;
 
-    private Region background;
+    private Node background;
     private Rectangle clip;
     private Pane enemyLayer;
     private Pane towerLayer;
     private List<Tower> towers;
     private List<Projectile> projectiles;
 
-    public LevelPanel(final Path path, Wave... waves) {
+    public Level(final Path path, Node background, Wave... waves) {
 
-        getStyleClass().add("game-board");
+        getStyleClass().add("level");
         this.waves = new LinkedList<>(Arrays.asList(waves));
         this.path = path;
+        this.background = background;
 
-        background = new Region();
-        background.getStyleClass().add("background");
         enemies = new ArrayList<>();
         enemyLayer = new Pane();
         towers = new ArrayList<>();

File defender/src/main/java/com/fxexperience/games/defender/games/simple/LevelFactory.java

+/*
+* Copyright (c) 2012, FX Experience. All rights  reserved.
+*
+* Redistribution and use in source and binary forms, with or without modification, are
+* permitted provided that the following conditions are met:
+*
+* Redistributions of source code must retain the above copyright notice, this list of
+* conditions and the following disclaimer. 
+*
+* Redistributions in binary form must reproduce the above copyright notice, this list of 
+* conditions and the following disclaimer in the documentation and/or other materials provided 
+* with the distribution.    
+*
+* Neither the name of the author nor the names of its contributors may be used to endorse or
+* promote products derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+package com.fxexperience.games.defender.games.simple;
+
+public interface LevelFactory {
+
+    String getName();
+
+    Level createLevel();
+}

File defender/src/main/java/com/fxexperience/games/defender/games/simple/Projectile.java

 
 public class Projectile extends Parent implements GamePulseListener {
 
-    protected LevelPanel level;
+    protected Level level;
 
-    public void setLevel(LevelPanel level) {
+    public void setLevel(Level level) {
         this.level = level;
     }
 

File defender/src/main/java/com/fxexperience/games/defender/games/simple/Tower.java

 public class Tower extends Parent implements GamePulseListener {
 
     protected String name;
-    protected LevelPanel level;
+    protected Level level;
 
     public Tower(String name) {
         this.name = name;
     // is hit. For example, if the blue tower hits a blue enemy, it has 2x damage, but
     // if the tower hits a green enemy, it has .5x damage.
 
-    public void setLevel(LevelPanel level) {
+    public void setLevel(Level level) {
         this.level = level;
     }
 

File defender/src/main/java/com/fxexperience/games/defender/games/simple/Wave.java

     private final LinkedList<Enemy> enemies = new LinkedList<>();
     private Path path;
     private final Duration delay;
-    private LevelPanel level;
+    private Level level;
     private long lastSpawn;
 
     public Wave(Duration delay, Enemy... enemies) {
         this.enemies.addAll(Arrays.asList(enemies));
     }
 
-    public void setLevel(LevelPanel level) {
+    public void setLevel(Level level) {
         this.level = level;
     }
 

File defender/src/main/java/com/fxexperience/games/defender/games/simple/enemies/SpriteEnemy.java

     public SpriteEnemy() {
         super(300, 10);
 
-        AnimatedImageNode animatedNode = new AnimatedImageNode(new Image("flier.png"), 80, 63, 2, 20);
+        AnimatedImageNode animatedNode = new AnimatedImageNode(new Image("images/flier.png"), 80, 63, 2, 20);
         getChildren().add(animatedNode);
         animatedNode.playFromStart();
     }

File defender/src/main/java/com/fxexperience/games/defender/games/simple/levels/BrownLevelFactory.java

+/*
+ * Copyright (c) 2012, FX Experience. All rights  reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.fxexperience.games.defender.games.simple.levels;
+
+import com.fxexperience.games.defender.games.simple.Level;
+import com.fxexperience.games.defender.games.simple.LevelFactory;
+import com.fxexperience.games.defender.games.simple.Wave;
+import com.fxexperience.games.defender.games.simple.enemies.*;
+import javafx.scene.layout.HBox;
+import javafx.scene.shape.LineTo;
+import javafx.scene.shape.MoveTo;
+import javafx.scene.shape.Path;
+import javafx.util.Duration;
+
+public class BrownLevelFactory implements LevelFactory {
+
+    @Override
+    public String getName() {
+        return "Bricks";
+    }
+
+    @Override
+    public Level createLevel() {
+        Path path = new Path(
+                new MoveTo(-20, -20),
+                new LineTo(100, 100),
+                new LineTo(600, 150),
+                new LineTo(640, 300),
+                new LineTo(100, 340),
+                new LineTo(1024, 768)
+        );
+
+
+        Wave wave1 = new Wave(
+                Duration.seconds(1),
+                new VectorEnemy(),
+                new VectorEnemy(),
+                new VectorEnemy(),
+                new VectorEnemy(),
+                new VectorEnemy(),
+                new VectorEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy(),
+                new SpriteEnemy()
+        );
+
+        Wave wave2 = new Wave(
+                Duration.seconds(1),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy()
+        );
+
+        Wave wave3 = new Wave(
+                Duration.seconds(3),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy()
+        );
+
+        Wave wave4 = new Wave(
+                Duration.seconds(1),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new GreenEnemy(),
+                new BlackEnemy(),
+                new BlackEnemy(),
+                new BlueEnemy(),
+                new BlueEnemy(),
+                new BlackEnemy()
+        );
+
+        HBox background = new HBox();
+        background.setStyle("-fx-background-color: SANDYBROWN");
+
+        return new Level(path, background, wave1, wave2, wave3, wave4);
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/games/simple/levels/FirstLevel.java

-/*
- * Copyright (c) 2012, FX Experience. All rights  reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice, this list of
- * conditions and the following disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * Neither the name of the author nor the names of its contributors may be used to endorse or
- * promote products derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.fxexperience.games.defender.games.simple.levels;
-
-import com.fxexperience.games.defender.games.simple.LevelPanel;
-import com.fxexperience.games.defender.games.simple.Wave;
-import com.fxexperience.games.defender.games.simple.enemies.BlackEnemy;
-import com.fxexperience.games.defender.games.simple.enemies.BlueEnemy;
-import com.fxexperience.games.defender.games.simple.enemies.GreenEnemy;
-import com.fxexperience.games.defender.games.simple.enemies.SpriteEnemy;
-import com.fxexperience.games.defender.games.simple.enemies.VectorEnemy;
-import javafx.scene.shape.LineTo;
-import javafx.scene.shape.MoveTo;
-import javafx.scene.shape.Path;
-import javafx.util.Duration;
-
-public class FirstLevel extends LevelPanel {
-    public FirstLevel() {
-        super(new Path(
-                new MoveTo(-20, -20),
-                new LineTo(100, 100),
-                new LineTo(600, 150),
-                new LineTo(640, 300),
-                new LineTo(100, 340),
-                new LineTo(1024, 768)
-        ), new Wave(
-                Duration.seconds(1),
-                new VectorEnemy(),
-                new VectorEnemy(),
-                new VectorEnemy(),
-                new VectorEnemy(),
-                new VectorEnemy(),
-                new VectorEnemy(),
-                new VectorEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy(),
-                new SpriteEnemy()
-        ), new Wave(
-                Duration.seconds(1),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy()
-        ), new Wave(
-                Duration.seconds(3),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy()
-        ), new Wave(
-                Duration.seconds(1),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new GreenEnemy(),
-                new BlackEnemy(),
-                new BlackEnemy(),
-                new BlueEnemy(),
-                new BlueEnemy(),
-                new BlackEnemy()
-        ));
-    }
-}

File defender/src/main/java/com/fxexperience/games/defender/games/simple/levels/GrassLevelFactory.java

+/*
+ * Copyright (c) 2012, FX Experience. All rights  reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.fxexperience.games.defender.games.simple.levels;
+
+import com.fxexperience.games.defender.games.simple.Level;
+import com.fxexperience.games.defender.games.simple.LevelFactory;
+import com.fxexperience.games.defender.games.simple.Wave;
+import com.fxexperience.games.defender.games.simple.enemies.SpriteEnemy;
+import com.fxexperience.games.defender.games.simple.enemies.VectorEnemy;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.image.Image;
+import javafx.scene.shape.LineTo;
+import javafx.scene.shape.MoveTo;
+import javafx.scene.shape.Path;
+import javafx.util.Duration;
+
+public class GrassLevelFactory implements LevelFactory {
+
+    @Override
+    public String getName() {
+        return "Grass";
+    }
+
+    @Override
+    public Level createLevel() {
+
+        Path path = new Path(
+                new MoveTo(-20, -20),
+                new LineTo(100, 100),
+                new LineTo(600, 150),
+                new LineTo(640, 300),
+                new LineTo(100, 340),
+                new LineTo(1024, 768)
+        );
+
+
+        Wave wave1 = new Wave(
+                Duration.seconds(1),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy()
+        );
+
+
+        Wave wave2 = new Wave(
+                Duration.seconds(1),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy(),
+                new VectorEnemy(),
+                new SpriteEnemy()
+        );
+
+        Canvas background = new Canvas(800, 600);
+        GraphicsContext g = background.getGraphicsContext2D();
+        double x = 0;
+        double y = 0;
+        Image tile = new Image(getClass().getResourceAsStream("/images/grass.png"));
+        while (y < background.getHeight()) {
+            x = 0;
+            while (x < background.getWidth()) {
+                g.drawImage(tile, x, y);
+                x += tile.getWidth();
+            }
+            y += tile.getHeight();
+        }
+        return new Level(path, background, wave1, wave2);
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/games/simple/ui/GameControlPanel.java

 package com.fxexperience.games.defender.games.simple.ui;
 
 import com.fxexperience.games.defender.games.simple.Game;
-import com.fxexperience.games.defender.games.simple.LevelPanel;
+import com.fxexperience.games.defender.games.simple.Level;
 import com.fxexperience.games.defender.games.simple.Tower;
 import com.fxexperience.games.defender.games.simple.TowerFactory;
 import com.fxexperience.games.defender.games.simple.towers.BasicTowerFactory;
         playButton.setText(game.paused.get() ? "Send The Enemies!" : "Pause");
     }
 
+    public void startNewGame() {
+        game.showNewGamePanel();
+    }
 
     protected ToggleButton createTowerButton(ToggleGroup toggleGroup, final Game game, final TowerFactory towerFactory, int column, int row) {
         final ToggleButton towerButton = new ToggleButton(towerFactory.getTowerName());
         towerButton.setOnAction(new EventHandler<ActionEvent>() {
             @Override
             public void handle(ActionEvent actionEvent) {
-                final LevelPanel level = game.getCurrentLevel();
+                final Level level = game.getCurrentLevel();
                 if (level != null) {
                     final Tower tower = towerFactory.createTower();
                     tower.setTranslateX(-100);

File defender/src/main/java/com/fxexperience/games/defender/games/simple/ui/NewGamePanel.java

 package com.fxexperience.games.defender.games.simple.ui;
 
 import com.fxexperience.games.defender.games.simple.Game;
-import com.fxexperience.games.defender.games.simple.levels.FirstLevel;
+import com.fxexperience.games.defender.games.simple.LevelFactory;
+import com.fxexperience.games.defender.games.simple.levels.BrownLevelFactory;
+import com.fxexperience.games.defender.games.simple.levels.GrassLevelFactory;
 import javafx.animation.FadeTransitionBuilder;
 import javafx.animation.ParallelTransition;
 import javafx.animation.ParallelTransitionBuilder;
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.fxml.FXML;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
 import javafx.scene.layout.Pane;
 import javafx.scene.layout.StackPane;
 import javafx.util.Duration;
 
     @FXML private Pane dialog;
     @FXML private Pane glassPane;
+    @FXML private Pane levelButtonArea;
+
+    private ToggleGroup levelToggleGroup;
 
     public NewGamePanel(final Game game) {
         this.game = game;
         FxmlHelper.load("/fxml/NewGamePanel.fxml", this);
+
+        // hard code available levels for now - eventually derive from unlocked levels, etc
+        levelToggleGroup = new ToggleGroup();
+        createLevelButton(new BrownLevelFactory());
+        createLevelButton(new GrassLevelFactory());
+        levelToggleGroup.selectToggle(levelToggleGroup.getToggles().get(0));
     }
 
     public void reset() {
     }
 
     public void startGame() {
+
+        LevelFactory selectedLevelFactory = (LevelFactory) levelToggleGroup.getSelectedToggle().getUserData();
         game.paused.set(true);
-        game.setCurrentLevel(new FirstLevel());
+        game.setCurrentLevel(selectedLevelFactory.createLevel());
         playExitAnimation();
     }
 
 
         transition.play();
     }
+
+    protected void createLevelButton(LevelFactory levelFactory) {
+        ToggleButton levelButton = new ToggleButton(levelFactory.getName());
+        levelButton.getStyleClass().add("level-button");
+        levelButton.setUserData(levelFactory);
+        levelToggleGroup.getToggles().add(levelButton);
+        levelButtonArea.getChildren().add(levelButton);
+    }
 }

File defender/src/main/java/com/fxexperience/games/defender/games/simple/ui/WelcomePanel.java

 import javafx.event.EventHandler;
 import javafx.fxml.FXML;
 import javafx.scene.control.Label;
+import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.VBox;
 import javafx.util.Duration;
 
     @FXML private VBox bottomHalf;
     @FXML private Label producedByLabel;
 
+    private SequentialTransition transition;
+
     public WelcomePanel(final Game game) {
         this.game = game;
         FxmlHelper.load("/fxml/WelcomePanel.fxml", this);
+
+        setOnMouseClicked(new EventHandler<MouseEvent>() {
+            @Override
+            public void handle(MouseEvent event) {
+                if (transition != null) {
+                    transition.stop();
+                    game.showNewGamePanel();
+                }
+            }
+        });
     }
 
     public void reset() {
 
     public void playWelcome() {
 
-        SequentialTransition transition = new SequentialTransition();
+        if (transition == null) {
+            transition = new SequentialTransition();
 
-        // animate in the text
+            // animate in the text
 
-        transition.getChildren().add(
-                ScaleTransitionBuilder.create()
-                        .node(titleLabel)
-                        .duration(Duration.millis(1500))
-                        .fromX(0).fromY(0)
-                        .toX(1).toY(1)
-                        .build());
+            transition.getChildren().add(
+                    ScaleTransitionBuilder.create()
+                            .node(titleLabel)
+                            .duration(Duration.millis(1500))
+                            .fromX(0).fromY(0)
+                            .toX(1).toY(1)
+                            .build());
 
-        transition.getChildren().add(
-                FadeTransitionBuilder.create()
-                        .node(captionLabel)
-                        .duration(Duration.millis(1500))
-                        .fromValue(0)
-                        .toValue(1)
-                        .build());
+            transition.getChildren().add(
+                    FadeTransitionBuilder.create()
+                            .node(captionLabel)
+                            .duration(Duration.millis(1500))
+                            .fromValue(0)
+                            .toValue(1)
+                            .build());
 
-        transition.getChildren().add(
-                ParallelTransitionBuilder.create()
-                        .children(
-                                RotateTransitionBuilder.create()
-                                        .node(producedByLabel)
-                                        .duration(Duration.millis(1500))
-                                        .fromAngle(180)
-                                        .toAngle(0)
-                                        .build(),
+            transition.getChildren().add(
+                    ParallelTransitionBuilder.create()
+                            .children(
+                                    RotateTransitionBuilder.create()
+                                            .node(producedByLabel)
+                                            .duration(Duration.millis(1500))
+                                            .fromAngle(180)
+                                            .toAngle(0)
+                                            .build(),
 
-                                FadeTransitionBuilder.create()
-                                        .node(producedByLabel)
-                                        .duration(Duration.millis(1500))
-                                        .fromValue(0)
-                                        .toValue(1)
-                                        .build(),
+                                    FadeTransitionBuilder.create()
+                                            .node(producedByLabel)
+                                            .duration(Duration.millis(1500))
+                                            .fromValue(0)
+                                            .toValue(1)
+                                            .build(),
 
-                                ScaleTransitionBuilder.create()
-                                        .node(producedByLabel)
-                                        .duration(Duration.millis(1500))
-                                        .fromX(0).fromY(0)
-                                        .toX(1).toY(1)
-                                        .build()
-                        )
-                        .build()
-                );
+                                    ScaleTransitionBuilder.create()
+                                            .node(producedByLabel)
+                                            .duration(Duration.millis(1500))
+                                            .fromX(0).fromY(0)
+                                            .toX(1).toY(1)
+                                            .build()
+                            )
+                            .build()
+                    );
 
 
-        // give everyone a moment to take it all in
+            // give everyone a moment to take it all in
 
-        transition.getChildren().add(new PauseTransition(Duration.seconds(1)));
+            transition.getChildren().add(new PauseTransition(Duration.seconds(1)));
 
-        // open up the gates when done
+            // open up the gates when done
 
-        transition.getChildren().add(ParallelTransitionBuilder.create()
-                .children(
+            transition.getChildren().add(ParallelTransitionBuilder.create()
+                    .children(
 
-                        TranslateTransitionBuilder.create()
-                                .node(topHalf)
-                                .duration(Duration.millis(1000))
-                                .fromY(0)
-                                .toY(-game.getHeight() / 2 - 100)
-                                .build(),
+                            TranslateTransitionBuilder.create()
+                                    .node(topHalf)
+                                    .duration(Duration.millis(1000))
+                                    .fromY(0)
+                                    .toY(-game.getHeight() / 2 - 100)
+                                    .build(),
 
-                        TranslateTransitionBuilder.create()
-                                .node(bottomHalf)
-                                .duration(Duration.millis(1000))
-                                .fromY(0)
-                                .toY(game.getHeight() / 2 + 100)
-                                .build()
+                            TranslateTransitionBuilder.create()
+                                    .node(bottomHalf)
+                                    .duration(Duration.millis(1000))
+                                    .fromY(0)
+                                    .toY(game.getHeight() / 2 + 100)
+                                    .build()
 
-                ).build());
+                    ).build());
 
-        transition.setOnFinished(new EventHandler<ActionEvent>() {
-            public void handle(ActionEvent actionEvent) {
-                game.showNewGamePanel();
-            }
-        });
-        transition.play();
+            transition.setOnFinished(new EventHandler<ActionEvent>() {
+                public void handle(ActionEvent actionEvent) {
+                    game.showNewGamePanel();
+                }
+            });
+        }
+
+        transition.playFromStart();
     }
 }

File defender/src/main/resources/flier.png

Removed
Old image

File defender/src/main/resources/fxml/GameControlPanel.fxml

 
     <Button fx:id="playButton" text="Send The Enemies!" maxWidth="Infinity" onAction="#togglePlaying"/>
 
+    <Button fx:id="newGameButton" text="New Game" maxWidth="Infinity" onAction="#startNewGame"/>
+
 </fx:root>

File defender/src/main/resources/fxml/NewGamePanel.fxml

 
     <Pane fx:id="glassPane" styleClass="glass-pane"/>
 
-    <VBox alignment="CENTER" fillWidth="false" >
-        <VBox fx:id="dialog" styleClass="dialog" VBox.vgrow="NEVER" fillWidth="false" alignment="CENTER">
+    <VBox alignment="CENTER" fillWidth="false">
+        <VBox fx:id="dialog" styleClass="dialog" spacing="20" VBox.vgrow="NEVER" fillWidth="false" alignment="CENTER">
             <Label fx:id="titleLabel" styleClass="title" alignment="CENTER" text="Start New Game"/>
-            <Label fx:id="captionLabel" alignment="CENTER" text="todo: choose difficulty and level"/>
+
+            <VBox styleClass="select-difficulty-area">
+                <Label text="Select difficulty"/>
+                <HBox fx:id="difficultyButtonArea" spacing="10">
+                    <Label text="todo: difficulties"/>
+                </HBox>
+            </VBox>
+
+            <VBox styleClass="select-level-area">
+                <Label text="Select level to play"/>
+                <HBox fx:id="levelButtonArea" spacing="10"/>
+            </VBox>
 
             <Button fx:id="startGameButton" alignment="CENTER" text="Start Game" onAction="#startGame"/>
         </VBox>

File defender/src/main/resources/images/flier.png

Added
New image

File defender/src/main/resources/images/grass.png

Added
New image

File defender/src/main/resources/styles/games/simple/styles.css

 
 
 /******************************************
-    Styling for the Game Board
+    Styling for the Level
 ******************************************/
 
-.background {
+.level {
     -fx-background-color: darkblue, ghostwhite;
     -fx-background-insets: 0, 1;
 }