diff --git a/build.gradle b/build.gradle
index c4c14252..78531794 100644
--- a/build.gradle
+++ b/build.gradle
@@ -61,6 +61,14 @@ test {
useJUnitPlatform()
}
+compileJava {
+ options.compilerArgs += ["--add-exports=javafx.graphics/com.sun.javafx.scene=io.rpg"]
+}
+
+run {
+ jvmArgs = ['--add-exports=javafx.graphics/com.sun.javafx.scene=io.rpg']
+}
+
jlink {
imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index f160a312..7d3736bb 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -149,11 +149,6 @@
-
-
-
-
-
diff --git a/src/main/java/io/rpg/Initializer.java b/src/main/java/io/rpg/Initializer.java
index a76d14cb..91a2c66e 100644
--- a/src/main/java/io/rpg/Initializer.java
+++ b/src/main/java/io/rpg/Initializer.java
@@ -1,16 +1,13 @@
package io.rpg;
import io.rpg.config.ConfigLoader;
-import io.rpg.config.model.PlayerConfig;
import io.rpg.controller.Controller;
import io.rpg.config.model.GameWorldConfig;
import io.rpg.config.model.LocationConfig;
import io.rpg.controller.PlayerController;
import io.rpg.model.actions.LocationChangeAction;
-import io.rpg.model.data.Position;
import io.rpg.model.location.LocationModel;
import io.rpg.model.object.GameObject;
-import io.rpg.config.model.GameObjectConfig;
import io.rpg.model.object.Player;
import io.rpg.util.GameObjectFactory;
import io.rpg.util.GameObjectViewFactory;
@@ -79,7 +76,6 @@ public Result initialize() {
assert view != null;
gameObjectViews.forEach(view::addChild);
-
model.addOnLocationModelStateChangeObserver(view);
controllerBuilder
@@ -87,7 +83,6 @@ public Result initialize() {
.addModelForTag(locationConfig.getTag(), model)
.registerToViews(gameObjectViews);
- view.createViewsForObjects(model);
}
// Player is created separately
@@ -100,8 +95,6 @@ public Result initialize() {
Controller controller = controllerBuilder.build();
-// // TODO: this is a temporary solution
-// controller.setPlayerView(playerView);
Game.Builder gameBuilder = new Game.Builder();
Game game = gameBuilder
@@ -135,6 +128,7 @@ public static void registerGameObjectViewsToModel(List gameObjects,
// registration
gameObject.addGameObjectStateChangeObserver(gameObjectView);
+ gameObjectView.bindToGameObject(gameObject);
}
}
diff --git a/src/main/java/io/rpg/Main.java b/src/main/java/io/rpg/Main.java
index c497e85c..ece22952 100644
--- a/src/main/java/io/rpg/Main.java
+++ b/src/main/java/io/rpg/Main.java
@@ -49,11 +49,7 @@ public void handle(long now) {
if (lastUpdate != -1) {
float difference = (now - lastUpdate) / 1e6f;
- game.getController().getCurrentModel().update(difference);
- Player player = game.getController().getCurrentModel().getPlayer();
- if (player != null) {
- player.render();
- }
+ game.getController().getPlayerController().getPlayer().update(difference);
}
lastUpdate = now;
}
diff --git a/src/main/java/io/rpg/config/model/GameObjectConfig.java b/src/main/java/io/rpg/config/model/GameObjectConfig.java
index 6fb2499a..419cf392 100644
--- a/src/main/java/io/rpg/config/model/GameObjectConfig.java
+++ b/src/main/java/io/rpg/config/model/GameObjectConfig.java
@@ -13,12 +13,28 @@
* Represents {@link io.rpg.model.object.GameObject} configuration provided by user
* in configuration files.
*/
-public class GameObjectConfig extends GameObject {
+public class GameObjectConfig {
private String type;
+ private String tag;
+ private Position position;
+ private String assetPath;
public GameObjectConfig(@NotNull String tag, @NotNull Position position) {
- super(tag, position);
+ this.tag = tag;
+ this.position = position;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public String getAssetPath() {
+ return assetPath;
}
public String getTypeString() {
@@ -39,15 +55,10 @@ public Result validate() {
}
public void updateFrom(GameObjectConfig gameObjectConfig) {
- if (gameObjectConfig.getPosition() != null) {
- this.position = gameObjectConfig.getPosition();
- }
+ this.position = gameObjectConfig.position;
if (gameObjectConfig.getTypeString() != null) {
this.type = gameObjectConfig.getTypeString();
}
- if (gameObjectConfig.getAssetPath() != null) {
- this.assetPath = gameObjectConfig.assetPath;
- }
}
public String getFieldDescription() {
@@ -68,9 +79,7 @@ public String getFieldDescription() {
@Override
public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("\n{\n").append(super.getFieldDescription()).append(getFieldDescription());
- return builder.append("}").toString();
+ return "\n{\n" + getFieldDescription() + "}";
}
}
diff --git a/src/main/java/io/rpg/controller/Controller.java b/src/main/java/io/rpg/controller/Controller.java
index c4bebae8..551c3395 100644
--- a/src/main/java/io/rpg/controller/Controller.java
+++ b/src/main/java/io/rpg/controller/Controller.java
@@ -5,7 +5,6 @@
import io.rpg.model.data.KeyboardEvent;
import io.rpg.model.data.MouseClickedEvent;
import io.rpg.model.data.Position;
-import io.rpg.model.data.Vector;
import io.rpg.model.location.LocationModel;
import io.rpg.model.object.*;
import io.rpg.util.Result;
@@ -13,6 +12,8 @@
import io.rpg.view.LocationView;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.function.Supplier;
+import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
@@ -135,7 +136,7 @@ else if (tagToLocationViewMap.size() != tagToLocationModelMap.size())
@Override
public void onKeyboardEvent(KeyboardEvent event) {
// TODO: implement event handling
- logger.info("Controller notified on key pressed from " + event.source());
+ logger.trace("Controller notified on key pressed from " + event.source());
//TODO: call Player::set...Pressed depending on keyCode and whether the key was pressed or released
KeyEvent payload = event.payload();
@@ -163,11 +164,14 @@ private int getWindowCenterY() {
@Override
public void onMouseClickedEvent(MouseClickedEvent event) {
- int SCALE = 64;
- Vector playerPos = currentModel.getPlayer().getPixelPosition();
+ Point2D playerPos = playerController.getPlayer().getExactPosition();
GameObjectView objectView = event.source();
- GameObject object = currentModel.getObject((int) objectView.getY() / SCALE, (int) objectView.getX() / SCALE);
- if (Math.abs(playerPos.x - objectView.getX()) / SCALE <= 1.5 && Math.abs(playerPos.y - objectView.getY()) / SCALE <= 1.5) {
+ Position position = new Position(objectView.getPosition());
+ GameObject object = currentModel.getObject(position)
+ .orElseThrow(() -> new RuntimeException("No object present at position " + position));
+
+ double distance = playerPos.distance(objectView.getPosition());
+ if (distance < 1.5) {
if (object instanceof InteractiveGameObject) {
((InteractiveGameObject) object).onAction();
}
@@ -175,11 +179,17 @@ public void onMouseClickedEvent(MouseClickedEvent event) {
if (object instanceof CollectibleGameObject) {
popupController.openTextImagePopup("Picked up an item!", objectView.getImage(), getWindowCenterX(), getWindowCenterY());
objectView.setVisible(false);
+ currentModel.removeGameObject(object);
}
}
+
logger.info("Controller notified on click from " + event.source());
}
+ public PlayerController getPlayerController() {
+ return playerController;
+ }
+
public static class Builder {
private final Controller controller;
diff --git a/src/main/java/io/rpg/controller/PlayerController.java b/src/main/java/io/rpg/controller/PlayerController.java
index cab82897..c3f5a8b2 100644
--- a/src/main/java/io/rpg/controller/PlayerController.java
+++ b/src/main/java/io/rpg/controller/PlayerController.java
@@ -16,6 +16,7 @@ public class PlayerController implements KeyboardEvent.Observer {
public PlayerController(Player player, GameObjectView playerView) {
this.player = player;
this.playerView = playerView;
+ playerView.bindToGameObject(player);
this.onChangeLocation = () -> {};
player.addGameObjectStateChangeObserver(playerView);
@@ -54,7 +55,7 @@ public void teleportTo(LocationModel model, LocationView view, Position playerPo
updateOnChangeLocation(model, view);
player.setPosition(playerPosition);
- model.setPlayer(player);
+ model.addGameObject(player);
view.addChild(playerView);
view.addKeyboardEventObserver(this);
}
@@ -63,6 +64,11 @@ private void updateOnChangeLocation(LocationModel model, LocationView view) {
this.onChangeLocation = () -> {
view.removeKeyboardEventObserver(this);
view.removeChild(this.playerView);
+ model.removeGameObject(player);
};
}
+
+ public Player getPlayer() {
+ return player;
+ }
}
diff --git a/src/main/java/io/rpg/model/data/Position.java b/src/main/java/io/rpg/model/data/Position.java
index 276655a3..8ca050f1 100644
--- a/src/main/java/io/rpg/model/data/Position.java
+++ b/src/main/java/io/rpg/model/data/Position.java
@@ -1,15 +1,15 @@
package io.rpg.model.data;
import java.util.Objects;
+import javafx.geometry.Point2D;
/**
* 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 class Position {
public final int row;
-
public final int col;
public Position(int row, int col) {
@@ -17,6 +17,10 @@ public Position(int row, int col) {
this.col = col;
}
+ public Position(Point2D point2D) {
+ this((int) Math.round(point2D.getY()), (int) Math.round(point2D.getX()));
+ }
+
public int getRow() {
return row;
}
diff --git a/src/main/java/io/rpg/model/data/Vector.java b/src/main/java/io/rpg/model/data/Vector.java
deleted file mode 100644
index 4bfdc61a..00000000
--- a/src/main/java/io/rpg/model/data/Vector.java
+++ /dev/null
@@ -1,25 +0,0 @@
-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;
- }
-
- public Vector(Position position) {
- // TODO THIS MOTHT
- int SCALE = 64;
- this.x = position.col * SCALE;
- this.y = position.row * SCALE;
- }
-
-}
diff --git a/src/main/java/io/rpg/model/location/LocationModel.java b/src/main/java/io/rpg/model/location/LocationModel.java
index 07da8020..73237b17 100644
--- a/src/main/java/io/rpg/model/location/LocationModel.java
+++ b/src/main/java/io/rpg/model/location/LocationModel.java
@@ -2,15 +2,18 @@
import io.rpg.model.data.LocationModelStateChange;
import io.rpg.model.data.Position;
-import io.rpg.model.object.Player;
import io.rpg.model.object.GameObject;
import io.rpg.util.Result;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javafx.beans.value.ChangeListener;
+import javafx.geometry.Point2D;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.UnmodifiableView;
-
-import java.util.*;
/**
* Represents single location in our game.
@@ -18,7 +21,9 @@
public class LocationModel implements LocationModelStateChange.Emitter {
private String tag;
private List gameObjects;
- private Player player;
+ private final HashMap> positionListeners;
+ private final HashMap positionGameObjectMap;
+ public final Point2D bounds;
private final Set locationModelStateChangeObservers;
@@ -30,45 +35,19 @@ public LocationModel(@NotNull String tag, @NotNull List gameObjects)
private LocationModel() {
this.locationModelStateChangeObservers = new LinkedHashSet<>();
- }
-
- public void setPlayer(@NotNull Player player) {
- this.player = player;
+ this.positionListeners = new HashMap<>();
+ this.positionGameObjectMap = new HashMap<>();
+ bounds = new Point2D(10, 10); // TODO: 09.05.2022 Implement loading from config
}
public String getTag() {
return tag;
}
- public Player getPlayer() {
- return player;
+ public Optional getObject(Position position) {
+ return Optional.ofNullable(positionGameObjectMap.get(position));
}
-
- public GameObject getObject(int row, int column) {
- GameObject object = gameObjects.stream().filter(gameObject -> gameObject.getPosition()
- .equals(new Position(row, column)))
- .findFirst().orElse(null);
- if (object == null) {
- throw new NullPointerException("No object found on (" + row + ", " + column + ")");
- }
- return object;
- }
-
- /**
- * Private setter for Builder usage only.
- *
- * @param tag tag for the location
- */
- private void setTag(String tag) {
- this.tag = tag;
- }
-
-// @UnmodifiableView
-// public List getGameObjects() {
-// return Collections.unmodifiableList(gameObjects);
-// }
-
/**
* Private setter for Builder usage only. Notice that ownership of {@link GameObject}s is not
* transferred to LocationModel.
@@ -78,6 +57,88 @@ private void setTag(String tag) {
*/
private void setGameObjects(List gameObjects) {
this.gameObjects = gameObjects;
+ gameObjects.forEach(this::registerGameObject);
+ gameObjects.forEach(g -> checkAndCorrectBoundsCrossing(g, g.getExactPosition()));
+ gameObjects.forEach(g -> positionGameObjectMap.put(g.getPosition(), g));
+ }
+
+ public void addGameObject(GameObject gameObject) {
+ gameObjects.add(gameObject);
+ positionGameObjectMap.put(gameObject.getPosition(), gameObject);
+ registerGameObject(gameObject);
+ checkAndCorrectBoundsCrossing(gameObject, gameObject.getExactPosition());
+ }
+
+ private void registerGameObject(GameObject gameObject) {
+ ChangeListener positionListener =
+ (observable, oldValue, newValue) -> onGameObjectPositionChange(gameObject, oldValue, newValue);
+ gameObject.getExactPositionProperty()
+ .addListener(positionListener);
+ positionListeners.put(gameObject, positionListener);
+ }
+
+ public void removeGameObject(GameObject gameObject) {
+ gameObjects.remove(gameObject);
+ positionGameObjectMap.remove(gameObject.getPosition());
+ unRegisterGameObject(gameObject);
+ }
+
+ private void unRegisterGameObject(GameObject gameObject) {
+ ChangeListener positionListener = positionListeners.remove(gameObject);
+ gameObject.getExactPositionProperty()
+ .removeListener(positionListener);
+ }
+
+ private void onGameObjectPositionChange(GameObject gameObject, Point2D oldPosition, Point2D newPosition) {
+ checkAndCorrectBoundsCrossing(gameObject, newPosition);
+
+ Position newPos = new Position(newPosition);
+ Position oldPos = new Position(oldPosition);
+ if (newPos.equals(oldPos)) {
+ return;
+ }
+
+ // Collision check
+ if (positionGameObjectMap.containsKey(newPos) && !positionGameObjectMap.get(newPos)
+ .equals(gameObject)) {
+ gameObject.setExactPosition(oldPosition);
+ return;
+ }
+
+ if (gameObject.equals(positionGameObjectMap.get(oldPos))) {
+ changeField(gameObject, oldPos, newPos);
+ }
+ }
+
+ private void changeField(GameObject gameObject, Position oldPos, Position newPos) {
+ positionGameObjectMap.remove(oldPos);
+ positionGameObjectMap.put(newPos, gameObject);
+ }
+
+
+ private void checkAndCorrectBoundsCrossing(GameObject gameObject, Point2D newPosition) {
+ Point2D boundPosition = getBoundPosition(newPosition);
+ if (boundPosition.equals(newPosition)) {
+ return;
+ }
+
+ gameObject.setExactPosition(boundPosition);
+ Point2D boundsCrossedDirection = newPosition.subtract(boundPosition)
+ .normalize();
+
+ emitBoundCrossedEvent(boundsCrossedDirection);
+ }
+
+ private void emitBoundCrossedEvent(Point2D boundsCrossedDirection) {
+ // TODO: 10.05.2022 Bound crossed action
+ System.out.println(boundsCrossedDirection);
+ }
+
+ private Point2D getBoundPosition(Point2D pos) {
+ double offset = 0.3; // it should be less than 0.5
+ double x = Math.max(-offset, Math.min(bounds.getX() - 1 + offset, pos.getX()));
+ double y = Math.max(-offset, Math.min(bounds.getY() - 1 + offset, pos.getY()));
+ return new Point2D(x, y);
}
@Override
@@ -97,6 +158,7 @@ public void emitLocationModelStateChange(LocationModelStateChange event) {
});
}
+
public Result validate() {
if (tag == null || gameObjects == null) {
return Result.error(null);
@@ -120,8 +182,7 @@ public Builder setGameObjects(@NotNull List gameObjects) {
}
public Builder setTag(@NotNull String tag) {
- assert tag != null : "Location tag must not be null!";
- locationModel.setTag(tag);
+ locationModel.tag = tag;
return this;
}
@@ -136,14 +197,5 @@ public LocationModel build() {
return locationModel;
}
}
- public List getGameObjects() {
- return gameObjects;
- }
-
- public void update(float elapsed){
- if(player!=null){
- player.update(elapsed);
- }
- }
}
diff --git a/src/main/java/io/rpg/model/object/GameObject.java b/src/main/java/io/rpg/model/object/GameObject.java
index 315f58d9..6aff9210 100644
--- a/src/main/java/io/rpg/model/object/GameObject.java
+++ b/src/main/java/io/rpg/model/object/GameObject.java
@@ -1,18 +1,17 @@
package io.rpg.model.object;
-import io.rpg.config.model.GameObjectConfig;
import io.rpg.model.data.GameObjectStateChange;
import io.rpg.model.data.Position;
-import io.rpg.model.data.Vector;
-import io.rpg.view.GameObjectView;
-import javafx.scene.image.ImageView;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
import java.lang.reflect.Field;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ObservableValue;
+import javafx.geometry.Point2D;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* Class representing common state properties for all
@@ -20,15 +19,10 @@
*/
public class GameObject implements GameObjectStateChange.Emitter {
-// protected Vector currentPosition;
-
-// public GameObjectView view;
-
/**
* Position of game object in model's representation of location.
*/
- @Nullable
- protected Position position;
+ private final SimpleObjectProperty exactPositionProperty;
/**
* Unique identifier of this game object.
@@ -37,20 +31,9 @@ public class GameObject implements GameObjectStateChange.Emitter {
@NotNull
private final String tag;
- /**
- *
- */
- @Nullable
- protected String assetPath;
-
@NotNull
private final Set stateChangeObservers;
- @Nullable
- public String getAssetPath() {
- return assetPath;
- }
-
/**
* Unique identifier of this game object.
*/
@@ -59,6 +42,13 @@ public String getTag() {
return tag;
}
+
+ public GameObject(@NotNull String tag, @NotNull Position position) {
+ this.tag = tag;
+ this.stateChangeObservers = new LinkedHashSet<>();
+ this.exactPositionProperty = new SimpleObjectProperty<>(new Point2D(position.col, position.row));
+ }
+
/**
* Position of game object in model's representation of location.
*
@@ -66,18 +56,20 @@ public String getTag() {
*/
@Nullable
public Position getPosition() {
- return position;
+ Point2D exactPosition = getExactPosition();
+ return new Position(exactPosition);
}
- public GameObject(@NotNull String tag, @NotNull Position position) {
- this(tag, position, "");
+ public void setExactPosition(Point2D position) {
+ exactPositionProperty.setValue(position);
}
- public GameObject(@NotNull String tag, @NotNull Position position, @NotNull String assetPath) {
- this.tag = tag;
- this.position = position;
- this.assetPath = assetPath;
- this.stateChangeObservers = new LinkedHashSet<>();
+ public Point2D getExactPosition() {
+ return exactPositionProperty.getValue();
+ }
+
+ public ObservableValue getExactPositionProperty() {
+ return exactPositionProperty;
}
@Override
@@ -96,6 +88,7 @@ public void removeGameObjectStateChangeObserver(GameObjectStateChange.Observer o
this.stateChangeObservers.remove(observer);
}
+
public String getFieldDescription() {
StringBuilder builder = new StringBuilder();
for (Field field : GameObject.class.getDeclaredFields()) {
@@ -120,6 +113,12 @@ public String toString() {
return builder.append("}").toString();
}
+ public void setPosition(Position playerPosition) {
+ setExactPosition(new Point2D(playerPosition.col, playerPosition.row));
+ }
+
+
+
public enum Type {
NAVIGABLE("navigable"),
DIALOG("dialog"),
diff --git a/src/main/java/io/rpg/model/object/Player.java b/src/main/java/io/rpg/model/object/Player.java
index 4f5b8e6e..9a0a9771 100644
--- a/src/main/java/io/rpg/model/object/Player.java
+++ b/src/main/java/io/rpg/model/object/Player.java
@@ -1,50 +1,35 @@
package io.rpg.model.object;
import io.rpg.model.data.Position;
-import io.rpg.model.data.Vector;
import io.rpg.view.GameObjectView;
-import io.rpg.model.object.GameObject;
-import javafx.scene.image.Image;
+import javafx.geometry.Point2D;
import org.jetbrains.annotations.NotNull;
public class Player extends GameObject {
- private Vector currentPosition;
private int strength;
private float speed;
- private Vector direction;
private boolean rightPressed;
private boolean leftPressed;
private boolean upPressed;
private boolean downPressed;
private GameObjectView gameObjectView;
- private Vector pixelPosition;
public Player(@NotNull String tag, @NotNull Position position, @NotNull String assetPath) {
- super(tag, position, assetPath);
- this.currentPosition = new Vector(position.col, position.row);
- this.speed = 100f;
- this.direction = new Vector(0, 0);
+ super(tag, position);
+ this.speed = 6f;
this.rightPressed = false;
this.leftPressed = false;
this.upPressed = false;
this.downPressed = false;
this.strength = 0;
- this.pixelPosition = new Vector(position);
}
public void updateStrength(int value) {
strength += value;
}
- public void setDirection(Vector direction) {
- this.direction = direction;
- }
-
- public Vector getPixelPosition() {
- return pixelPosition;
- }
public void update(float elapsed) {
float y = 0;
@@ -66,7 +51,11 @@ public void update(float elapsed) {
x += 1;
}
- this.pixelPosition = new Vector(this.pixelPosition.x + speed * x * elapsed / 1000, this.pixelPosition.y + speed * y * elapsed / 1000);
+ Point2D nextPosition = new Point2D(x, y)
+ .multiply(speed * elapsed / 1000)
+ .add(this.getExactPosition());
+
+ this.setExactPosition(nextPosition);
}
public void setRightPressed(boolean rightPressed) {
@@ -93,14 +82,4 @@ public void setGameObjectView(GameObjectView gameObjectView) {
this.gameObjectView = gameObjectView;
}
- public void render() {
- if (gameObjectView != null) {
- gameObjectView.setX(this.pixelPosition.x);
- gameObjectView.setY(this.pixelPosition.y);
- }
- }
-
- public void setPosition(Position position) {
- pixelPosition = new Vector(position);
- }
}
diff --git a/src/main/java/io/rpg/util/GameObjectFactory.java b/src/main/java/io/rpg/util/GameObjectFactory.java
index 2fd84ead..c9b38b82 100644
--- a/src/main/java/io/rpg/util/GameObjectFactory.java
+++ b/src/main/java/io/rpg/util/GameObjectFactory.java
@@ -1,13 +1,10 @@
package io.rpg.util;
import io.rpg.config.model.GameObjectConfig;
-import io.rpg.model.data.Vector;
import io.rpg.model.object.*;
-import javafx.scene.image.Image;
import java.util.LinkedList;
import java.util.List;
-import java.util.Locale;
/**
* Exposes collection of methods to create {@link io.rpg.model.object.GameObject} class instances.
@@ -20,11 +17,10 @@ public class GameObjectFactory {
* @return game object created based on information located in config
*/
public static GameObject fromConfig(GameObjectConfig config) {
- // TODO: add case for Player type here!
switch (GameObject.Type.valueOf(config.getTypeString().toUpperCase())) {
case COLLECTIBLE -> { return new CollectibleGameObject(config.getTag(), config.getPosition()); }
case DIALOG -> { return new DialogGameObject(config.getTag(), config.getPosition()); }
- case PLAYER -> { return new Player(config.getTag(), config.getPosition(),config.getAssetPath()); }
+ case PLAYER -> { return new Player(config.getTag(), config.getPosition(), config.getAssetPath()); }
case NAVIGABLE -> { return new NavigationalGameObject(config.getTag(), config.getPosition()); }
default -> throw new RuntimeException("Unknown GameObject type. This should not happen!");
}
diff --git a/src/main/java/io/rpg/view/DiscretePane.java b/src/main/java/io/rpg/view/DiscretePane.java
new file mode 100644
index 00000000..37e368c8
--- /dev/null
+++ b/src/main/java/io/rpg/view/DiscretePane.java
@@ -0,0 +1,72 @@
+package io.rpg.view;
+
+import javafx.collections.ListChangeListener;
+import javafx.geometry.HPos;
+import javafx.geometry.Point2D;
+import javafx.geometry.VPos;
+import javafx.scene.Node;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.Pane;
+
+/**
+ * Custom pane. It scales children to correct size and lays them in correct spot.
+ */
+public class DiscretePane extends Pane {
+ private static final double FIELD_SIZE = 64;
+
+ /**
+ * Creates a DiscretePane.
+ */
+ public DiscretePane() {
+ super();
+ getChildren().addListener((ListChangeListener) c -> {
+ while (c.next()) {
+ if (c.wasAdded()) {
+ c.getAddedSubList().forEach(this::modifyNewChild);
+ }
+ }
+ });
+ }
+
+ private void modifyNewChild(Node node) {
+ node.maxWidth(FIELD_SIZE);
+ node.maxHeight(FIELD_SIZE);
+ if (node instanceof ImageView imageView) {
+ imageView.setFitWidth(FIELD_SIZE);
+ imageView.setFitHeight(FIELD_SIZE);
+ }
+ }
+
+
+ @Override
+ protected void layoutChildren() {
+ double baselineOffset = 0;
+ for (Node child : getManagedChildren()) {
+
+ Point2D position = calcLayoutPosition(child);
+
+ layoutInArea(child,
+ position.getX(),
+ position.getY(),
+ FIELD_SIZE,
+ FIELD_SIZE,
+ baselineOffset,
+ HPos.LEFT,
+ VPos.TOP);
+ }
+ }
+
+ private Point2D calcLayoutPosition(Node child) {
+ double x;
+ double y;
+
+ if (child instanceof GameObjectView gameObjectView) {
+ return gameObjectView.getPosition().multiply(FIELD_SIZE);
+ } else {
+ x = child.getLayoutX();
+ y = child.getLayoutY();
+ }
+
+ return new Point2D(x, y);
+ }
+}
diff --git a/src/main/java/io/rpg/view/GameObjectView.java b/src/main/java/io/rpg/view/GameObjectView.java
index 2da8b341..5aad20e4 100644
--- a/src/main/java/io/rpg/view/GameObjectView.java
+++ b/src/main/java/io/rpg/view/GameObjectView.java
@@ -1,39 +1,46 @@
package io.rpg.view;
+import com.sun.javafx.scene.ImageViewHelper;
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 io.rpg.model.object.GameObject;
import java.nio.file.Path;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.geometry.Point2D;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import org.jetbrains.annotations.NotNull;
public class GameObjectView extends ImageView
implements MouseClickedEvent.Emitter, GameObjectStateChange.Observer {
private Path path;
private final Set onClickedObservers;
+ private final SimpleObjectProperty position;
- private final int SCALE = 64;
public GameObjectView(@NotNull Path assetPath, @NotNull Position position) {
this.path = assetPath;
-// String xdpath =
this.setImage(new Image(resolvePathToJFXFormat(path.toString())));
- // todo: better position class
- this.setX(position.col * SCALE);
- this.setY(position.row * SCALE);
-
- this.setPreserveRatio(true);
- this.setSmooth(false);
- this.setFitHeight(SCALE);
-
+ this.position = new SimpleObjectProperty<>(new Point2D(position.col, position.row));
+ setLayoutUpdateOnPositionChange();
this.onClickedObservers = new HashSet<>();
this.setOnMouseClicked(event -> emitOnMouseClickedEvent(new MouseClickedEvent(this, event)));
}
+ private void setLayoutUpdateOnPositionChange() {
+ this.position.addListener((observable, oldValue, newValue) -> {
+ if (Objects.equals(oldValue, newValue)) {
+ return;
+ }
+
+ ImageViewHelper.geomChanged(this);
+ });
+ }
+
public static String resolvePathToJFXFormat(String path) {
return "file:" + path;
}
@@ -45,6 +52,14 @@ public void emitOnMouseClickedEvent(MouseClickedEvent event) {
onClickedObservers.forEach(listener -> listener.onMouseClickedEvent(event));
}
+ public void bindToGameObject(GameObject gameObject) {
+ this.position.bind(gameObject.getExactPositionProperty());
+ }
+
+ public Point2D getPosition() {
+ return position.get();
+ }
+
@Override
public void addOnClickedObserver(MouseClickedEvent.Observer observer) {
onClickedObservers.add(observer);
diff --git a/src/main/java/io/rpg/view/LocationView.java b/src/main/java/io/rpg/view/LocationView.java
index 11889df2..7a343efe 100644
--- a/src/main/java/io/rpg/view/LocationView.java
+++ b/src/main/java/io/rpg/view/LocationView.java
@@ -1,16 +1,11 @@
package io.rpg.view;
-import io.rpg.Game;
-import io.rpg.Initializer;
-import io.rpg.config.model.GameObjectConfig;
import io.rpg.model.data.KeyboardEvent;
import io.rpg.model.data.LocationModelStateChange;
import io.rpg.model.location.LocationModel;
import io.rpg.model.object.GameObject;
import io.rpg.viewmodel.LocationViewModel;
import io.rpg.config.model.LocationConfig;
-import java.util.Collection;
-import java.util.Collections;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
@@ -31,7 +26,6 @@
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;
@@ -100,20 +94,13 @@ public void emitKeyboardEvent(KeyboardEvent 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
+ // most likely here we want to pass this event to LocationViewModel or even
// make LocationViewModel implement LocationModelStateChange.Observer
}
List gameObjectViews = new ArrayList<>();
- public void createViewsForObjects(LocationModel locationModel) {
- for (GameObject g : locationModel.getGameObjects()) {
- GameObjectView gameObjectView = new GameObjectView(Path.of(g.getAssetPath()), g.getPosition());
- gameObjectViews.add(gameObjectView);
- viewModel.getForegroundPane().getChildren().add(gameObjectView);
- }
- }
public void removeChild(GameObjectView view) {
viewModel.getForegroundPane().getChildren().remove(view);
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index b6cc828d..c4140771 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -18,6 +18,7 @@
opens io.rpg.model.data to com.google.gson;
opens io.rpg.viewmodel to javafx.fxml;
+ opens io.rpg.view to javafx.fxml;
opens io.rpg.config.model to com.google.gson;
exports io.rpg;
diff --git a/src/main/resources/io/rpg/viewmodel/location-view.fxml b/src/main/resources/io/rpg/viewmodel/location-view.fxml
index c567fec8..fecd5b24 100644
--- a/src/main/resources/io/rpg/viewmodel/location-view.fxml
+++ b/src/main/resources/io/rpg/viewmodel/location-view.fxml
@@ -5,6 +5,7 @@
+
@@ -14,7 +15,7 @@
-
+
diff --git a/src/test/java/io/rpg/config/ConfigLoaderTest.java b/src/test/java/io/rpg/config/ConfigLoaderTest.java
index a82168c2..c73c3163 100644
--- a/src/test/java/io/rpg/config/ConfigLoaderTest.java
+++ b/src/test/java/io/rpg/config/ConfigLoaderTest.java
@@ -79,7 +79,7 @@ public void gameWorldConfigIsLoadedProperly() {
// "tag": "player",
// "position": { "row": 4, "col": 5 },
// "type": "player",
- // "assetPath": "assets/stone.png",
+ // "assetPath": "assets/stone.png", (Removed)
// "location": "location-1"
// }
PlayerConfig actualPlayerConfig = config.getPlayerConfig();
@@ -87,6 +87,6 @@ public void gameWorldConfigIsLoadedProperly() {
Assertions.assertEquals("player", actualPlayerConfig.getTag());
Assertions.assertEquals("player", actualPlayerConfig.getTypeString());
Assertions.assertEquals(new Position(4, 5), actualPlayerConfig.getPosition());
- Assertions.assertEquals("assets/stone.png", actualPlayerConfig.getAssetPath());
+ // Assertions.assertEquals("assets/stone.png", actualPlayerConfig.getAssetPath());
}
}