diff --git a/build.gradle b/build.gradle index 3c2909a4..c4c14252 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ tasks.withType(JavaCompile) { application { mainModule = 'io.rpg' - mainClass = 'io.rpg.HelloApplication' + mainClass = 'io.rpg.Main' } javafx { diff --git a/configurations/config-1/locations/location-1/location-1.json b/configurations/config-1/locations/location-1/location-1.json index 2b451703..5e9bfa04 100644 --- a/configurations/config-1/locations/location-1/location-1.json +++ b/configurations/config-1/locations/location-1/location-1.json @@ -3,5 +3,6 @@ "objects": [ { "tag": "object-1", "position": { "row": 0, "col": 5 }, "type": "collectible" }, { "tag": "object-2", "position": { "row": 1, "col": 3 }, "type": "interactive" } - ] -} \ No newline at end of file + ], + "backgroundPath": "file:assets/map.png" +} diff --git a/configurations/config-1/locations/location-2/location-2.json b/configurations/config-1/locations/location-2/location-2.json index 7153494b..dc949d25 100644 --- a/configurations/config-1/locations/location-2/location-2.json +++ b/configurations/config-1/locations/location-2/location-2.json @@ -1,7 +1,8 @@ { "tag": "location-2", "objects": [ - { "tag": "object-1", "position": { "row": 0, "col": 0 } }, - { "tag": "object-3", "position": { "row": 0, "col": 1 } } - ] + { "tag": "object-1", "position": { "row": 0, "col": 0 }, "type": "collectible" }, + { "tag": "object-3", "position": { "row": 0, "col": 1 }, "type": "navigable" } + ], + "backgroundPath": "file:assets/map.png" } \ No newline at end of file diff --git a/configurations/config-1/locations/location-2/objects/object-1.json b/configurations/config-1/locations/location-2/objects/object-1.json index e69de29b..6f2ed3ae 100644 --- a/configurations/config-1/locations/location-2/objects/object-1.json +++ b/configurations/config-1/locations/location-2/objects/object-1.json @@ -0,0 +1,5 @@ +{ + "tag": "object-1", + "assetPath": "/path/to/the/asset", + "type": "" +} \ No newline at end of file diff --git a/configurations/config-1/locations/location-2/objects/object-3.json b/configurations/config-1/locations/location-2/objects/object-3.json index e69de29b..791023ce 100644 --- a/configurations/config-1/locations/location-2/objects/object-3.json +++ b/configurations/config-1/locations/location-2/objects/object-3.json @@ -0,0 +1,5 @@ +{ + "tag": "object-3", + "assetPath": "/path/to/the/asset", + "type": "" +} \ No newline at end of file diff --git a/configurations/config-1/root.json b/configurations/config-1/root.json index 087555ca..2f174211 100644 --- a/configurations/config-1/root.json +++ b/configurations/config-1/root.json @@ -1,7 +1,8 @@ { "tag": "GameByConfig1", - "locations": [ + "locationTags": [ "location-1", "location-2" - ] + ], + "rootLocation": "location-1" } \ No newline at end of file diff --git a/configurations/unit-test-configuration/locations/location-1/location-1.json b/configurations/unit-test-configuration/locations/location-1/location-1.json index 33029c78..5e9bfa04 100644 --- a/configurations/unit-test-configuration/locations/location-1/location-1.json +++ b/configurations/unit-test-configuration/locations/location-1/location-1.json @@ -1,7 +1,8 @@ { "tag": "location-1", "objects": [ - { "tag": "object-1", "position": { "row": 0, "col": 5 } }, - { "tag": "object-2", "position": { "row": 1, "col": 3 } } - ] -} \ No newline at end of file + { "tag": "object-1", "position": { "row": 0, "col": 5 }, "type": "collectible" }, + { "tag": "object-2", "position": { "row": 1, "col": 3 }, "type": "interactive" } + ], + "backgroundPath": "file:assets/map.png" +} diff --git a/configurations/unit-test-configuration/locations/location-1/objects/object-1.json b/configurations/unit-test-configuration/locations/location-1/objects/object-1.json index e69de29b..6f2ed3ae 100644 --- a/configurations/unit-test-configuration/locations/location-1/objects/object-1.json +++ b/configurations/unit-test-configuration/locations/location-1/objects/object-1.json @@ -0,0 +1,5 @@ +{ + "tag": "object-1", + "assetPath": "/path/to/the/asset", + "type": "" +} \ No newline at end of file diff --git a/configurations/unit-test-configuration/locations/location-2/location-2.json b/configurations/unit-test-configuration/locations/location-2/location-2.json index 7153494b..dc949d25 100644 --- a/configurations/unit-test-configuration/locations/location-2/location-2.json +++ b/configurations/unit-test-configuration/locations/location-2/location-2.json @@ -1,7 +1,8 @@ { "tag": "location-2", "objects": [ - { "tag": "object-1", "position": { "row": 0, "col": 0 } }, - { "tag": "object-3", "position": { "row": 0, "col": 1 } } - ] + { "tag": "object-1", "position": { "row": 0, "col": 0 }, "type": "collectible" }, + { "tag": "object-3", "position": { "row": 0, "col": 1 }, "type": "navigable" } + ], + "backgroundPath": "file:assets/map.png" } \ No newline at end of file diff --git a/configurations/unit-test-configuration/locations/location-2/objects/object-1.json b/configurations/unit-test-configuration/locations/location-2/objects/object-1.json index e69de29b..6f2ed3ae 100644 --- a/configurations/unit-test-configuration/locations/location-2/objects/object-1.json +++ b/configurations/unit-test-configuration/locations/location-2/objects/object-1.json @@ -0,0 +1,5 @@ +{ + "tag": "object-1", + "assetPath": "/path/to/the/asset", + "type": "" +} \ No newline at end of file diff --git a/configurations/unit-test-configuration/locations/location-2/objects/object-2.json b/configurations/unit-test-configuration/locations/location-2/objects/object-2.json deleted file mode 100644 index e69de29b..00000000 diff --git a/configurations/unit-test-configuration/locations/location-2/objects/object-3.json b/configurations/unit-test-configuration/locations/location-2/objects/object-3.json new file mode 100644 index 00000000..791023ce --- /dev/null +++ b/configurations/unit-test-configuration/locations/location-2/objects/object-3.json @@ -0,0 +1,5 @@ +{ + "tag": "object-3", + "assetPath": "/path/to/the/asset", + "type": "" +} \ No newline at end of file diff --git a/configurations/unit-test-configuration/root.json b/configurations/unit-test-configuration/root.json index 7353bec9..ff5b72e3 100644 --- a/configurations/unit-test-configuration/root.json +++ b/configurations/unit-test-configuration/root.json @@ -1,7 +1,8 @@ { "tag": "test-tag", - "locations": [ + "locationTags": [ "location-1", "location-2" - ] + ], + "rootLocation": "location-1" } \ No newline at end of file diff --git a/src/main/java/io/rpg/Game.java b/src/main/java/io/rpg/Game.java new file mode 100644 index 00000000..ecd9f532 --- /dev/null +++ b/src/main/java/io/rpg/Game.java @@ -0,0 +1,44 @@ +package io.rpg; + +import io.rpg.controller.Controller; +import javafx.scene.Scene; +import org.jetbrains.annotations.NotNull; + +public class Game { + private Controller controller; + + private Game() { + + } + + public void setLocationControllerForLocationTag(String tag) { + // tutaj przyda sie hashmapa: nazwa lokacji -> kontroller + } + + public Scene getWorldView() { + return controller.getView(); + } + + public void setController(Controller controller) { + this.controller = controller; + } + + public static class Builder { + private final Game game; + + public Builder() { + game = new Game(); + } + + public Game build() { + assert game.controller != null : "Attempt to build Game object without controller"; + return game; + } + + public Builder setController(Controller controller) { + assert controller != null : "Attempt to set null controller"; + game.setController(controller); + return this; + } + } +} diff --git a/src/main/java/io/rpg/HelloApplication.java b/src/main/java/io/rpg/HelloApplication.java deleted file mode 100644 index 4499fbe6..00000000 --- a/src/main/java/io/rpg/HelloApplication.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.rpg; - -import io.rpg.config.ConfigLoader; -import io.rpg.gui.DisplayLayer; -import io.rpg.model.GameObjectStandIn; -import io.rpg.gui.LocationController; -import io.rpg.gui.model.LocationModel; -import io.rpg.model.*; -import io.rpg.model.GameObject; -import javafx.animation.AnimationTimer; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.image.Image; -import javafx.stage.Stage; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.config.Configurator; -import javafx.util.Pair; - -import java.io.IOException; -import java.net.URL; - -public class HelloApplication extends Application { - @Override - public void start(Stage stage) throws IOException { - Image someMap = new Image("file:assets/map.png"); - Image someMap10x10 = new Image("file:assets/map10x10.png"); - Image someDude1 = new Image("file:assets/someDude.png"); - Image someDude2 = new Image("file:assets/someDudeButGreen.png"); - Image player = new Image("file:assets/stone.png"); - Game game=new Game(); - DisplayLayer displayLayer = new DisplayLayer(stage); - LocationModel locationModel=displayLayer.showLocation(); - LocationController locationController=displayLayer.getLocationController(); - locationController.setGame(game); - try{ - game.addGameObject(new GameObject(new Vector(0,0), someDude1)); - game.addGameObject(new GameObject(new Vector(0,5), someDude2)); - game.addGameObject(new GameObject(new Vector(5,5), someDude2)); - game.addGameObject(new Player(new Vector(7,7), player)); -// locationModel=displayLayer.showLocation(); - locationModel.setBackgroundImage(someMap); - locationModel.setGame(game); - }catch(Exception e){ - e.printStackTrace(); - } - AnimationTimer animationTimer=new AnimationTimer() { - long lastUpdate=-1; - @Override - public void handle(long now) { - if(lastUpdate!=-1){ - float difference=(now-lastUpdate)/1e6f; - game.update(difference); - locationModel.update(difference); - } - lastUpdate=now; - } - - }; - - animationTimer.start(); - } -} diff --git a/src/main/java/io/rpg/HelloController.java b/src/main/java/io/rpg/HelloController.java deleted file mode 100644 index f927ff20..00000000 --- a/src/main/java/io/rpg/HelloController.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.rpg; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; - -public class HelloController { - @FXML - private Label welcomeText; - - @FXML - protected void onHelloButtonClick() { - welcomeText.setText("Welcome to JavaFX Application!"); - } -} \ No newline at end of file diff --git a/src/main/java/io/rpg/Initializer.java b/src/main/java/io/rpg/Initializer.java new file mode 100644 index 00000000..965c2ea9 --- /dev/null +++ b/src/main/java/io/rpg/Initializer.java @@ -0,0 +1,106 @@ +package io.rpg; + +import io.rpg.config.ConfigLoader; +import io.rpg.controller.Controller; +import io.rpg.config.model.GameWorldConfig; +import io.rpg.config.model.LocationConfig; +import io.rpg.model.location.LocationModel; +import io.rpg.model.object.GameObject; +import io.rpg.config.model.GameObjectConfig; +import io.rpg.util.Result; +import io.rpg.view.LocationView; +import javafx.stage.Stage; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; + +public class Initializer { + private Path pathToConfigDir; + private ConfigLoader configLoader; + private Stage mainStage; + + private final Logger logger; + + public Initializer(@NotNull String pathToConfigDir, Stage mainStage) { + this.configLoader = new ConfigLoader(pathToConfigDir); + this.mainStage = mainStage; + this.logger = LogManager.getLogger(Initializer.class); + } + + public Result initialize() { + Result gameWorldConfigLoadResult = configLoader.load(); + + if (gameWorldConfigLoadResult.isError()) { + gameWorldConfigLoadResult.getErrorValueOpt().ifPresentOrElse( + ex -> logger.error(ex.getMessage()), + () -> logger.error("Unknown error returned from config loader") + ); + return Result.error(gameWorldConfigLoadResult.getErrorValue()); + } else if (gameWorldConfigLoadResult.isOkValueNull()) { + logger.error("ConfigLoader fetched null GameWorldConfig"); + return Result.error(new RuntimeException("ConfigLoader fetched null GameWorldConfig")); + } + + GameWorldConfig worldConfig = gameWorldConfigLoadResult.getOkValue(); + + Controller.Builder controllerBuilder = new Controller.Builder(); + + assert worldConfig.getLocationConfigs() != null; + assert worldConfig.getLocationConfigs().size() > 0 : "There must be at least one location config specified"; + + for (LocationConfig locationConfig : worldConfig.getLocationConfigs()) { + LocationModel model = loadLocationModelFromConfig(locationConfig); + LocationView view = loadLocationViewFromConfig(locationConfig); + + assert view != null; + + if (locationConfig.getTag().equals(worldConfig.getRootLocation())) { + controllerBuilder + .setModel(model) + .setView(view); + } + + model.addOnLocationModelStateChangeObserver(view); + + controllerBuilder + .addViewForTag(locationConfig.getTag(), view) + .addModelForTag(locationConfig.getTag(), model); + } + + Game.Builder gameBuilder = new Game.Builder(); + gameBuilder.setController(controllerBuilder.build()); + + return Result.ok(gameBuilder.build()); + } + + @Nullable + public static LocationView loadLocationViewFromConfig(LocationConfig config) { + try { + return LocationView.fromConfig(config); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static LocationModel loadLocationModelFromConfig(LocationConfig config) { + List gameObjectConfigs = config.getObjects(); + List gameObjects = new LinkedList<>(); + + for (GameObjectConfig goconfig : gameObjectConfigs) { + gameObjects.add(GameObject.fromConfig(goconfig)); + } + + return new LocationModel( + config.getTag(), + gameObjects + ); + } +} diff --git a/src/main/java/io/rpg/Main.java b/src/main/java/io/rpg/Main.java new file mode 100644 index 00000000..d41e5106 --- /dev/null +++ b/src/main/java/io/rpg/Main.java @@ -0,0 +1,48 @@ +package io.rpg; + +import io.rpg.util.Result; +import javafx.application.Application; +import javafx.stage.Stage; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; + +import java.io.IOException; + +public class Main extends Application { + @Override + public void start(Stage stage) throws IOException { + Configurator.setRootLevel(Level.DEBUG); + Logger logger = LogManager.getLogger(Main.class); + + Initializer worldInitializer = new Initializer("configurations/config-1", stage); + Result initializationResult = worldInitializer.initialize(); + + if (initializationResult.isError()) { + logger.error("Initialization error"); + + initializationResult.getErrorValueOpt().ifPresentOrElse( + ex -> { + logger.error(ex.getMessage()); + ex.printStackTrace(); + }, + () -> logger.error("No reason provided") + ); + return; + } else if (initializationResult.isOkValueNull()) { + logger.error("Initialization returned null value"); + return; + } + + Game game = initializationResult.getOkValue(); + + stage.setScene(game.getWorldView()); + + stage.show(); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/src/main/java/io/rpg/config/ConfigLoader.java b/src/main/java/io/rpg/config/ConfigLoader.java index a00de6fa..2ee4bce1 100644 --- a/src/main/java/io/rpg/config/ConfigLoader.java +++ b/src/main/java/io/rpg/config/ConfigLoader.java @@ -1,14 +1,12 @@ package io.rpg.config; import com.google.gson.Gson; - -import io.rpg.model.GameWorldConfig; -import io.rpg.model.location.LocationConfig; - -import io.rpg.model.object.GameObjectConfig; +import io.rpg.config.model.GameObjectConfig; +import io.rpg.config.model.GameWorldConfig; +import io.rpg.config.model.LocationConfig; +import io.rpg.util.Result; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.jetbrains.annotations.NotNull; import java.io.BufferedReader; @@ -17,27 +15,47 @@ import java.nio.file.Files; import java.nio.file.Path; +/** + * This class exposes methods to load user specified configuration into + * configuration objects: + * {@link GameObjectConfig}, + * {@link GameWorldConfig}, + * {@link LocationConfig}. + */ public class ConfigLoader { @NotNull private final Gson gson; + /** + * Path to the directory that contains configuration - + * root.json file & locations directory + */ @NotNull private final Path pathToConfigDir; + /** + * This filed is initialized basing on {@link io.rpg.config.ConfigLoader#pathToConfigDir}. + * It represents path to the root.json file which is entry point for {@link ConfigLoader} + */ @NotNull private final Path pathToRootFile; + /** + * Similarly to {@link io.rpg.config.ConfigLoader#pathToRootFile} this field is initialized basing on + * {@link io.rpg.config.ConfigLoader#pathToConfigDir}. + * It represents path to the directory that holds locations configuration files. + */ @NotNull private final Path pathToLocationsDir; - public static final String ERR_INVALID_CFG_DIR_PATH = "Could not resolve config directory." + - " Make sure that the config dir path is correct"; + public static final String ERR_INVALID_CFG_DIR_PATH = "Could not resolve config directory." + + " Make sure that the config dir path is correct"; - public static final String ERR_ROOT_FNF = ConfigConstants.ROOT + - " file was not found inside config directory. Make sure that the file exists and is named properly"; + public static final String ERR_ROOT_FNF = ConfigConstants.ROOT + + " file was not found inside config directory. Make sure that the file exists and is named properly"; - public static final String ERR_LOCATIONS_DIR_FNF = ConfigConstants.LOCATIONS_DIR + - " directory was not found inside configuration directory"; + public static final String ERR_LOCATIONS_DIR_FNF = ConfigConstants.LOCATIONS_DIR + + " directory was not found inside configuration directory"; public static final String ERR_LOCATION_DIR_FNF_FOR_TAG = "Directory was not found for location with tag: "; @@ -48,6 +66,11 @@ public class ConfigLoader { @NotNull private final Logger logger; + /** + * Creates {@link ConfigLoader} for configuration under configDirPath. + * + * @param configDirPath Path to the root directory. + */ public ConfigLoader(@NotNull String configDirPath) { logger = LogManager.getLogger(ConfigLoader.class); @@ -61,30 +84,42 @@ public ConfigLoader(@NotNull String configDirPath) { validate(); } - public void load() { - logger.info("Load"); - - GameWorldConfig config; - try { - config = loadGameWorldConfig(); + /** + * Loads {@link io.rpg.config.model.GameWorldConfig} from the configuration files specified + * by the user. + * + * @return Valid {@link io.rpg.config.model.GameWorldConfig} or exception. + */ + public Result load() { + logger.info("Loading GameWorldConfig"); + + Result configLoadResult = loadGameWorldConfig(); + + if (configLoadResult.isError()) { + logger.error("Error while loading GameWorldConfig"); + configLoadResult.getErrorValueOpt().ifPresent(ex -> logger.error(ex.getMessage())); + return configLoadResult; + } else if (configLoadResult.getOkValue() == null) { + return Result.error(new RuntimeException("loadGameWorldConfig returned null config")); + } - logger.info("GameWorldConfig loaded"); - logger.info(config.toString()); + GameWorldConfig gameWorldConfig = configLoadResult.getOkValue(); - } catch (FileNotFoundException e) { - throw new RuntimeException(ERR_ROOT_FNF); - } + logger.info("GameWorldConfig loaded"); + logger.info(gameWorldConfig.toString()); - assert config.getLocations().size() > 0 : "Configuration must specify locations"; + // we assume here that gameWorldConfig was validated in loadGameWorldConfig method - for (String locationTag : config.getLocations()) { + for (String locationTag : gameWorldConfig.getLocationTags()) { try { logger.info("Loading location config for tag: " + locationTag); LocationConfig locationConfig = loadLocationConfig(locationTag); // todo: this should be called in loadLocationConfig method? - // locationConfig.validate(); + locationConfig.validate(); + + gameWorldConfig.addLocationConfig(locationConfig); logger.info("Location config loaded for tag: " + locationTag); logger.info(locationConfig.toString()); @@ -99,12 +134,12 @@ public void load() { } catch (Exception ex) { String exceptionMessage = ex.getMessage(); - logger.warn("Validation for game object config with tag: " + - gameObjectConfig.getTag() + " failed." + - (exceptionMessage != null ? "Reason: " + exceptionMessage : "No reason provided")); + logger.warn("Validation for game object config with tag: " + + gameObjectConfig.getTag() + " failed." + + (exceptionMessage != null ? "Reason: " + exceptionMessage : "No reason provided")); } -// Path GameObjectConfig + // TODO: @kkafar: Load objects inside objects directory } @@ -113,18 +148,37 @@ public void load() { e.printStackTrace(); } } + + Result validationResult = gameWorldConfig.validate(); + if (validationResult.isError()) { + return Result.error(validationResult.getErrorValue()); + } + + return Result.ok(validationResult.getOkValue()); } @NotNull - GameWorldConfig loadGameWorldConfig() throws FileNotFoundException { + Result loadGameWorldConfig() { logger.info("Loading game world config"); - BufferedReader reader = new BufferedReader(new FileReader(pathToRootFile.toString())); - GameWorldConfig config = gson.fromJson(reader, GameWorldConfig.class); - // todo: validate input -// config.validate(); + BufferedReader reader; - return config; + try { + reader = new BufferedReader(new FileReader(pathToRootFile.toString())); + } catch (FileNotFoundException exception) { + return Result.error(exception); + } + + // after loading a object from JSON we should always call the validate method + GameWorldConfig gameWorldConfigShell = gson.fromJson(reader, GameWorldConfig.class); + + System.out.println(gameWorldConfigShell.toString()); + + // GameWorldConfig is loaded in two stages right now + // todo: fix this! Separate initial GameWorldConfig to different class + Result configLoadResult = gameWorldConfigShell.validateStageOne(); + + return configLoadResult; } LocationConfig loadLocationConfig(@NotNull String locationTag) throws FileNotFoundException { @@ -150,7 +204,7 @@ LocationConfig loadLocationConfig(@NotNull String locationTag) throws FileNotFou return config; } - public void validate() { + private void validate() { if (!Files.isDirectory(pathToConfigDir)) { logger.error(ERR_INVALID_CFG_DIR_PATH); throw new IllegalArgumentException(ERR_INVALID_CFG_DIR_PATH); diff --git a/src/main/java/io/rpg/config/model/GameObjectConfig.java b/src/main/java/io/rpg/config/model/GameObjectConfig.java new file mode 100644 index 00000000..7175de06 --- /dev/null +++ b/src/main/java/io/rpg/config/model/GameObjectConfig.java @@ -0,0 +1,37 @@ +package io.rpg.config.model; + +import io.rpg.model.data.Position; +import io.rpg.model.object.GameObject; +import io.rpg.model.object.GameObjects; +import io.rpg.util.Result; +import org.jetbrains.annotations.NotNull; + +/** + * Represents {@link io.rpg.model.object.GameObject} configuration provided by user + * in configuration files. + */ +public class GameObjectConfig extends GameObject { + + + private String type; + + public GameObjectConfig(@NotNull String tag, @NotNull Position position) { + super(tag, position); + } + + public String getType() { + return type; + } + + /** + * Allows for validation of the object's state. + * + * @return Object in valid state or exception. + */ + public Result validate() { + if (!GameObjects.isValidType(type)) { + return Result.error(new IllegalStateException("Invalid object type: " + type)); + } + return Result.ok(this); + } +} diff --git a/src/main/java/io/rpg/config/model/GameWorldConfig.java b/src/main/java/io/rpg/config/model/GameWorldConfig.java new file mode 100644 index 00000000..ede8ed45 --- /dev/null +++ b/src/main/java/io/rpg/config/model/GameWorldConfig.java @@ -0,0 +1,121 @@ +package io.rpg.config.model; + +import io.rpg.util.Result; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class in not meant to be instantiated by hand. It is used by {@link io.rpg.config.ConfigLoader} + * via Gson. + */ +public class GameWorldConfig { + /** + * Unique tag for the game. This can be treated as name of the game. + */ + private String tag; + + /** + * List that contains all the names of locations specified by user. + * This filed is inflated from information in root.json file. + */ + private ArrayList locationTags; + + /** + * Contains inflated {@link io.rpg.config.model.LocationConfig}s of all the + * locations specified by user. + */ + private ArrayList locationConfigs; + + private GameWorldConfig() { + locationTags = new ArrayList<>(); + locationConfigs = new ArrayList<>(); + } + + /** + * Describes tag of the root location. (The location that is displayed first) + */ + private String rootLocation; + + /** + * Unique tag for the game. This can be treated as name of the game. + * + * @return String representing name of the game. + */ + public String getTag() { + return tag; + } + + /** + * @return List containing names of locations. + */ + public List getLocationTags() { + return locationTags; + } + + /** + * @return inflated {@link io.rpg.config.model.LocationConfig}s of all the + * locations specified by user. + */ + public ArrayList getLocationConfigs() { + return locationConfigs; + } + + /** + * @return Tag of the root location. + */ + public String getRootLocation() { + return rootLocation; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("\n{\n").append("\ttag: ").append(tag).append('\n'); + locationTags.forEach(location -> { + builder.append("\tlocation-tag: ").append(location).append('\n'); + }); + return builder.append("}\n").toString(); + } + + public void addLocationConfig(LocationConfig locationConfig) { + locationConfigs.add(locationConfig); + } + + /** + * As {@link GameWorldConfig} is loaded in two stages right now: + * 1. first data from root.json is loaded first + * 2. then location configs are added + * validation after step 1 is also required. + * + * @return Current configuration state or exception. + */ + public Result validateStageOne() { + if (locationTags.size() < 1) { + return Result.error(new IllegalStateException("No location tags detected")); + } else if (tag == null) { + return Result.error(new IllegalStateException("Null tag")); + } else { + return Result.ok(this); + } + } + + /** + * Allows for validation of the object's state. + * + * @return Object with valid state or exception. + */ + public Result validate() { + if (locationTags.size() < 1) { + return Result.error(new IllegalStateException("No location tags detected")); + } else if (locationConfigs.size() < 1) { + return Result.error(new IllegalStateException("No location configs loaded")); + } else if (tag == null) { + return Result.error(new IllegalStateException("Null tag")); + } else if (rootLocation == null) { + return Result.error(new IllegalStateException("No root location set!")); + } else { + return Result.ok(this); + } + } +} diff --git a/src/main/java/io/rpg/model/location/LocationConfig.java b/src/main/java/io/rpg/config/model/LocationConfig.java similarity index 80% rename from src/main/java/io/rpg/model/location/LocationConfig.java rename to src/main/java/io/rpg/config/model/LocationConfig.java index b7d8043a..68e32dda 100644 --- a/src/main/java/io/rpg/model/location/LocationConfig.java +++ b/src/main/java/io/rpg/config/model/LocationConfig.java @@ -1,7 +1,6 @@ -package io.rpg.model.location; +package io.rpg.config.model; -import io.rpg.model.object.GameObjectConfig; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,6 +18,9 @@ public class LocationConfig { @Nullable private Path path; + @Nullable + private String backgroundPath; + // This class is not meant to be instantiated // by hand. Only Gson should be able to do so private LocationConfig() { @@ -30,6 +32,11 @@ public String getTag() { return tag; } + @Nullable + public String getBackgroundPath() { + return backgroundPath; + } + @NotNull public List getObjects() { return objects; @@ -43,4 +50,8 @@ public Path getPath() { public void setPath(@NotNull Path path) { this.path = path; } + + public void validate() { + + } } diff --git a/src/main/java/io/rpg/controller/.gitkeep b/src/main/java/io/rpg/controller/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/io/rpg/controller/Controller.java b/src/main/java/io/rpg/controller/Controller.java new file mode 100644 index 00000000..96200780 --- /dev/null +++ b/src/main/java/io/rpg/controller/Controller.java @@ -0,0 +1,159 @@ +package io.rpg.controller; + +import io.rpg.model.data.KeyboardEvent; +import io.rpg.model.data.MouseClickedEvent; +import io.rpg.model.location.LocationModel; +import io.rpg.util.Result; +import io.rpg.view.GameObjectView; +import io.rpg.view.LocationView; +import javafx.scene.Scene; +import javafx.scene.input.KeyEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashMap; + +public class Controller implements KeyboardEvent.Observer, MouseClickedEvent.Observer { + private Scene currentView; + private LinkedHashMap tagToLocationModelMap; + private LocationModel currentModel; + private LinkedHashMap tagToLocationViewMap; + private Logger logger; + + + public Controller() { + logger = LogManager.getLogger(Controller.class); + + tagToLocationModelMap = new LinkedHashMap<>(); + tagToLocationViewMap = new LinkedHashMap<>(); + } + + public Controller(LinkedHashMap tagToLocationModelMap, + LinkedHashMap tagToLocationViewMap, + String rootTag) { + logger = LogManager.getLogger(Controller.class); + + this.tagToLocationViewMap = tagToLocationViewMap; + this.tagToLocationModelMap = tagToLocationModelMap; + + // TODO: handle errors + this.currentModel = this.tagToLocationModelMap.get(rootTag); + this.currentView = this.tagToLocationViewMap.get(rootTag); + } + + public void setModel(@NotNull LocationModel model) { + this.tagToLocationModelMap.put(model.getTag(), model); + currentModel = model; + } + + public void setView(Scene currentView) { + this.currentView = currentView; + } + + public Scene getView() { + return currentView; + } + + public void setTagToLocationModelMap(LinkedHashMap tagToLocationModelMap) { + this.tagToLocationModelMap = tagToLocationModelMap; + } + + public void setTagToLocationViewMap(LinkedHashMap tagToLocationViewMap) { + this.tagToLocationViewMap = tagToLocationViewMap; + } + + public LinkedHashMap getTagToLocationModelMap() { + return tagToLocationModelMap; + } + + public LinkedHashMap getTagToLocationViewMap() { + return tagToLocationViewMap; + } + + public Result validate() { + if (tagToLocationModelMap.size() == 0) + return Result.error(new Exception("Empty tag to location model map!")); + else if (tagToLocationViewMap.size() == 0) + return Result.error(new Exception("Empty tag to location view map!")); + else if (tagToLocationViewMap.size() != tagToLocationModelMap.size()) + return Result.error(new Exception("Mismatched sizes of maps!")); + else if (currentView == null) + return Result.error(new Exception("No current view set!")); + else if (currentModel == null) + return Result.error(new Exception("No current model set!")); + else + return Result.ok(this); + } + + @Override + public void onKeyboardEvent(KeyboardEvent event) { + // TODO: implement event handling + logger.info("Controller notified on key pressed from " + event.source()); + } + + @Override + public void onMouseClickedEvent(MouseClickedEvent event) { + // TODO: implement event handling + logger.info("Controller notified on click from " + event.source()); + } + + public static class Builder { + private final Controller controller; + private boolean isViewSet = false; + private boolean isModelSet = false; + + public Builder() { + controller = new Controller(); + } + + public Builder setTagToLocationModelMap(LinkedHashMap tagToLocationModelMap) { + controller.setTagToLocationModelMap(tagToLocationModelMap); + return this; + } + + public Builder setTagToLocationViewMap(LinkedHashMap tagToLocationViewMap) { + controller.setTagToLocationViewMap(tagToLocationViewMap); + return this; + } + + public Builder setModel(@NotNull LocationModel model) { + if (!isModelSet) { + isModelSet = true; + controller.setModel(model); + return this; + } else { + throw new IllegalStateException("Attempt to set model for the second time!"); + } + } + + public Builder setView(Scene currentView) { + if (!isViewSet) { + isViewSet = true; + controller.setView(currentView); + return this; + } else { + throw new IllegalStateException("Attempt to set view for the second time!"); + } + } + + public Controller build() { + Result validationResult = controller.validate(); + if (validationResult.isError()) { + throw new IllegalStateException(validationResult.getErrorValue()); + } + return controller; + } + + public Builder addViewForTag(String tag, LocationView view) { + controller.getTagToLocationViewMap().put(tag, view); + view.addKeyboardEventObserver(controller); + return this; + } + + public Builder addModelForTag(String tag, LocationModel model) { + controller.getTagToLocationModelMap().put(tag, model); + return this; + } + } +} diff --git a/src/main/java/io/rpg/gui/DisplayLayer.java b/src/main/java/io/rpg/gui/DisplayLayer.java deleted file mode 100644 index c7187cb1..00000000 --- a/src/main/java/io/rpg/gui/DisplayLayer.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.rpg.gui; - -import io.rpg.gui.model.LocationModel; -import javafx.fxml.Initializable; -import javafx.scene.Scene; -import javafx.stage.Stage; - -import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; - -public class DisplayLayer { - private final Stage mainStage; - private final LocationController locationController; - - public DisplayLayer(Stage mainStage) throws IOException { - this.mainStage = mainStage; - mainStage.show(); - locationController = LocationController.load(); - } - - - public LocationModel showLocation(){ - // maybe initialize the scene only once - mainStage.setScene(locationController.getScene()); - return locationController.getModel().clear(); - } - - public LocationController getLocationController(){ - return locationController; - } -} diff --git a/src/main/java/io/rpg/gui/LocationController.java b/src/main/java/io/rpg/gui/LocationController.java deleted file mode 100644 index f9fe0ed6..00000000 --- a/src/main/java/io/rpg/gui/LocationController.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.rpg.gui; - -import io.rpg.gui.model.LocationModel; -import io.rpg.gui.popups.PointsEarnedPopup; -import io.rpg.model.Game; -import io.rpg.model.Vector; -import javafx.event.Event; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Scene; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.stage.Stage; - -import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; - -public class LocationController implements Initializable { - private final static URL FXML_URL = LocationController.class.getResource("location-view.fxml"); - - @FXML private ImageView mapImageView; - @FXML private Pane foregroundPane, contentPane; - @FXML private HBox parent; - - private LocationModel model; - private Scene scene; - private Game game; - - private final PointsEarnedPopup pointsPopup = new PointsEarnedPopup(); - - public static LocationController load() throws IOException { - FXMLLoader loader = new FXMLLoader(FXML_URL); - loader.load(); - return loader.getController(); - } - - public LocationController() { - - } - - @Override - public void initialize(URL location, ResourceBundle resources) { - mapImageView.imageProperty().addListener((property, oldImg, newImg) -> { - contentPane.setPrefWidth(newImg.getWidth()); - contentPane.setPrefHeight(newImg.getHeight()); - - foregroundPane.setPrefWidth(newImg.getWidth()); - foregroundPane.setPrefHeight(newImg.getHeight()); - - mapImageView.setFitWidth(newImg.getWidth()); - mapImageView.setFitHeight(newImg.getHeight()); - - }); - - model = new LocationModel(mapImageView.imageProperty(), foregroundPane.getChildren(), this); - - scene = new Scene(parent); - scene.addEventFilter(KeyEvent.KEY_TYPED, this::onKeyTyped); - scene.addEventFilter(KeyEvent.KEY_PRESSED, this::onKeyPressed); - scene.addEventFilter(KeyEvent.KEY_RELEASED, this::onKeyReleased); - } - - public void onKeyPressed(KeyEvent event){ - switch(event.getCode()){ - case W: -// game.getPlayer().setDirection(new Vector(0,-1)); - game.getPlayer().setUpPressed(true); - break; - case S: -// game.getPlayer().setDirection(new Vector(0,1)); - game.getPlayer().setDownPressed(true); - break; - case A: -// game.getPlayer().setDirection(new Vector(-1,0)); - game.getPlayer().setLeftPressed(true); - break; - case D: -// game.getPlayer().setDirection(new Vector(1,0)); - game.getPlayer().setRightPressed(true); - break; - } - - } - - public void onKeyReleased(KeyEvent event){ - event.getCharacter(); -// System.out.println(); - switch(event.getCode()){ - case W: -// game.getPlayer().setDirection(new Vector(0,-1)); - game.getPlayer().setUpPressed(false); - break; - case S: -// game.getPlayer().setDirection(new Vector(0,1)); - game.getPlayer().setDownPressed(false); - break; - case A: -// game.getPlayer().setDirection(new Vector(-1,0)); - game.getPlayer().setLeftPressed(false); - break; - case D: -// game.getPlayer().setDirection(new Vector(1,0)); - game.getPlayer().setRightPressed(false); - break; - } - - } - - public void onKeyTyped(KeyEvent event) { - // TODO: 01.04.2022 Implement key actions - System.out.println(event); - String c=event.getCharacter(); -// System.out.println(); - switch(c){ - case "f": - Stage popup = pointsPopup.getPopup(5, scene); - popup.show(); - break; - } - } - - public void setGame(Game game){ - this.game=game; - } - public LocationModel getModel(){ - return model; - } - - public Scene getScene() { - return scene; - } -} diff --git a/src/main/java/io/rpg/gui/model/LocationModel.java b/src/main/java/io/rpg/gui/model/LocationModel.java deleted file mode 100644 index aa0f05b5..00000000 --- a/src/main/java/io/rpg/gui/model/LocationModel.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.rpg.gui.model; - -import io.rpg.gui.LocationController; -import io.rpg.model.GameObjectStandIn; -import io.rpg.model.*; -import io.rpg.model.GameObject; -import javafx.beans.property.ObjectProperty; -import javafx.collections.ObservableList; -import javafx.scene.Node; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.util.Pair; - -import java.util.HashMap; - -public class LocationModel { - static final int SCALE = 32; - private Game game; - private final ObjectProperty background; - // TODO: 01.04.2022 Think about a better name -// objects which we store on the playground -// mapNodes views to be displayed - private final ObservableList mapNodes; -// private - -// private HashMap gameObject2NodeMap; - private final LocationController controller; - - public LocationModel(ObjectProperty background, ObservableList mapNodes, LocationController controller) { - this.background = background; - this.mapNodes = mapNodes; -// this.gameObject2NodeMap = new HashMap<>(); - this.controller = controller; - } - - public LocationModel clear() { -// this.gameObject2NodeMap = new HashMap<>(); - mapNodes.clear(); - return this; - } - - public LocationModel setBackgroundImage(Image backgroundImage){ - background.setValue(backgroundImage); - return this; - } - - public LocationModel addMapObject(GameObject gameObject){ - ImageView imageView = new ImageView(gameObject.getImage()); -// gameObject - Vector mapPosition = getMapPosition(gameObject.getPosition()); -// Pair mapPosition = getMapPosition(gameObject.getPosition()); - imageView.setX(mapPosition.x); - imageView.setY(mapPosition.y); -// gameObject2NodeMap.put(gameObject, imageView); - gameObject.setImageView(imageView); - mapNodes.add(imageView); - imageView.setOnMouseClicked((e) -> onGameObjectAction(gameObject)); - return this; - } - - private void onGameObjectAction(GameObject source){ - // TODO: 01.04.2022 What to do when some GameObject was clicked - System.out.println(source); - } - - // TODO: 01.04.2022 Replace with Position - private Vector getMapPosition(Vector position){ - // size of one tile - return new Vector(position.x * SCALE, position.y * SCALE); -// return new Pair<>(position.x * scale, position.y * scale); - } - - public LocationModel setGame(Game game) { - this.game = game; - for(int i=0;i backgroundDims = controller.setBackgroundImage("file:assets/point-popup-bg.png"); - - // create popup stage - Stage popupStage = new Stage(StageStyle.TRANSPARENT); - Window window = scene.getWindow(); - popupStage.initOwner(window); - - // add and center popupScene on popup stage - popupStage.setScene(popupScene); - popupStage.setX(window.getX() + window.getWidth()/2 - backgroundDims.getKey()/2); - popupStage.setY(window.getY() + window.getHeight()/2 - backgroundDims.getValue()/2); - - // close popup after clicking aside - popupStage.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> { - if (!isNowFocused) { - popupStage.close(); - } - }); - - return popupStage; - } -} diff --git a/src/main/java/io/rpg/gui/popups/PointsPopupController.java b/src/main/java/io/rpg/gui/popups/PointsPopupController.java deleted file mode 100644 index a5d60352..00000000 --- a/src/main/java/io/rpg/gui/popups/PointsPopupController.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.rpg.gui.popups; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.layout.*; -import javafx.util.Pair; - -public class PointsPopupController { - - @FXML private Label label; - @FXML private Pane background; - - protected void setPointsCount(int pointsCount) { - label.setText("Earned " + pointsCount + " points!"); - } - - protected Pair setBackgroundImage(String url){ - BackgroundImage backgroundImg= new BackgroundImage( - new Image(url), - BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, - BackgroundPosition.DEFAULT, - BackgroundSize.DEFAULT - ); - background.setBackground(new Background(backgroundImg)); - return new Pair<>(background.getPrefWidth(), background.getPrefHeight()); - } -} \ No newline at end of file diff --git a/src/main/java/io/rpg/model/Game.java b/src/main/java/io/rpg/model/Game.java deleted file mode 100644 index b50acc30..00000000 --- a/src/main/java/io/rpg/model/Game.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.rpg.model; - -import java.util.ArrayList; - -public class Game { - private Player player; -// Timer - ArrayList gameObjects; - - public Game(){ - this.gameObjects=new ArrayList<>(); - this.player=null; - } - - public void addGameObject(GameObject gameObject) throws Exception { - if(gameObject instanceof Player){ - if(player!=null) - throw new Exception(); - player=(Player)gameObject; - } - gameObjects.add(gameObject); - } - - public GameObject getObject(int index){ - return gameObjects.get(index); - } - public int getObjectCount(){ - return gameObjects.size(); - } - - public Player getPlayer() { - return player; - } - public void update(float elapsed){ - player.update(elapsed); - } -} diff --git a/src/main/java/io/rpg/model/GameObject.java b/src/main/java/io/rpg/model/GameObject.java deleted file mode 100644 index a9d5c3e9..00000000 --- a/src/main/java/io/rpg/model/GameObject.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.rpg.model; - -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; - -/** - * A Temporary Class waiting to be replaced with GameObject class from branch RPG-79 - */ -public class GameObject { - -// private Pair position; - protected Vector position; - private Image image; -// control that displays objects - private ImageView imageView; - - public GameObject(Vector position, Image image) { - this.position = position; - this.image = image; - } - - public Vector getPosition() { - return position; - } - - public Image getImage(){ - return image; - } - - public void setImage(Image image) { - this.image = image; - } - - public ImageView getImageView() { - return imageView; - } - - public void setImageView(ImageView imageView) { - this.imageView = imageView; - } - -} diff --git a/src/main/java/io/rpg/model/GameObjectStandIn.java b/src/main/java/io/rpg/model/GameObjectStandIn.java deleted file mode 100644 index ff7e92d2..00000000 --- a/src/main/java/io/rpg/model/GameObjectStandIn.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.rpg.model; - -import javafx.scene.image.Image; -import javafx.util.Pair; - -/** - * A Temporary Class waiting to be replaced with GameObject class from branch RPG-79 - */ -public class GameObjectStandIn { - - private Pair position; - private Image image; - - public GameObjectStandIn(Pair position, Image image) { - this.position = position; - this.image = image; - } - - public Pair getPosition() { - return position; - } - - public Image getImage(){ - return image; - } -} diff --git a/src/main/java/io/rpg/model/GameWorld.java b/src/main/java/io/rpg/model/GameWorld.java deleted file mode 100644 index bfc03791..00000000 --- a/src/main/java/io/rpg/model/GameWorld.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.rpg.model; - -final public class GameWorld { -} diff --git a/src/main/java/io/rpg/model/GameWorldConfig.java b/src/main/java/io/rpg/model/GameWorldConfig.java deleted file mode 100644 index 53eade11..00000000 --- a/src/main/java/io/rpg/model/GameWorldConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.rpg.model; - -import java.util.ArrayList; -import java.util.List; - -public class GameWorldConfig { - private String tag; - - private ArrayList locations; - - // This class is not meant to be instantiated - // by hand. Only Gson should be able to do so - private GameWorldConfig() {} - - public String getTag() { - return tag; - } - - public List getLocations() { - return locations; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("\n{\n").append("\ttag: ").append(tag).append('\n'); - locations.forEach(location -> { - builder.append("\tlocation-tag: ").append(location).append('\n'); - }); - return builder.append("}\n").toString(); - } -} diff --git a/src/main/java/io/rpg/model/Item.java b/src/main/java/io/rpg/model/Item.java deleted file mode 100644 index ee942f7f..00000000 --- a/src/main/java/io/rpg/model/Item.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.rpg.model; - -public class Item { - private int strength; - private final String name; - - public Item(int strength, String name) { - this.strength = strength; - this.name = name; - } - - public int strength() { - return strength; - } - - public String name() { - return name; - } -} \ No newline at end of file diff --git a/src/main/java/io/rpg/model/Player.java b/src/main/java/io/rpg/model/Player.java deleted file mode 100644 index 1b8d8eb5..00000000 --- a/src/main/java/io/rpg/model/Player.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.rpg.model; - -import javafx.scene.image.Image; - -public class Player extends GameObject{ - - int strength; - float speed; - Vector direction; - boolean rightPressed; - boolean leftPressed; - boolean upPressed; - boolean downPressed; - - public Player(Vector position, Image image){ - super(position,image); - speed=5f; - direction=new Vector(0,0); - this.rightPressed=false; - this.leftPressed=false; - this.upPressed=false; - this.downPressed=false; - this.strength=0; - } - - public void updateStrength(int value){ - strength += value; - } - - public void setDirection(Vector direction) { - this.direction = direction; - } - - public void update(float elapsed){ - float y=0; - float x=0; -// the sum tells us the direction - if(upPressed) - y+=-1; - - if(downPressed) - y+=1; - - if(leftPressed) - x+=-1; - - if(rightPressed) - x+=1; - - - this.position=new Vector(this.position.x+speed*x*elapsed/1000,this.position.y+speed*y*elapsed/1000); - } - - public void setRightPressed(boolean rightPressed) { - this.rightPressed = rightPressed; - } - - public void setLeftPressed(boolean leftPressed) { - this.leftPressed = leftPressed; - } - - public void setUpPressed(boolean upPressed) { - this.upPressed = upPressed; - } - - public void setDownPressed(boolean downPressed) { - this.downPressed = downPressed; - } - - public void setStrength(int strength) { - this.strength = strength; - } -} diff --git a/src/main/java/io/rpg/model/Vector.java b/src/main/java/io/rpg/model/Vector.java deleted file mode 100644 index 55ba4f8b..00000000 --- a/src/main/java/io/rpg/model/Vector.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.rpg.model; - -public class Vector { - public final float x; - public final float y; - public Vector(float x,float y){ - this.x=x; - this.y=y; - } - -} diff --git a/src/main/java/io/rpg/model/data/GameObjectStateChange.java b/src/main/java/io/rpg/model/data/GameObjectStateChange.java new file mode 100644 index 00000000..20367182 --- /dev/null +++ b/src/main/java/io/rpg/model/data/GameObjectStateChange.java @@ -0,0 +1,22 @@ +package io.rpg.model.data; + +import io.rpg.model.object.GameObject; + +public record GameObjectStateChange ( + GameObject source, + Object payload // TODO: what kind of data do we want? What changed? What can change? + // or maybe implement separate methods for observer & emitter for different kinds + // of event +) { + public interface Observer { + void onGameObjectStateChange(GameObjectStateChange event); + } + + public interface Emitter { + void emitGameObjectStateChange(GameObjectStateChange event); + + void addGameObjectStateChangeObserver(Observer observer); + + void removeGameObjectStateChangeObserver(Observer observer); + } +} diff --git a/src/main/java/io/rpg/model/data/KeyboardEvent.java b/src/main/java/io/rpg/model/data/KeyboardEvent.java new file mode 100644 index 00000000..d7021da6 --- /dev/null +++ b/src/main/java/io/rpg/model/data/KeyboardEvent.java @@ -0,0 +1,18 @@ +package io.rpg.model.data; + +import javafx.scene.Scene; +import javafx.scene.input.KeyEvent; + +final public record KeyboardEvent(Scene source, KeyEvent payload) { + public interface Observer { + void onKeyboardEvent(KeyboardEvent event); + } + + public interface Emitter { + void addKeyboardEventObserver(KeyboardEvent.Observer observer); + + void removeKeyboardEventObserver(KeyboardEvent.Observer observer); + + void emitKeyboardEvent(KeyboardEvent event); + } +} diff --git a/src/main/java/io/rpg/model/data/LocationModelStateChange.java b/src/main/java/io/rpg/model/data/LocationModelStateChange.java new file mode 100644 index 00000000..0720837f --- /dev/null +++ b/src/main/java/io/rpg/model/data/LocationModelStateChange.java @@ -0,0 +1,20 @@ +package io.rpg.model.data; + +import io.rpg.model.location.LocationModel; + +public record LocationModelStateChange( + LocationModel source, + Object payload // TODO: Same considerations as for GameObjectStateChange +) { + public interface Observer { + void onLocationModelStateChange(LocationModelStateChange event); + } + + public interface Emitter { + void addOnLocationModelStateChangeObserver(Observer observer); + + void removeOnLocationModelStateChangeObserver(Observer observer); + + void emitLocationModelStateChange(LocationModelStateChange event); + } +} diff --git a/src/main/java/io/rpg/model/data/MouseClickedEvent.java b/src/main/java/io/rpg/model/data/MouseClickedEvent.java new file mode 100644 index 00000000..9bd59fa7 --- /dev/null +++ b/src/main/java/io/rpg/model/data/MouseClickedEvent.java @@ -0,0 +1,27 @@ +package io.rpg.model.data; + +import io.rpg.view.GameObjectView; +import javafx.scene.input.MouseEvent; +import org.jetbrains.annotations.NotNull; + +/** + * + */ +final public record MouseClickedEvent( + @NotNull GameObjectView source, + @NotNull MouseEvent payload +) { + public interface Observer { + void onMouseClickedEvent(MouseClickedEvent event); + } + + public interface Emitter { + void emitOnMouseClickedEvent(MouseClickedEvent event); + + void addOnClickedObserver(MouseClickedEvent.Observer observer); + + void removeOnClickedObserver(MouseClickedEvent.Observer observer); + } +} + + diff --git a/src/main/java/io/rpg/model/data/Position.java b/src/main/java/io/rpg/model/data/Position.java index 40f2a1bb..347715dd 100644 --- a/src/main/java/io/rpg/model/data/Position.java +++ b/src/main/java/io/rpg/model/data/Position.java @@ -2,8 +2,11 @@ import java.util.Objects; -// This class can NOT be record due to some issues -// with Gson library +/** + * Represents current position by holding row / col values. + * This class can NOT be record due to some issues with + * Gson library. + */ public class Position { public final int row; @@ -26,10 +29,9 @@ public int getCol() { public boolean equals(Object obj) { if (this == obj) { return true; - } else if (!(obj instanceof Position)){ + } else if (!(obj instanceof Position that)) { return false; } else { - Position that = (Position) obj; return this.row == that.row && this.col == that.col; } } diff --git a/src/main/java/io/rpg/model/data/Vector.java b/src/main/java/io/rpg/model/data/Vector.java new file mode 100644 index 00000000..01e2b325 --- /dev/null +++ b/src/main/java/io/rpg/model/data/Vector.java @@ -0,0 +1,18 @@ +package io.rpg.model.data; + +/** + * TODO. + */ +public class Vector { + @SuppressWarnings("checkstyle:MemberName") + public final float x; + + @SuppressWarnings("checkstyle:MemberName") + public final float y; + + public Vector(float x, float y) { + this.x = x; + this.y = y; + } + +} diff --git a/src/main/java/io/rpg/model/location/Location.java b/src/main/java/io/rpg/model/location/Location.java deleted file mode 100644 index bea5b5aa..00000000 --- a/src/main/java/io/rpg/model/location/Location.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.rpg.model.location; - -/** - * Represents single location in our game - */ -public class Location { -} diff --git a/src/main/java/io/rpg/model/location/LocationModel.java b/src/main/java/io/rpg/model/location/LocationModel.java new file mode 100644 index 00000000..6e4b240a --- /dev/null +++ b/src/main/java/io/rpg/model/location/LocationModel.java @@ -0,0 +1,53 @@ +package io.rpg.model.location; + +import io.rpg.model.data.LocationModelStateChange; +import io.rpg.model.object.Player; +import io.rpg.model.object.GameObject; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents single location in our game + */ +public class LocationModel implements LocationModelStateChange.Emitter { + private String tag; + private List gameObjects; + private Player player; + + private final Set locationModelStateChangeObservers; + + public LocationModel(@NotNull String tag, @NotNull List gameObjects) { + this.tag = tag; + this.gameObjects = gameObjects; + this.player = null; + this.locationModelStateChangeObservers = new LinkedHashSet<>(); + } + + public void setPlayer(@NotNull Player player) { + this.player = player; + } + + public String getTag() { + return tag; + } + + @Override + public void addOnLocationModelStateChangeObserver(LocationModelStateChange.Observer observer) { + locationModelStateChangeObservers.add(observer); + } + + @Override + public void removeOnLocationModelStateChangeObserver(LocationModelStateChange.Observer observer) { + locationModelStateChangeObservers.remove(observer); + } + + @Override + public void emitLocationModelStateChange(LocationModelStateChange event) { + locationModelStateChangeObservers.forEach(observer -> { + observer.onLocationModelStateChange(event); + }); + } +} diff --git a/src/main/java/io/rpg/model/metadata/.gitkeep b/src/main/java/io/rpg/model/metadata/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/io/rpg/model/object/CollectibleGameObject.java b/src/main/java/io/rpg/model/object/CollectibleGameObject.java index 1cd8abf7..60c437ff 100644 --- a/src/main/java/io/rpg/model/object/CollectibleGameObject.java +++ b/src/main/java/io/rpg/model/object/CollectibleGameObject.java @@ -4,12 +4,12 @@ import org.jetbrains.annotations.NotNull; final public class CollectibleGameObject extends InteractiveGameObject { - public CollectibleGameObject(@NotNull String tag, @NotNull Position position) { - super(tag, position); - } - - @Override - public void onAction() { - - } + public CollectibleGameObject(@NotNull String tag, @NotNull Position position) { + super(tag, position); + } + + @Override + public void onAction() { + + } } diff --git a/src/main/java/io/rpg/model/object/DialogGameObject.java b/src/main/java/io/rpg/model/object/DialogGameObject.java index b191ea8e..54a97023 100644 --- a/src/main/java/io/rpg/model/object/DialogGameObject.java +++ b/src/main/java/io/rpg/model/object/DialogGameObject.java @@ -3,13 +3,13 @@ import io.rpg.model.data.Position; import org.jetbrains.annotations.NotNull; -final public class DialogGameObject extends InteractiveGameObject{ - public DialogGameObject(@NotNull String tag, @NotNull Position position) { - super(tag, position); - } +final public class DialogGameObject extends InteractiveGameObject { + public DialogGameObject(@NotNull String tag, @NotNull Position position) { + super(tag, position); + } - @Override - public void onAction() { + @Override + public void onAction() { - } + } } diff --git a/src/main/java/io/rpg/model/object/GameObject.java b/src/main/java/io/rpg/model/object/GameObject.java index ad6350da..ad4089ae 100644 --- a/src/main/java/io/rpg/model/object/GameObject.java +++ b/src/main/java/io/rpg/model/object/GameObject.java @@ -1,5 +1,6 @@ package io.rpg.model.object; +import io.rpg.config.model.GameObjectConfig; import io.rpg.model.data.Position; import org.jetbrains.annotations.NotNull; @@ -20,6 +21,17 @@ public class GameObject { @NotNull private final String tag; + /** + * + */ + @NotNull + private String assetPath; + + @NotNull + public String getAssetPath() { + return assetPath; + } + /** * Unique identifier of this game object */ @@ -36,12 +48,19 @@ public Position getPosition() { return position; } - public void validate() { - // nothing to validate for now + public GameObject(@NotNull String tag, @NotNull Position position) { + this.tag = tag; + this.position = position; + this.assetPath = ""; // TODO } - public GameObject(@NotNull String tag, @NotNull Position position) { + public GameObject(@NotNull String tag, @NotNull Position position, @NotNull String assetPath) { this.tag = tag; this.position = position; + this.assetPath = assetPath; + } + + public static GameObject fromConfig(GameObjectConfig config) { + return new GameObject("XD", new Position(3, 3), "file:assets/someDude.png"); } } diff --git a/src/main/java/io/rpg/model/object/GameObjectConfig.java b/src/main/java/io/rpg/model/object/GameObjectConfig.java deleted file mode 100644 index ea0de3e6..00000000 --- a/src/main/java/io/rpg/model/object/GameObjectConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.rpg.model.object; - -import io.rpg.model.data.Position; -import org.jetbrains.annotations.NotNull; - -public class GameObjectConfig extends GameObject { - private String type; - - public GameObjectConfig(@NotNull String tag, @NotNull Position position) { - super(tag, position); - } - - @Override - public void validate() { - super.validate(); - if (!GameObjects.isValidType(type)) { - throw new IllegalStateException("Invalid object type: " + type); - } - } -} diff --git a/src/main/java/io/rpg/model/object/GameObjects.java b/src/main/java/io/rpg/model/object/GameObjects.java index b0f05629..b513fce7 100644 --- a/src/main/java/io/rpg/model/object/GameObjects.java +++ b/src/main/java/io/rpg/model/object/GameObjects.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Set; +import java.util.concurrent.Executors; public class GameObjects { // this class is mean to only provide static methods diff --git a/src/main/java/io/rpg/model/object/Item.java b/src/main/java/io/rpg/model/object/Item.java new file mode 100644 index 00000000..3a6cd4cc --- /dev/null +++ b/src/main/java/io/rpg/model/object/Item.java @@ -0,0 +1,19 @@ +package io.rpg.model.object; + +public class Item { + private int strength; + private final String name; + + public Item(int strength, String name) { + this.strength = strength; + this.name = name; + } + + public int strength() { + return strength; + } + + public String name() { + return name; + } +} diff --git a/src/main/java/io/rpg/model/object/NavigationalGameObject.java b/src/main/java/io/rpg/model/object/NavigationalGameObject.java index f98ee0e4..e68e543d 100644 --- a/src/main/java/io/rpg/model/object/NavigationalGameObject.java +++ b/src/main/java/io/rpg/model/object/NavigationalGameObject.java @@ -4,15 +4,16 @@ import org.jetbrains.annotations.NotNull; final public class NavigationalGameObject extends InteractiveGameObject { - public NavigationalGameObject(@NotNull String tag, - @NotNull Position position) { - super(tag, position); - } + public NavigationalGameObject(@NotNull String tag, + @NotNull Position position) { + super(tag, position); + } - public void navigateTo(Object target){}; + public void navigateTo(Object target) { + } - @Override - public void onAction() { + @Override + public void onAction() { - } + } } diff --git a/src/main/java/io/rpg/model/object/Player.java b/src/main/java/io/rpg/model/object/Player.java new file mode 100644 index 00000000..d1409b58 --- /dev/null +++ b/src/main/java/io/rpg/model/object/Player.java @@ -0,0 +1,74 @@ +package io.rpg.model.object; + +import io.rpg.model.data.Vector; +import io.rpg.torefact.GameObject; +import javafx.scene.image.Image; + +public class Player extends GameObject { + + int strength; + float speed; + Vector direction; + boolean rightPressed; + boolean leftPressed; + boolean upPressed; + boolean downPressed; + + public Player(Vector position, Image image) { + super(position, image); + speed = 5f; + direction = new Vector(0, 0); + this.rightPressed = false; + this.leftPressed = false; + this.upPressed = false; + this.downPressed = false; + this.strength = 0; + } + + public void updateStrength(int value) { + strength += value; + } + + public void setDirection(Vector direction) { + this.direction = direction; + } + + public void update(float elapsed) { + float y = 0; + float x = 0; +// the sum tells us the direction + if (upPressed) + y += -1; + + if (downPressed) + y += 1; + + if (leftPressed) + x += -1; + + if (rightPressed) + x += 1; + + this.position = new Vector(this.position.x + speed * x * elapsed / 1000, this.position.y + speed * y * elapsed / 1000); + } + + public void setRightPressed(boolean rightPressed) { + this.rightPressed = rightPressed; + } + + public void setLeftPressed(boolean leftPressed) { + this.leftPressed = leftPressed; + } + + public void setUpPressed(boolean upPressed) { + this.upPressed = upPressed; + } + + public void setDownPressed(boolean downPressed) { + this.downPressed = downPressed; + } + + public void setStrength(int strength) { + this.strength = strength; + } +} diff --git a/src/main/java/io/rpg/torefact/GameObject.java b/src/main/java/io/rpg/torefact/GameObject.java new file mode 100644 index 00000000..893b6887 --- /dev/null +++ b/src/main/java/io/rpg/torefact/GameObject.java @@ -0,0 +1,43 @@ +package io.rpg.torefact; + +import io.rpg.model.data.Vector; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; + +/** + * A Temporary Class waiting to be replaced with GameObject class from branch RPG-79 + */ +public class GameObject { + + // private Pair position; + protected Vector position; + private Image image; + // control that displays objects + private ImageView imageView; + + public GameObject(Vector position, Image image) { + this.position = position; + this.image = image; + } + + public Vector getPosition() { + return position; + } + + public Image getImage() { + return image; + } + + public void setImage(Image image) { + this.image = image; + } + + public ImageView getImageView() { + return imageView; + } + + public void setImageView(ImageView imageView) { + this.imageView = imageView; + } + +} diff --git a/src/main/java/io/rpg/torefact/GameToRefract.java b/src/main/java/io/rpg/torefact/GameToRefract.java new file mode 100644 index 00000000..e7a25564 --- /dev/null +++ b/src/main/java/io/rpg/torefact/GameToRefract.java @@ -0,0 +1,41 @@ +package io.rpg.torefact; + +import io.rpg.model.object.Player; + +import java.util.ArrayList; + +public class GameToRefract { + private Player player; + // Timer + ArrayList gameObjects; + + public GameToRefract() { + this.gameObjects = new ArrayList<>(); + this.player = null; + } + + public void addGameObject(GameObject gameObject) throws Exception { + if (gameObject instanceof Player) { + if (player != null) + throw new Exception(); + player = (Player) gameObject; + } + gameObjects.add(gameObject); + } + + public GameObject getObject(int index) { + return gameObjects.get(index); + } + + public int getObjectCount() { + return gameObjects.size(); + } + + public Player getPlayer() { + return player; + } + + public void update(float elapsed) { + player.update(elapsed); + } +} diff --git a/src/main/java/io/rpg/util/Result.java b/src/main/java/io/rpg/util/Result.java new file mode 100644 index 00000000..6c669367 --- /dev/null +++ b/src/main/java/io/rpg/util/Result.java @@ -0,0 +1,84 @@ +package io.rpg.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class Result { + @Nullable + private final OkT okValue; + + @Nullable + private final ErrorT errorValue; + + @NotNull + private final Type type; + + private Result(@Nullable OkT okValue, @Nullable ErrorT errorValue, @NotNull Type type) { + this.okValue = okValue; + this.errorValue = errorValue; + this.type = type; + } + + public static Result ok(@Nullable S okValue) { + return new Result<>(okValue, null, Type.OK); + } + + public static Result error(@Nullable E errorValue) { + return new Result<>(null, errorValue, Type.ERROR); + } + + public boolean isError() { + return type == Type.ERROR; + } + + public boolean isOk() { + return type == Type.OK; + } + + @NotNull + public Type getType() { + return type; + } + + @Nullable + public OkT getOkValue() { + if (isOk()) { + return okValue; + } else { + throw new IllegalStateException("Attempt to access ok value on error result!"); + } + } + + @Nullable + public ErrorT getErrorValue() { + if (isError()) { + return errorValue; + } else { + throw new IllegalStateException("Attempt to access error value on ok result!"); + } + } + + public boolean isOkValueNull() { + return okValue == null; + } + + public boolean isErrorValueNull() { + return errorValue == null; + } + + @NotNull + public Optional getOkValueOpt() { + return Optional.ofNullable(getOkValue()); + } + + @NotNull + public Optional getErrorValueOpt() { + return Optional.ofNullable(getErrorValue()); + } + + public enum Type { + OK, ERROR + } +} diff --git a/src/main/java/io/rpg/view/.gitkeep b/src/main/java/io/rpg/view/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/io/rpg/view/GameObjectView.java b/src/main/java/io/rpg/view/GameObjectView.java new file mode 100644 index 00000000..b55aaad7 --- /dev/null +++ b/src/main/java/io/rpg/view/GameObjectView.java @@ -0,0 +1,49 @@ +package io.rpg.view; + +import io.rpg.model.data.GameObjectStateChange; +import io.rpg.model.data.MouseClickedEvent; +import io.rpg.model.data.Position; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import org.jetbrains.annotations.NotNull; + +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; + +public class GameObjectView extends ImageView + implements MouseClickedEvent.Emitter, GameObjectStateChange.Observer { + private Path path; + private final Set onClickedObservers; + + public GameObjectView(@NotNull Path assetPath, @NotNull Position position) { + this.path = assetPath; + this.setImage(new Image(path.toString())); + // todo: better position class + this.setX(position.col); + this.setY(position.row); + this.onClickedObservers = new HashSet<>(); + this.setOnMouseClicked(event -> emitOnMouseClickedEvent(new MouseClickedEvent(this, event))); + } + + @Override + public void emitOnMouseClickedEvent(MouseClickedEvent event) { + onClickedObservers.forEach(listener -> listener.onMouseClickedEvent(event)); + } + + @Override + public void addOnClickedObserver(MouseClickedEvent.Observer observer) { + onClickedObservers.add(observer); + } + + @Override + public void removeOnClickedObserver(MouseClickedEvent.Observer observer) { + onClickedObservers.remove(observer); + } + + @Override + public void onGameObjectStateChange(GameObjectStateChange event) { + // TODO: implement update logic here or create view model class but it + // is even more boilerplate + } +} diff --git a/src/main/java/io/rpg/view/LocationView.java b/src/main/java/io/rpg/view/LocationView.java new file mode 100644 index 00000000..a3d0b55a --- /dev/null +++ b/src/main/java/io/rpg/view/LocationView.java @@ -0,0 +1,91 @@ +package io.rpg.view; + +import io.rpg.model.data.KeyboardEvent; +import io.rpg.model.data.LocationModelStateChange; +import io.rpg.viewmodel.LocationViewModel; +import io.rpg.config.model.LocationConfig; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.layout.HBox; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; + +public class LocationView extends Scene + implements KeyboardEvent.Emitter, LocationModelStateChange.Observer { + static final int SCALE = 32; // TODO: REMOVE THIS + private final static URL FXML_URL = LocationViewModel.class.getResource("location-view.fxml"); + + private final Logger logger; + + private final Set onKeyPressedObservers; + + private final LocationViewModel viewModel; + + public LocationView(HBox root, LocationViewModel viewModel) { + super(root); + + logger = LogManager.getLogger(LocationView.class); + + this.viewModel = viewModel; + onKeyPressedObservers = new HashSet<>(); + + this.setOnKeyPressed(event -> emitKeyboardEvent(new KeyboardEvent(this, event))); + this.setOnKeyReleased(event -> emitKeyboardEvent(new KeyboardEvent(this, event))); + + System.out.println("CHILDREN"); + System.out.println(root.getChildrenUnmodifiable()); + } + + public LocationViewModel getViewModel() { + return viewModel; + } + + public static LocationView loadFromFXML(@NotNull URL fxmlUrl) throws IOException { + FXMLLoader loader = new FXMLLoader(fxmlUrl); + Parent parent = loader.load(); + LocationViewModel viewModel = loader.getController(); + assert parent == viewModel.getParent(); + return new LocationView(viewModel.getParent(), viewModel); + } + + public static LocationView fromConfig(LocationConfig config) throws IOException { + LocationView view = loadFromFXML(FXML_URL); + System.out.println("BACKGROUND PATH"); + System.out.println(config.getBackgroundPath()); + view.getViewModel().setBackground(new Image(config.getBackgroundPath())); + // todo: na podstawie configu ustawić pola korzystając z view modelu + return view; + } + + @Override + public void addKeyboardEventObserver(KeyboardEvent.Observer observer) { + onKeyPressedObservers.add(observer); + } + + @Override + public void removeKeyboardEventObserver(KeyboardEvent.Observer observer) { + onKeyPressedObservers.remove(observer); + } + + @Override + public void emitKeyboardEvent(KeyboardEvent event) { + onKeyPressedObservers.forEach(observer -> { + observer.onKeyboardEvent(event); + }); + } + + @Override + public void onLocationModelStateChange(LocationModelStateChange event) { + // TODO: implement state change & appropriate events + // most likely here we watn to pass this event to LocationViewModel or even + // make LocationViewModel implement LocationModelStateChange.Observer + } +} diff --git a/src/main/java/io/rpg/view/popups/PointsEarnedPopup.java b/src/main/java/io/rpg/view/popups/PointsEarnedPopup.java new file mode 100644 index 00000000..b818e8e6 --- /dev/null +++ b/src/main/java/io/rpg/view/popups/PointsEarnedPopup.java @@ -0,0 +1,59 @@ +package io.rpg.view.popups; + +import io.rpg.viewmodel.PointsPopupViewModel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.paint.Color; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import javafx.stage.Window; +import javafx.util.Pair; + +import java.io.IOException; +import java.util.Objects; + +public class PointsEarnedPopup { + + private final FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(PointsEarnedPopup.class.getResource("points-earned-view.fxml"))); + private Parent root; + private PointsPopupViewModel controller; + private final Scene popupScene; + + public PointsEarnedPopup() { + // read FXML view + try { + root = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + popupScene = new Scene(root, Color.TRANSPARENT); + } + + public Stage getPopup(int pointsCount, Scene scene) { + // fill dynamic view components + if (controller == null) controller = loader.getController(); + controller.setPointsCount(pointsCount); + Pair backgroundDims = controller.setBackgroundImage("file:assets/point-popup-bg.png"); + + // create popup stage + Stage popupStage = new Stage(StageStyle.TRANSPARENT); + Window window = scene.getWindow(); + popupStage.initOwner(window); + + // add and center popupScene on popup stage + popupStage.setScene(popupScene); + popupStage.setX(window.getX() + window.getWidth() / 2 - backgroundDims.getKey() / 2); + popupStage.setY(window.getY() + window.getHeight() / 2 - backgroundDims.getValue() / 2); + + // close popup after clicking aside + popupStage.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> { + if (!isNowFocused) { + popupStage.close(); + } + }); + + return popupStage; + } +} diff --git a/src/main/java/io/rpg/viewmodel/LocationViewModel.java b/src/main/java/io/rpg/viewmodel/LocationViewModel.java new file mode 100644 index 00000000..8b51c8e0 --- /dev/null +++ b/src/main/java/io/rpg/viewmodel/LocationViewModel.java @@ -0,0 +1,48 @@ +package io.rpg.viewmodel; + +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; + +import java.net.URL; +import java.util.ResourceBundle; + +public class LocationViewModel implements Initializable { + @FXML + private ImageView mapImageView; + @FXML + private Pane foregroundPane, contentPane; + @FXML + private HBox parent; + + public LocationViewModel() { + + } + + public void setBackground(Image background) { + this.mapImageView.imageProperty().setValue(background); + } + + public HBox getParent() { + return parent; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + System.out.println("INITIALIZE"); + mapImageView.imageProperty().addListener((property, oldImg, newImg) -> { + System.out.println("Map image view changing"); + contentPane.setPrefWidth(newImg.getWidth()); + contentPane.setPrefHeight(newImg.getHeight()); + + foregroundPane.setPrefWidth(newImg.getWidth()); + foregroundPane.setPrefHeight(newImg.getHeight()); + + mapImageView.setFitWidth(newImg.getWidth()); + mapImageView.setFitHeight(newImg.getHeight()); + }); + } +} diff --git a/src/main/java/io/rpg/viewmodel/PointsPopupViewModel.java b/src/main/java/io/rpg/viewmodel/PointsPopupViewModel.java new file mode 100644 index 00000000..6523df37 --- /dev/null +++ b/src/main/java/io/rpg/viewmodel/PointsPopupViewModel.java @@ -0,0 +1,30 @@ +package io.rpg.viewmodel; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.layout.*; +import javafx.util.Pair; + +public class PointsPopupViewModel { + + @FXML + private Label label; + @FXML + private Pane background; + + public void setPointsCount(int pointsCount) { + label.setText("Earned " + pointsCount + " points!"); + } + + public Pair setBackgroundImage(String url) { + BackgroundImage backgroundImg = new BackgroundImage( + new Image(url), + BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, + BackgroundPosition.DEFAULT, + BackgroundSize.DEFAULT + ); + background.setBackground(new Background(backgroundImg)); + return new Pair<>(background.getPrefWidth(), background.getPrefHeight()); + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index b613de50..d8bcfddd 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -13,12 +13,13 @@ requires org.apache.logging.log4j.core; opens io.rpg to javafx.fxml; - opens io.rpg.model to com.google.gson; opens io.rpg.model.location to com.google.gson; opens io.rpg.model.object to com.google.gson; opens io.rpg.model.data to com.google.gson; - opens io.rpg.gui to javafx.fxml; - opens io.rpg.gui.popups to javafx.fxml; - + + opens io.rpg.viewmodel to javafx.fxml; + opens io.rpg.torefact to com.google.gson; + opens io.rpg.config.model to com.google.gson; + exports io.rpg; } diff --git a/src/main/resources/io/rpg/hello-view.fxml b/src/main/resources/io/rpg/hello-view.fxml deleted file mode 100644 index adb9c0f5..00000000 --- a/src/main/resources/io/rpg/hello-view.fxml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - -