/* * The MIT License (MIT) * * FXGL - JavaFX Game Library * * Copyright (c) 2015-2017 AlmasB (almaslvl@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.almasb.fxglgames.geowars; import com.almasb.fxgl.app.ApplicationMode; import com.almasb.fxgl.app.GameApplication; import com.almasb.fxgl.ecs.Entity; import com.almasb.fxgl.entity.Entities; import com.almasb.fxgl.entity.EntityView; import com.almasb.fxgl.entity.GameEntity; import com.almasb.fxgl.input.UserAction; import com.almasb.fxgl.physics.CollisionHandler; import com.almasb.fxgl.physics.PhysicsWorld; import com.almasb.fxgl.service.Input; import com.almasb.fxgl.settings.GameSettings; import com.almasb.fxgl.ui.WheelMenu; import com.almasb.fxglgames.geowars.component.GraphicsComponent; import com.almasb.fxglgames.geowars.component.HPComponent; import com.almasb.fxglgames.geowars.component.OldPositionComponent; import com.almasb.fxglgames.geowars.control.GraphicsUpdateControl; import com.almasb.fxglgames.geowars.control.PlayerControl; import com.almasb.fxglgames.geowars.grid.Grid; import javafx.animation.FadeTransition; import javafx.animation.TranslateTransition; import javafx.geometry.Point2D; import javafx.scene.canvas.Canvas; import javafx.scene.input.KeyCode; import javafx.scene.input.MouseButton; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import javafx.util.Duration; import java.util.Map; /** * @author Almas Baimagambetov (AlmasB) (almaslvl@gmail.com) */ public class GeoWarsApp extends GameApplication { private GameEntity player; private PlayerControl playerControl; private Grid grid; public GameEntity getPlayer() { return player; } public Grid getGrid() { return grid; } @Override protected void initSettings(GameSettings settings) { settings.setWidth(1280); settings.setHeight(720); settings.setTitle("FXGL Geometry Wars"); settings.setVersion("0.7"); settings.setFullScreen(false); settings.setIntroEnabled(false); settings.setMenuEnabled(false); settings.setProfilingEnabled(true); settings.setCloseConfirmation(false); settings.setApplicationMode(ApplicationMode.DEVELOPER); } @Override protected void initAssets() { // preload explosion sprite sheet getAssetLoader().loadTexture("explosion.png", 80 * 48, 80); } @Override protected void initInput() { Input input = getInput(); input.addAction(new UserAction("Move Left") { @Override protected void onAction() { playerControl.left(); } }, KeyCode.A); input.addAction(new UserAction("Move Right") { @Override protected void onAction() { playerControl.right(); } }, KeyCode.D); input.addAction(new UserAction("Move Up") { @Override protected void onAction() { playerControl.up(); } }, KeyCode.W); input.addAction(new UserAction("Move Down") { @Override protected void onAction() { playerControl.down(); } }, KeyCode.S); input.addAction(new UserAction("Shoot") { @Override protected void onAction() { playerControl.shoot(input.getMousePositionWorld()); } }, MouseButton.PRIMARY); input.addAction(new UserAction("Weapon Menu") { @Override protected void onActionBegin() { openWeaponMenu(); } }, MouseButton.SECONDARY); } @Override protected void initGameVars(Map<String, Object> vars) { vars.put("score", 0); vars.put("multiplier", 1); vars.put("kills", 0); vars.put("time", 180); vars.put("weaponType", WeaponType.NORMAL); } @Override protected void initGame() { getAudioPlayer().setGlobalSoundVolume(0.2); getAudioPlayer().setGlobalMusicVolume(0.1); initBackground(); player = (GameEntity) getGameWorld().spawn("Player"); playerControl = player.getControlUnsafe(PlayerControl.class); getMasterTimer().runAtInterval(() -> getGameWorld().spawn("Wanderer"), Duration.seconds(1)); getMasterTimer().runAtInterval(() -> getGameWorld().spawn("Seeker"), Duration.seconds(2)); getMasterTimer().runAtInterval(() -> getGameWorld().spawn("Crystal", getRandomPoint()), Duration.seconds(4)); getMasterTimer().runAtInterval(() -> getGameState().increment("time", -1), Duration.seconds(1)); getAudioPlayer().playMusic("bgm.mp3"); } @Override protected void initPhysics() { PhysicsWorld physics = getPhysicsWorld(); CollisionHandler bulletEnemy = new CollisionHandler(GeoWarsType.BULLET, GeoWarsType.WANDERER) { @Override protected void onCollisionBegin(Entity bullet, Entity enemy) { bullet.removeFromWorld(); HPComponent hp = enemy.getComponentUnsafe(HPComponent.class); hp.setValue(hp.getValue() - 1); if (hp.getValue() == 0) { getGameWorld().spawn("Explosion", Entities.getBBox(enemy).getCenterWorld()); enemy.removeFromWorld(); addScoreKill(); } } }; physics.addCollisionHandler(bulletEnemy); physics.addCollisionHandler(bulletEnemy.copyFor(GeoWarsType.BULLET, GeoWarsType.SEEKER)); CollisionHandler playerEnemy = new CollisionHandler(GeoWarsType.PLAYER, GeoWarsType.WANDERER) { @Override protected void onCollisionBegin(Entity a, Entity b) { Entities.getPosition(a).setValue(getRandomPoint()); b.removeFromWorld(); deductScoreDeath(); } }; physics.addCollisionHandler(playerEnemy); physics.addCollisionHandler(playerEnemy.copyFor(GeoWarsType.PLAYER, GeoWarsType.SEEKER)); // TODO: add shockwave skill // CollisionHandler shockEnemy = new CollisionHandler(GeoWarsType.SHOCKWAVE, GeoWarsType.SEEKER) { // @Override // protected void onCollisionBegin(Entity a, Entity b) { // PositionComponent pos = Entities.getPosition(b); // // pos.translate(pos.getValue() // .subtract(Entities.getPosition(player).getValue()) // .normalize() // .multiply(100)); // } // }; // // physics.addCollisionHandler(shockEnemy); // physics.addCollisionHandler(shockEnemy.copyFor(GeoWarsType.SHOCKWAVE, GeoWarsType.WANDERER)); } private WheelMenu weaponMenu; @Override protected void initUI() { Text scoreText = getUIFactory().newText("", Color.WHITE, 18); scoreText.setTranslateX(1100); scoreText.setTranslateY(70); scoreText.textProperty().bind(getGameState().intProperty("score").asString()); Text timerText = getUIFactory().newText("", Color.WHITE, 18); timerText.layoutBoundsProperty().addListener((o, old, bounds) -> { timerText.setTranslateX(getWidth() / 2 - bounds.getWidth() / 2); }); timerText.setTranslateX(getWidth() / 2); timerText.setTranslateY(60); timerText.textProperty().bind(getGameState().intProperty("time").asString()); Circle timerCircle = new Circle(40, 40, 40, null); timerCircle.setStrokeWidth(2); timerCircle.setStroke(Color.AQUA); timerCircle.setTranslateX(getWidth() / 2 - 40); timerCircle.setTranslateY(60 - 40 - 5); WeaponType[] weaponTypes = WeaponType.values(); weaponMenu = new WheelMenu( weaponTypes[0].toString(), weaponTypes[1].toString(), weaponTypes[2].toString(), weaponTypes[3].toString() ); weaponMenu.setSelectionHandler(typeName -> { WeaponType type = WeaponType.valueOf(typeName); getGameState().setValue("weaponType", type); getAudioPlayer().playSound(typeName.toLowerCase() + ".wav"); }); getGameScene().addUINodes(scoreText, timerText, timerCircle, weaponMenu); weaponMenu.close(); Text beware = getUIFactory().newText("Beware! Seekers get smarter every time!", Color.AQUA, 24); beware.setTranslateX(getWidth() / 2 - beware.getLayoutBounds().getWidth() / 2); beware.setTranslateY(getHeight() / 2); beware.setOpacity(0); getGameScene().addUINode(beware); FadeTransition ft = new FadeTransition(Duration.seconds(2), beware); ft.setCycleCount(2); ft.setAutoReverse(true); ft.setFromValue(0); ft.setToValue(1); ft.setOnFinished(e -> getGameScene().removeUINode(beware)); ft.play(); } @Override protected void onUpdate(double tpf) { player.getComponentUnsafe(OldPositionComponent.class) .setValue(Entities.getPosition(player).getValue()); grid.update(); } @Override protected void onPostUpdate(double tpf) { if (getGameState().getInt("time") == 0) { getDisplay().showConfirmationBox("Demo Over. Press anything to exit. Your score: " + getGameState().getInt("score"), yes -> { exit(); }); } } private void initBackground() { EntityView backgroundView = new EntityView(getAssetLoader().loadTexture("background.png", getWidth(), getHeight())); getGameScene().addGameView(backgroundView); Canvas canvas = new Canvas(getWidth(), getHeight()); canvas.getGraphicsContext2D().setStroke(new Color(0.118, 0.118, 0.545, 1)); Entities.builder() .viewFromNode(canvas) .with(new GraphicsComponent(canvas.getGraphicsContext2D())) .with(new GraphicsUpdateControl()) .buildAndAttach(getGameWorld()); Rectangle size = new Rectangle(0, 0, getWidth(), getHeight()); Point2D spacing = new Point2D(38.8, 40); grid = new Grid(size, spacing, getGameWorld(), canvas.getGraphicsContext2D()); } private void openWeaponMenu() { weaponMenu.setTranslateX(getInput().getMouseXWorld() - 20); weaponMenu.setTranslateY(getInput().getMouseYWorld() - 70); weaponMenu.open(); } private Point2D getRandomPoint() { return new Point2D(Math.random() * getWidth(), Math.random() * getHeight()); } private void addScoreKill() { getGameState().increment("kills", 1); if (getGameState().getInt("kills") == 15) { getGameState().setValue("kills", 0); getGameState().increment("multiplier", +1); } final int multiplier = getGameState().getInt("multiplier"); Text bonusText = getUIFactory().newText("+100" + (multiplier == 1 ? "" : "x" + multiplier), Color.WHITE, 18); bonusText.setTranslateX(1100); bonusText.setTranslateY(0); getGameScene().addUINode(bonusText); TranslateTransition tt = new TranslateTransition(Duration.seconds(0.5), bonusText); tt.setToY(70); tt.setOnFinished(e -> { getGameState().increment("score", +100*multiplier); getGameScene().removeUINode(bonusText); }); tt.play(); } private void deductScoreDeath() { getGameState().increment("score", -1000); getGameState().setValue("kills", 0); getGameState().setValue("multiplier", 1); Text bonusText = getUIFactory().newText("-1000", Color.WHITE, 18); bonusText.setTranslateX(1100); bonusText.setTranslateY(70); getGameScene().addUINode(bonusText); TranslateTransition tt = new TranslateTransition(Duration.seconds(0.5), bonusText); tt.setToY(0); tt.setOnFinished(e -> { getGameScene().removeUINode(bonusText); }); tt.play(); } public static void main(String[] args) { launch(args); } }