Commits

zonski  committed 8373b7a

Added a lot of UI stuff, refactored the Level/Game relationship and implemented a base timeline concept to allow for pausing, etc.

  • Participants
  • Parent commits 740aada

Comments (0)

Files changed (21)

File defender/src/main/java/com/fxexperience/games/defender/BadGuy.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;
-
-import javafx.animation.*;
-import javafx.collections.ListChangeListener;
-import javafx.collections.ObservableList;
-import javafx.event.ActionEvent;
-import javafx.event.EventHandler;
-import javafx.geometry.Point2D;
-import javafx.scene.paint.Color;
-import javafx.scene.shape.CubicCurveTo;
-import javafx.scene.shape.MoveTo;
-import javafx.scene.shape.Path;
-import javafx.scene.shape.Rectangle;
-import javafx.util.Duration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BadGuy extends Sprite {
-
-    private static final Logger log = LoggerFactory.getLogger(BadGuy.class);
-
-    private Animation movement;
-    private ExitListener exitListener;
-
-    public BadGuy() {
-
-        Rectangle body = new Rectangle(0, 0, 40, 40);
-        body.setArcHeight(6);
-        body.setArcWidth(6);
-        body.setFill(Color.BLUE);
-        getChildren().add(body);
-
-        exitListener = new ExitListener();
-    }
-
-    @Override
-    public void setLevel(Level level) {
-
-        if (movement != null) {
-            movement.stop();
-        }
-        if (this.level != null) {
-            this.level.getExits().removeListener(exitListener);
-        }
-
-        super.setLevel(level);
-
-        if (level != null) {
-            findPathToNearestExit();
-            level.getExits().addListener(exitListener);
-        }
-    }
-
-    protected void findPathToNearestExit() {
-
-        if (movement != null) {
-            movement.stop();
-        }
-
-        // just use the first available exit for now - this whole path selection needs to get smarter
-        ObservableList<Point2D> exits = level.getExits();
-        Point2D exit = exits.get(0);
-        log.debug("BadGuy is now heading for exit at {}", exit);
-
-        Path path = new Path();
-        path.getElements().add(new MoveTo(0, 0));
-        CubicCurveTo curve = new CubicCurveTo();
-        curve.setControlX1(exit.getX() * Math.random());
-        curve.setControlY1(exit.getY() * Math.random());
-        curve.setX(exit.getX());
-        curve.setY(exit.getY());
-        path.getElements().add(curve);
-
-        PathTransition pathTransition = new PathTransition();
-        pathTransition.setDuration(Duration.millis((Math.random() * 20000) + 5000));
-        pathTransition.setPath(path);
-        pathTransition.setNode(BadGuy.this);
-        pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
-
-        pathTransition.setOnFinished(new EventHandler<ActionEvent>() {
-            @Override
-            public void handle(ActionEvent actionEvent) {
-                level.removeBadGuy(BadGuy.this);
-            }
-        });
-
-        movement = pathTransition;
-        movement.play();
-
-    }
-
-    //-------------------------------------------------------------------------
-
-    private class ExitListener implements ListChangeListener<Point2D> {
-
-        @Override
-        public void onChanged(Change<? extends Point2D> change) {
-            findPathToNearestExit();
-        }
-    }
-
-}

File defender/src/main/java/com/fxexperience/games/defender/DefaultLevelFactory.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;
-
-public class DefaultLevelFactory implements LevelFactory {
-
-    @Override
-    public Level createLevel() {
-        return new SimpleLevel();
-    }
-}

File defender/src/main/java/com/fxexperience/games/defender/DefenderApp.java

  */
 package com.fxexperience.games.defender;
 
-import javafx.animation.Interpolator;
-import javafx.animation.KeyFrame;
-import javafx.animation.KeyValue;
-import javafx.animation.Timeline;
+import com.fxexperience.games.defender.ui.DefenderMainView;
 import javafx.application.Application;
-import javafx.scene.Group;
 import javafx.scene.Scene;
-import javafx.scene.control.Label;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.paint.Color;
-import javafx.scene.shape.Rectangle;
 import javafx.stage.Stage;
-import javafx.util.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 
         log.info("Starting JavaFX Tower Defender");
 
-        LevelFactory levelFactory = new DefaultLevelFactory();
-        Level level = levelFactory.createLevel();
-        Scene scene = new Scene(level, 800, 600);
+        DefenderMainView mainView = new DefenderMainView();
+        Scene scene = new Scene(mainView, 1100, 700);
         scene.getStylesheets().add("/styles/styles.css");
 
         stage.setTitle("JavaFX Tower Defender");
         stage.show();
         log.info("JavaFX Tower Defender started successfully - ready for action");
 
-        level.start();
+        Game game = new Game(mainView);
+        game.showNewGameDialog();
     }
 }

File defender/src/main/java/com/fxexperience/games/defender/Difficulty.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;
+
+public enum Difficulty {
+
+    easy, medium, hard
+}

File defender/src/main/java/com/fxexperience/games/defender/Game.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;
+
+import com.fxexperience.games.defender.level.Level;
+import com.fxexperience.games.defender.level.SimpleLevel;
+import com.fxexperience.games.defender.ui.DefenderMainView;
+import com.fxexperience.games.defender.ui.GameView;
+import com.fxexperience.games.defender.ui.LevelCompleteView;
+import com.fxexperience.games.defender.ui.NewGameView;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Game {
+
+    private static final Logger log = LoggerFactory.getLogger(Game.class);
+
+    private final DefenderMainView defenderMainView;
+    private GameView gameView;
+    private ObjectProperty<Level> level;
+    private ObjectProperty<Difficulty> difficulty;
+    private BooleanProperty paused;
+    private int nextLevel = 1;
+    private LevelCompleteListener levelCompleteListener;
+    private NewGameView newGameView;
+
+    public Game(DefenderMainView defenderMainView) {
+
+        this.defenderMainView = defenderMainView;
+        this.level = new SimpleObjectProperty<Level>();
+        this.difficulty = new SimpleObjectProperty<Difficulty>();
+        this.paused = new SimpleBooleanProperty();
+
+        this.gameView = new GameView(this);
+
+        levelCompleteListener = new LevelCompleteListener();
+        level.addListener(new ChangeListener<Level>() {
+            @Override
+            public void changed(ObservableValue<? extends Level> source, Level oldLevel, Level newLevel) {
+                if (oldLevel != null) {
+                    oldLevel.completeProperty().removeListener(levelCompleteListener);
+                }
+                if (newLevel != null) {
+                    log.debug("Adding level complete listener");
+                    newLevel.completeProperty().addListener(levelCompleteListener);
+                }
+            }
+        });
+    }
+
+    public void showNewGameDialog() {
+        setPaused(true);
+        if (newGameView == null) {
+            newGameView = new NewGameView(this);
+        }
+        defenderMainView.showView(newGameView);
+    }
+
+    public void startNewGame(Difficulty difficulty) {
+        log.info("Starting new game with difficulty '{}'", difficulty);
+        setDifficulty(difficulty);
+        stopCurrentLevel();
+        nextLevel = 1;
+        nextLevel();
+    }
+
+    public void nextLevel() {
+        stopCurrentLevel();
+        Level nextLevel = createNextLevel();
+        log.info("Changing game level to: {}", nextLevel.getName());
+        this.level.set(nextLevel);
+        nextLevel.start();
+        setPaused(false);
+        defenderMainView.showView(gameView);
+    }
+
+    public BooleanProperty pausedProperty() {
+        return paused;
+    }
+
+    public void setPaused(boolean paused) {
+        this.paused.set(paused);
+    }
+
+    public boolean isPaused() {
+        return paused.get();
+    }
+
+    public ObjectProperty<Level> levelProperty() {
+        return this.level;
+    }
+
+    public Level getLevel() {
+        return level.get();
+    }
+
+    public ObjectProperty<Difficulty> difficultyProperty() {
+        return difficulty;
+    }
+
+    public Difficulty getDifficulty() {
+        return difficulty.get();
+    }
+
+    public void setDifficulty(Difficulty difficulty) {
+        this.difficulty.set(difficulty);
+    }
+
+    protected void stopCurrentLevel() {
+        Level currentLevel = levelProperty().get();
+        if (currentLevel != null) {
+            log.debug("Stopping current game level: " + level.getName());
+            currentLevel.stop();
+        }
+    }
+
+    protected Level createNextLevel() {
+        // todo can create weird and wonderful levels here - load from level file, etc
+        return new SimpleLevel(this, "Level " + nextLevel++, 5);
+    }
+
+    //---------------------------------------------------------------------------------------------
+
+    private final class LevelCompleteListener implements ChangeListener<Boolean> {
+
+        private LevelCompleteView levelCompleteView;
+
+        @Override
+        public void changed(ObservableValue<? extends Boolean> source, Boolean wasComplete, Boolean isComplete) {
+
+            if (isComplete) {
+                Level level = getLevel();
+                log.info("{} was completed!", level.getName());
+                if (levelCompleteView == null) {
+                    levelCompleteView = new LevelCompleteView(Game.this);
+                }
+                levelCompleteView.setLevel(level);
+                defenderMainView.showView(levelCompleteView);
+            }
+        }
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/Level.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;
-
-import javafx.application.Platform;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import javafx.concurrent.Task;
-import javafx.geometry.Point2D;
-import javafx.scene.Group;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public abstract class Level extends Group {
-
-    private static final Logger log = LoggerFactory.getLogger(Level.class);
-
-    private ObservableList<BadGuy> badGuys;
-    private ObservableList<Tower> towers;
-    private ObservableList<Point2D> exits;
-
-    public Level() {
-        badGuys = FXCollections.observableArrayList();
-        towers = FXCollections.observableArrayList();
-        exits = FXCollections.observableArrayList();
-    }
-
-    public abstract void start();
-
-    public void addBadGuy(BadGuy badGuy) {
-        badGuy.setLevel(this);
-        badGuys.add(badGuy);
-        // probably could do some clever binding instead of this...
-        getChildren().add(badGuy);
-        log.debug("BadGuy added to level, we now have {} BadGuys on the Level", badGuys.size());
-    }
-
-    public void removeBadGuy(BadGuy badGuy) {
-        badGuy.setLevel(null);
-        badGuys.remove(badGuy);
-        // probably could do some clever binding instead of this...
-        getChildren().remove(badGuy);
-        log.debug("BadGuy removed from level, we now have {} BadGuys on the Level", badGuys.size());
-    }
-
-
-    public void setExits(Point2D... exits) {
-        this.exits.setAll(exits);
-    }
-
-    public ObservableList<Point2D> getExits() {
-        return exits;
-    }
-
-}

File defender/src/main/java/com/fxexperience/games/defender/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;
-
-public interface LevelFactory {
-
-    Level createLevel();
-}

File defender/src/main/java/com/fxexperience/games/defender/SimpleLevel.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;
-
-import javafx.application.Platform;
-import javafx.geometry.Bounds;
-import javafx.geometry.Point2D;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Very simple level just for testing the basics. More interesting levels can be added later.
- */
-public class SimpleLevel extends Level {
-
-    private static final Logger log = LoggerFactory.getLogger(SimpleLevel.class);
-
-    public void start() {
-
-        log.info("Starting SimpleLevel, adding BadGuys at random intervals ");
-
-        // just hard code exit point for now - need to define a level size at some point
-        setExits(new Point2D(800, 500));
-
-        // loop and add BadGuys forever (normally would be for a set amount of BadGuys until level is finished)
-        Thread thread = new Thread() {
-            @Override
-            public void run() {
-
-                while (true) {
-
-                    // need to add the BadGuy in the JFX Thread
-                    Platform.runLater(new Runnable() {
-                        @Override
-                        public void run() {
-                            addBadGuy(new BadGuy());
-                        }
-                    });
-
-                    // create a new BadGuy at random intervals
-                    try {
-                        long delay = (long) (Math.random() * 5000) + 100;
-                        log.debug("Next BadGuy will be added in {} seconds", delay/1000);
-                        Thread.sleep(delay);
-                    } catch (InterruptedException e) {
-                        // shouldn't happen, and if it does we don't care
-                    }
-
-                }
-            }
-        };
-        thread.start();
-    }
-
-}

File defender/src/main/java/com/fxexperience/games/defender/Sprite.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;
-
-import javafx.scene.Group;
-
-public class Sprite extends Group {
-
-    protected Level level;
-
-    public void setLevel(Level level) {
-        this.level = level;
-    }
-
-    public Level getLevel() {
-        return level;
-    }
-}

File defender/src/main/java/com/fxexperience/games/defender/Tower.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;
-
-public class Tower extends Sprite {
-}

File defender/src/main/java/com/fxexperience/games/defender/level/Level.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.level;
+
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.sprite.BadGuy;
+import com.fxexperience.games.defender.sprite.Tower;
+import javafx.animation.AnimationTimer;
+import javafx.animation.ParallelTransition;
+import javafx.animation.PathTransition;
+import javafx.animation.Timeline;
+import javafx.beans.property.*;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.geometry.Point2D;
+import javafx.scene.Group;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class Level extends Group {
+
+    private static final Logger log = LoggerFactory.getLogger(Level.class);
+
+    private final Game game;
+    private ReadOnlyStringProperty name;
+    private BooleanProperty complete;
+    private ReadOnlyObjectProperty<Rectangle> levelBounds;
+    private ObservableList<BadGuy> badGuys;
+    private ObservableList<Tower> towers;
+
+    public Level(Game game, String name, int width, int height) {
+        this.game = game;
+
+        this.name = new ReadOnlyStringWrapper(name);
+        this.complete = new SimpleBooleanProperty();
+        this.badGuys = FXCollections.observableArrayList();
+        this.towers = FXCollections.observableArrayList();
+
+        Rectangle bounds = new Rectangle(0, 0, width, height);
+        setClip(bounds);
+        this.levelBounds = new ReadOnlyObjectWrapper<Rectangle>(bounds);
+    }
+
+    public Game getGame() {
+        return game;
+    }
+
+    public ReadOnlyStringProperty nameProperty() {
+        return name;
+    }
+
+    public String getName() {
+        return name.get();
+    }
+
+    public ReadOnlyObjectProperty<Rectangle> levelBounds() {
+        return levelBounds;
+    }
+
+    public Rectangle getLevelBounds() {
+        return levelBounds.get();
+    }
+
+    public void start() {
+
+    }
+
+    public void stop() {
+
+    }
+
+    public BooleanProperty completeProperty() {
+        return this.complete;
+    }
+
+    public boolean isComplete() {
+        return complete.get();
+    }
+
+    public void setComplete(boolean complete) {
+        this.complete.set(complete);
+    }
+
+    public void addBadGuy(BadGuy badGuy) {
+        badGuy.start(this);
+        badGuys.add(badGuy);
+        // probably could do some clever binding instead of this...
+        getChildren().add(badGuy);
+        log.debug("BadGuy added to level, we now have {} BadGuys on the Level", badGuys.size());
+    }
+
+    public ObservableList<BadGuy> getBadGuys() {
+        return badGuys;
+    }
+
+    public void removeBadGuy(BadGuy badGuy) {
+        badGuy.stop();
+        badGuys.remove(badGuy);
+        // probably could do some clever binding instead of this...
+        getChildren().remove(badGuy);
+        log.debug("BadGuy removed from level, we now have {} BadGuys on the Level", badGuys.size());
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/level/SimpleLevel.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.level;
+
+import com.fxexperience.games.defender.Difficulty;
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.sprite.BadGuy;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Platform;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Point2D;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.util.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Very simple level just for testing the basics. More interesting levels can be added later.
+ */
+public class SimpleLevel extends Level {
+
+    private static final Logger log = LoggerFactory.getLogger(SimpleLevel.class);
+
+    private int numBadGuys;
+    private Timeline timeline;
+
+    public SimpleLevel(Game game, String name, int numBadGuys) {
+
+        super(game, name, 800, 600);
+        this.numBadGuys = numBadGuys;
+        getStyleClass().add("simpleLevel");
+
+        // background
+
+        Rectangle levelBounds = getLevelBounds();
+        Rectangle background = new Rectangle();
+        background.widthProperty().bind(levelBounds.widthProperty());
+        background.heightProperty().bind(levelBounds.heightProperty());
+        background.setFill(Color.GHOSTWHITE);
+        background.setStroke(Color.DARKBLUE);
+        getChildren().add(background);
+    }
+
+    @Override
+    public void start() {
+
+        // bad guy generation
+
+        timeline = new Timeline();
+        timeline.setCycleCount(numBadGuys);
+        KeyFrame addBadGuy = new KeyFrame(Duration.millis(2000), new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent actionEvent) {
+                addBadGuy(new BadGuy(getGame(), new Point2D(-20, 40), new Point2D(800, 600), Math.max(0.3, Math.random())));
+            }
+        });
+        timeline.getKeyFrames().add(addBadGuy);
+        timeline.playFromStart();
+
+        // handle pauses
+
+        getGame().pausedProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> source, Boolean wasPaused, Boolean isPaused) {
+                if (isPaused) {
+                    timeline.pause();
+                } else {
+                    if (!isComplete()) {
+                        timeline.play();
+                    }
+                }
+            }
+        });
+
+        // detect when the level is finished
+
+        timeline.setOnFinished(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent actionEvent) {
+                // finished making BadGuys, now wait for the last of the guys on the screen to go
+
+                final ObservableList<BadGuy> badGuys = getBadGuys();
+                if (badGuys.size() == 0) {
+                    // already done
+                    setComplete(true);
+                } else {
+                    // wait till list is empty
+                    badGuys.addListener(new InvalidationListener() {
+                        @Override
+                        public void invalidated(Observable source) {
+                            if (badGuys.size() == 0) {
+                                badGuys.removeListener(this);
+                                setComplete(true);
+                            }
+                        }
+                    });
+                }
+            }
+        });
+    }
+
+    @Override
+    public void stop() {
+        timeline.stop();
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/sprite/BadGuy.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.sprite;
+
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.level.Level;
+import javafx.animation.Animation;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Point2D;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.util.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BadGuy extends Sprite {
+
+    private static final Logger log = LoggerFactory.getLogger(BadGuy.class);
+
+    private Point2D destination;
+    private List<Point2D> pathToDestination;
+    private Timeline timeline;
+    private double speed;
+
+    public BadGuy(Game game, Point2D start, Point2D destination, double speed) {
+
+        super(game);
+
+        setTranslateX(start.getX());
+        setTranslateY(start.getY());
+        this.destination = destination;
+        this.speed = speed;
+        this.pathToDestination = new ArrayList<Point2D>();
+
+        Rectangle body = new Rectangle(0, 0, 40, 40);
+        body.setArcHeight(6);
+        body.setArcWidth(6);
+        body.setFill(Color.BLUE);
+        getChildren().add(body);
+    }
+
+    public void start(Level level) {
+
+        super.start(level);
+
+        log.debug("Attaching BadGuy to level: {}", level.getName());
+
+        timeline = new Timeline();
+        timeline.setCycleCount(Animation.INDEFINITE);
+        timeline.setAutoReverse(true);
+        KeyFrame updateStep = new KeyFrame(Duration.millis(1), new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent actionEvent) {
+                update();
+            }
+        });
+        timeline.getKeyFrames().add(updateStep);
+
+
+        getGame().pausedProperty().addListener(new ChangeListener<Boolean>() {
+            @Override
+            public void changed(ObservableValue<? extends Boolean> source, Boolean wasPaused, Boolean isPaused) {
+                if (isPaused) {
+                    timeline.pause();
+                } else {
+                    timeline.play();
+                }
+            }
+        });
+
+
+        findPathToDestination();
+        timeline.play();
+    }
+
+    public void stop() {
+        if (getLevel() != null) {
+            timeline.stop();
+        }
+    }
+
+    protected void update() {
+
+        // todo turn to face the direction we want to go
+
+        // take a step in the right direction
+        if (pathToDestination.size() > 0) {
+
+            Point2D currentPoint = new Point2D(getTranslateX(), getTranslateY());
+            Point2D nextWayPoint = pathToDestination.get(0);
+            Point2D stepTo = movePointTowards(currentPoint, nextWayPoint, speed);
+            setTranslateX(stepTo.getX());
+            setTranslateY(stepTo.getY());
+
+            // if we reached the way point, go for the next one
+            if (Math.abs(nextWayPoint.getX() - stepTo.getX()) < 1 && Math.abs(nextWayPoint.getY() - stepTo.getY()) < 1) {
+                pathToDestination.remove(0);
+            }
+
+        } else {
+
+            // bad guy reached his destination - what a hero
+            log.debug("BadGuy reached target destination");
+            getLevel().removeBadGuy(this);
+        }
+    }
+
+    protected void findPathToDestination() {
+
+        log.debug("Finding path for BadGuy to reach destination at {}", destination);
+
+        pathToDestination.clear();
+
+        // we need to route some way-points based on the Level and the towers in our way but for now just add some
+        // random in between points to get us going.
+        pathToDestination.add(new Point2D(destination.getX() * Math.random(), destination.getY() * Math.random()));
+
+        pathToDestination.add(destination);
+    }
+
+    protected Point2D movePointTowards(Point2D current, Point2D destination, double distance) {
+
+        Point2D vector = new Point2D(destination.getX() - current.getX(), destination.getY() - current.getY());
+        double length = Math.sqrt(vector.getX() * vector.getX() + vector.getY() * vector.getY());
+        Point2D unitVector = new Point2D(vector.getX() / length, vector.getY() / length);
+        return new Point2D(current.getX() + unitVector.getX() * distance, current.getY() + unitVector.getY() * distance);
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/sprite/Sprite.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.sprite;
+
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.level.Level;
+import javafx.scene.Group;
+
+public class Sprite extends Group {
+
+    private final Game game;
+    private Level level;
+
+    public Sprite(Game game) {
+        this.game = game;
+    }
+
+    public void start(Level level) {
+        this.level = level;
+    }
+
+    public void stop() {
+    }
+
+    public Level getLevel() {
+        return level;
+    }
+
+    public Game getGame() {
+        return game;
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/sprite/Tower.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.sprite;
+
+import com.fxexperience.games.defender.Game;
+
+public class Tower extends Sprite {
+
+    public Tower(Game game) {
+
+        super(game);
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/ui/DefenderMainView.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.ui;
+
+import javafx.scene.Node;
+import javafx.scene.layout.BorderPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefenderMainView extends BorderPane {
+
+    private static final Logger log = LoggerFactory.getLogger(DefenderMainView.class);
+
+    public void showView(Node node) {
+        log.debug("Showing view: {}", node);
+        setCenter(node);
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/ui/GameToolBox.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.ui;
+
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.level.Level;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.VBox;
+
+import javax.swing.*;
+
+public class GameToolBox extends VBox {
+
+    private final Game game;
+    private Label levelLabel;
+
+    public GameToolBox(Game game) {
+        super(20);
+        this.game = game;
+        getStyleClass().add("gameToolBox");
+        buildView();
+    }
+
+    private void buildView() {
+
+        levelLabel = new Label();
+        this.game.levelProperty().addListener(new ChangeListener<Level>() {
+            @Override
+            public void changed(ObservableValue<? extends Level> source, Level oldLevel, Level newLevel) {
+                levelLabel.textProperty().unbind();
+                if (newLevel != null) {
+                    levelLabel.textProperty().bind(newLevel.nameProperty());
+                }
+            }
+        });
+
+        getChildren().add(levelLabel);
+        getChildren().add(buildTowerArea());
+        getChildren().add(buildOptionsArea());
+    }
+
+    private Pane buildTowerArea() {
+
+        VBox box = new VBox(5);
+        // todo add some buttons for selecting the available towers
+
+        box.getChildren().add(new Label("todo: tower support"));
+        ToggleButton tower1Button = new ToggleButton("Tower Type 1");
+        box.getChildren().add(tower1Button);
+        ToggleButton tower2Button = new ToggleButton("Tower Type 2");
+        box.getChildren().add(tower2Button);
+        ToggleButton tower3Button = new ToggleButton("Tower Type 3");
+        box.getChildren().add(tower3Button);
+
+        ToggleGroup toggleGroup = new ToggleGroup();
+        toggleGroup.getToggles().addAll(tower1Button, tower2Button, tower3Button);
+
+        return box;
+    }
+
+    private Pane buildOptionsArea() {
+
+        VBox box = new VBox(5);
+
+        Button newGameButton = new Button("New Game");
+        newGameButton.setMaxSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        VBox.setVgrow(newGameButton, Priority.ALWAYS);
+        newGameButton.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent actionEvent) {
+                game.showNewGameDialog();
+            }
+        });
+        box.getChildren().add(newGameButton);
+
+        ToggleButton pauseButton = new ToggleButton("Pause");
+        pauseButton.setMaxSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        VBox.setVgrow(pauseButton, Priority.ALWAYS);
+        pauseButton.selectedProperty().bindBidirectional(game.pausedProperty());
+        box.getChildren().add(pauseButton);
+
+        return box;
+    }}

File defender/src/main/java/com/fxexperience/games/defender/ui/GameView.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.ui;
+
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.level.Level;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.Group;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.StackPane;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GameView extends BorderPane {
+
+    private static final Logger log = LoggerFactory.getLogger(GameView.class);
+
+    private final Game game;
+    private Group levelLayer;
+
+    public GameView(Game game) {
+        this.game = game;
+        getStyleClass().add("gameView");
+        buildView();
+    }
+
+    private void buildView() {
+
+        levelLayer = new Group();
+        game.levelProperty().addListener(new ChangeListener<Level>() {
+            @Override
+            public void changed(ObservableValue<? extends Level> source, Level oldLevel, Level newLevel) {
+                levelLayer.getChildren().clear();
+                if (newLevel != null) {
+                    levelLayer.getChildren().add(newLevel);
+                }
+            }
+        });
+        setCenter(levelLayer);
+
+        GameToolBox gameToolBox = new GameToolBox(game);
+        setRight(gameToolBox);
+    }
+
+}

File defender/src/main/java/com/fxexperience/games/defender/ui/LevelCompleteView.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.ui;
+
+import com.fxexperience.games.defender.Game;
+import com.fxexperience.games.defender.level.Level;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.VBox;
+
+public class LevelCompleteView extends VBox {
+
+    private final Game game;
+    private Label levelLabel;
+
+    public LevelCompleteView(Game game) {
+        this.game = game;
+        getStyleClass().add("levelCompleteView");
+        buildView();
+    }
+
+    public void setLevel(Level level) {
+        if (level != null) {
+            levelLabel.setText("Congratulations, you survived " + level.getName() + "!");
+        } else {
+            levelLabel.setText(null);
+        }
+    }
+    private void buildView() {
+        getStyleClass().add("levelCompleteView");
+        setFillWidth(false);
+        setAlignment(Pos.TOP_CENTER);
+        getChildren().add(buildDialogArea());
+    }
+
+    private Pane buildDialogArea() {
+
+        VBox box = new VBox(20);
+        box.setAlignment(Pos.CENTER);
+        box.getStyleClass().add("dialog");
+
+        levelLabel = new Label();
+        box.getChildren().add(levelLabel);
+
+        Button nextLevelButton = new Button("Next Level");
+        nextLevelButton.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent actionEvent) {
+                game.nextLevel();
+            }
+        });
+        box.getChildren().add(nextLevelButton);
+
+        return box;
+    }
+}

File defender/src/main/java/com/fxexperience/games/defender/ui/NewGameView.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.ui;
+
+import com.fxexperience.games.defender.Difficulty;
+import com.fxexperience.games.defender.Game;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.VBox;
+
+public class NewGameView extends VBox {
+
+    private final Game game;
+
+    public NewGameView(Game game) {
+        this.game = game;
+        buildView();
+    }
+
+    private void buildView() {
+        setSpacing(50);
+        getStyleClass().add("newGameView");
+        setFillWidth(false);
+        setAlignment(Pos.TOP_CENTER);
+
+        Label titleLabel = new Label("JavaFX Tower Defender");
+        titleLabel.getStyleClass().add("titleLabel");
+        getChildren().add(titleLabel);
+
+        getChildren().add(buildDialogArea());
+    }
+
+    private Pane buildDialogArea() {
+        VBox box = new VBox(20);
+        box.getStyleClass().add("dialog");
+        Label label = new Label("Choose difficulty");
+        box.getChildren().add(label);
+        box.getChildren().add(buildDifficultyArea());
+        return box;
+    }
+
+    private Pane buildDifficultyArea() {
+
+        VBox box = new VBox(5);
+        box.getChildren().add(createDifficultyButton("Easy", Difficulty.easy));
+        box.getChildren().add(createDifficultyButton("Medium", Difficulty.medium));
+        box.getChildren().add(createDifficultyButton("Hard", Difficulty.hard));
+        return box;
+    }
+
+    private Button createDifficultyButton(String label, final Difficulty difficulty) {
+        Button button = new Button(label);
+        button.setMaxSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        VBox.setVgrow(button, Priority.ALWAYS);
+        button.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent actionEvent) {
+                game.startNewGame(difficulty);
+                setVisible(false);
+            }
+        });
+        return button;
+    }
+}

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

  * 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.
  */
+
+.gameToolBox {
+    -fx-padding: 10;
+    -fx-border-color: gray;
+    -fx-background-color: #ffffdd;
+}
+
+.newGameView,
+.levelCompleteView {
+    -fx-padding: 60;
+    -fx-background-color: white;
+}
+
+.titleLabel {
+    -fx-text-fill: rgba(0,0,0,0.7);
+    -fx-font: 32pt Arial;
+}
+
+.dialog {
+    -fx-padding: 10 50;
+    -fx-border-color: gray;
+    -fx-background-color:linear-gradient(white,#DDDDDD);
+    -fx-effect: dropshadow(two-pass-box , darkgray, 10, 0.0 , 4, 5);
+}