Commits

Anonymous committed d275604

Added TowerFactory cursor so you can see what you're placing

Comments (0)

Files changed (6)

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

 import javafx.event.EventHandler;
 import javafx.fxml.FXMLLoader;
 import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
     private BooleanProperty paused;
     private ObjectProperty<Level> level;
     private ObjectProperty<TowerFactory> selectedTowerFactory;
+    private Circle towerFactoryCursor;
 
     private LevelCompleteHandler levelCompleteHandler;
+    private LevelMouseMoveHandler levelMouseMoveHandler;
+    private LevelMouseExitHandler levelMouseExitHandler;
     private LevelMouseClickHandler levelMouseClickHandler;
 
     public Game(DefenderMainView defenderMainView) {
         this.gameView = new GameView(this);
 
         levelCompleteHandler = new LevelCompleteHandler();
+        levelMouseMoveHandler = new LevelMouseMoveHandler();
+        levelMouseExitHandler = new LevelMouseExitHandler();
         levelMouseClickHandler = new LevelMouseClickHandler();
 
         level.addListener(new ChangeListener<Level>() {
             public void changed(ObservableValue<? extends Level> source, Level oldLevel, Level newLevel) {
                 if (oldLevel != null) {
                     oldLevel.completeProperty().removeListener(levelCompleteHandler);
+                    oldLevel.setOnMouseMoved(null);
+                    oldLevel.setOnMouseExited(null);
                     oldLevel.setOnMouseClicked(null);
                 }
                 if (newLevel != null) {
                     log.debug("Adding level complete listener");
                     newLevel.completeProperty().addListener(levelCompleteHandler);
+                    newLevel.setOnMouseMoved(levelMouseMoveHandler);
+                    newLevel.setOnMouseExited(levelMouseExitHandler);
                     newLevel.setOnMouseClicked(levelMouseClickHandler);
                 }
             }
                 }
             }
         });
+
+        selectedTowerFactory.addListener(new ChangeListener<TowerFactory>() {
+            @Override
+            public void changed(ObservableValue<? extends TowerFactory> source, TowerFactory oldTowerFactory, final TowerFactory newTowerFactory) {
+                if (newTowerFactory != null) {
+                    towerFactoryCursor = new Circle(newTowerFactory.getTowerRadius());
+                    towerFactoryCursor.setVisible(false);
+                    gameView.setCursorNode(towerFactoryCursor);
+                } else {
+                    gameView.setCursorNode(null);
+                }
+            }
+        });
     }
 
     public Player getPlayer() {
 
     //---------------------------------------------------------------------------------------------
 
+    private static final Color noGoColor = new Color(1, 0, 0, 0.5);
+    private static final Color allGoodColor = new Color(0, 1, 0, 0.5);
+
+    private final class LevelMouseMoveHandler implements EventHandler<MouseEvent> {
+
+        @Override
+        public void handle(MouseEvent event) {
+            Level level = getLevel();
+            TowerFactory towerFactory = getSelectedTowerFactory();
+            if (level != null && towerFactory != null && towerFactoryCursor != null) {
+                double x = event.getX();
+                double y = event.getY();
+                towerFactoryCursor.setTranslateX(x);
+                towerFactoryCursor.setTranslateY(y);
+                boolean towerSpaceAvailable = level.isTowerSpaceAvailable(x, y, towerFactory.getTowerRadius());
+                towerFactoryCursor.setFill(towerSpaceAvailable ? allGoodColor : noGoColor);
+                towerFactoryCursor.setVisible(true);
+            }
+        }
+    }
+
+    //---------------------------------------------------------------------------------------------
+
+    private final class LevelMouseExitHandler implements EventHandler<MouseEvent> {
+
+        @Override
+        public void handle(MouseEvent event) {
+            if (towerFactoryCursor != null) {
+                towerFactoryCursor.setVisible(false);
+            }
+        }
+    }
+
+    //---------------------------------------------------------------------------------------------
+
     private final class LevelMouseClickHandler implements EventHandler<MouseEvent> {
 
         @Override
             Level currentLevel = level.get();
             TowerFactory towerFactory = selectedTowerFactory.get();
             if (currentLevel != null && towerFactory != null) {
-                log.info("Adding new tower '{}' at ({},{}) ",
-                        new Object[]{towerFactory.getTowerName(), event.getX(), event.getY()});
+                log.debug("Attempting to add new tower '{}' at (x={},y={},r={}) ",
+                        new Object[]{towerFactory.getTowerName(), event.getX(), event.getY(), towerFactory.getTowerRadius()});
 
                 if (player.getCredits() >= towerFactory.getCreditCost()) {
-                    player.deductCredits(towerFactory.getCreditCost());
-                    Tower tower = towerFactory.createTower();
-                    if (currentLevel.canPlaceTowerAt(tower, event.getX(), event.getY())) {
+                    if (currentLevel.isTowerSpaceAvailable(event.getX(), event.getY(), towerFactory.getTowerRadius())) {
+                        log.info("Adding tower '{}' at (x={},y={},r={}) ",
+                                new Object[]{towerFactory.getTowerName(), event.getX(), event.getY(), towerFactory.getTowerRadius()});
+                        Tower tower = towerFactory.createTower();
+                        player.deductCredits(towerFactory.getCreditCost());
                         tower.setTranslateX(event.getX());
                         tower.setTranslateY(event.getY());
                         currentLevel.getTowers().add(tower);

defender/src/main/java/com/fxexperience/games/defender/level/Level.java

         return matches;
     }
 
-    public boolean canPlaceTowerAt(Tower tower, double x, double y)  {
+    public boolean isTowerSpaceAvailable(double x, double y, double radius)  {
 
-        // this is a very crude algorithm - need to do proper shape intersection
         for (Shape allowedTowerArea : allowedTowerAreas) {
-            if (allowedTowerArea.contains(x, y)) {
-                Bounds box = allowedTowerArea.getBoundsInParent();
-                double towerRadius = tower.getRadius();
-                if (box.getMinX() < x - towerRadius && box.getMaxX() > x + towerRadius
-                        && box.getMinY() < y - towerRadius && box.getMaxY() > y + towerRadius) {
-                    return true;
+            if (isCircleFullyInShape(allowedTowerArea, x, y, radius)) {
+                // check the space is not already taken by a tower
+                for (Tower nextTower : towers) {
+                    if (isCircleIntersection(x, y, radius, nextTower.getTranslateX(), nextTower.getTranslateY(), nextTower.getRadius())) {
+                        log.debug("Tower spot is already occupied by {}", nextTower);
+                        return false;
+                    }
                 }
+                return true;
             }
         }
         return false;
     }
+
+    protected boolean isCircleFullyInShape(Shape shape, double x, double y, double radius) {
+
+        // this is a very crude algorithm - need to do proper shape intersection
+        if (shape.contains(x, y)) {
+            Bounds box = shape.getBoundsInParent();
+            if (box.getMinX() < x - radius && box.getMaxX() > x + radius
+                    && box.getMinY() < y - radius && box.getMaxY() > y + radius) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isCircleIntersection(double x1, double y1, double r1, double x2, double y2, double r2) {
+        double dist = r1 + r1;
+        return Math.abs(x2 - x1) <= dist && Math.abs(y2 - y1) <= dist;
+    }
 }

defender/src/main/java/com/fxexperience/games/defender/tower/TowerFactory.java

     public Tower createTower() {
         Tower tower = new Tower(getTowerName(), towerRadius, range, shotDelay, damage);
 
-        // hard code the body for now
+        // hard code the body for now - should load this from a Towers file/class or something
         Circle body = new Circle(towerRadius);
         body.setFill(towerRadius < 20 ? Color.BURLYWOOD : Color.DARKORANGE);
+        body.setStroke(Color.DARKBLUE);
         tower.getChildren().add(body);
 
         return tower;

defender/src/main/java/com/fxexperience/games/defender/ui/GameView.java

 import com.fxexperience.games.defender.level.Level;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
+import javafx.geometry.Bounds;
 import javafx.scene.Group;
-import javafx.scene.layout.BorderPane;
+import javafx.scene.Node;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.StackPane;
+import javafx.scene.shape.Rectangle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class GameView extends BorderPane {
+public class GameView extends StackPane {
 
     private static final Logger log = LoggerFactory.getLogger(GameView.class);
 
     private final Game game;
     private Group levelLayer;
+    private Pane cursorLayer;
+    private AnchorPane hudLayer;
 
     public GameView(Game game) {
         this.game = game;
         buildView();
     }
 
+    public void setCursorNode(final Node cursorNode) {
+
+        cursorLayer.getChildren().clear();
+        if (cursorNode != null) {
+            cursorLayer.getChildren().add(cursorNode);
+        }
+    }
+
     private void buildView() {
 
         levelLayer = new Group();
                 }
             }
         });
-        setCenter(levelLayer);
+        getChildren().add(levelLayer);
 
+        cursorLayer = new Pane();
+        levelLayer.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
+            @Override
+            public void changed(ObservableValue<? extends Bounds> source, Bounds oldBounds, Bounds newBounds) {
+                cursorLayer.setClip(new Rectangle(0, 0, newBounds.getWidth(), newBounds.getHeight()));
+                cursorLayer.setTranslateX(newBounds.getMinX());
+                cursorLayer.setTranslateY(newBounds.getMinY());
+                cursorLayer.setPrefWidth(newBounds.getWidth());
+                cursorLayer.setPrefHeight(newBounds.getHeight());
+            }
+        });
+        cursorLayer.setMouseTransparent(true);
+        getChildren().add(cursorLayer);
+
+        hudLayer = new AnchorPane();
+        hudLayer.setPickOnBounds(false);
         GameToolBox gameToolBox = new GameToolBox(game);
-        setRight(gameToolBox);
+        AnchorPane.setRightAnchor(gameToolBox, 10.0);
+        AnchorPane.setTopAnchor(gameToolBox, 10.0);
+        hudLayer.getChildren().add(gameToolBox);
+        getChildren().add(hudLayer);
     }
 
 }

defender/src/main/resources/fxml/level/level-1.fxml

         <BadGuy path="$path1">
             <Rectangle width="20" height="20" fill="RED" arcWidth="6" arcHeight="6"/>
         </BadGuy>
+        <BadGuy initialDelay="1s" path="$path1">
+            <Rectangle width="20" height="20" fill="RED" arcWidth="6" arcHeight="6"/>
+        </BadGuy>
+        <BadGuy initialDelay="1500ms" path="$path2">
+            <Rectangle width="20" height="20" fill="BLUE" arcWidth="6" arcHeight="6"/>
+        </BadGuy>
+        <BadGuy initialDelay="2s" path="$path1">
+            <Rectangle width="20" height="20" fill="RED" arcWidth="6" arcHeight="6"/>
+        </BadGuy>
+        <BadGuy initialDelay="3s" path="$path1">
+            <Rectangle width="20" height="20" fill="RED" arcWidth="6" arcHeight="6"/>
+        </BadGuy>
         <BadGuy initialDelay="5s" path="$path2">
             <Rectangle width="20" height="20" fill="BLUE" arcWidth="6" arcHeight="6"/>
         </BadGuy>
+        <BadGuy initialDelay="6s" path="$path1">
+            <Rectangle width="20" height="20" fill="RED" arcWidth="6" arcHeight="6"/>
+        </BadGuy>
         <BadGuy initialDelay="10s" path="$path1">
             <Rectangle width="20" height="20" fill="GREEN" arcWidth="6" arcHeight="6"/>
         </BadGuy>

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

  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+.gameView {
+    -fx-background-color: #333333;
+}
+
 .gameToolBox {
     -fx-padding: 10;
     -fx-border-color: gray;