From dbd645e6d61b5f1a6e670cb15b590c20b75e492e Mon Sep 17 00:00:00 2001 From: Siphalor Date: Tue, 7 Jul 2020 22:51:15 +0200 Subject: [PATCH] A huge amount of work on GUI and Gameplay --- packs/test/lang/de_de.lang | 2 + .../java/de/siphalor/was/WhatAStorage.java | 79 +++++++- .../de/siphalor/was/assets/AssetsManager.java | 20 --- .../siphalor/was/content/product/Product.java | 22 ++- .../product/dynamic/DynamicProduct.java | 5 + .../java/de/siphalor/was/game/Storage.java | 4 +- .../de/siphalor/was/game/StorageSlot.java | 15 +- .../was/util/PersistentInputStream.java | 26 +++ .../de/siphalor/was/visual/CanvasVisual.java | 39 +++- .../de/siphalor/was/visual/JFXVisual.java | 129 +++++++++++++- .../java/de/siphalor/was/visual/Visual.java | 7 +- .../was/visual/jfx/DraggedProduct.java | 27 +++ .../de/siphalor/was/visual/jfx/JFXUtil.java | 25 +++ .../was/visual/jfx/MainController.java | 40 ++++- .../was/visual/jfx/QuestController.java | 62 ++++++- .../was/visual/jfx/StorageSlotController.java | 94 ++++++++++ src/main/resources/assets/jfx/game.fxml | 168 ++++++++++++++++++ src/main/resources/assets/jfx/main.css | 35 +++- src/main/resources/assets/jfx/main.fxml | 163 ----------------- .../resources/assets/jfx/quest_widget.fxml | 6 +- .../resources/assets/jfx/storage_slot.fxml | 65 +++++++ src/main/resources/content/lang/en_us.lang | 2 + .../content/textures/products/package.png | Bin 0 -> 3436 bytes .../content/textures/products/package.svg | 120 +++++++++++++ 24 files changed, 936 insertions(+), 219 deletions(-) create mode 100644 src/main/java/de/siphalor/was/util/PersistentInputStream.java create mode 100644 src/main/java/de/siphalor/was/visual/jfx/DraggedProduct.java create mode 100644 src/main/java/de/siphalor/was/visual/jfx/JFXUtil.java create mode 100644 src/main/java/de/siphalor/was/visual/jfx/StorageSlotController.java create mode 100644 src/main/resources/assets/jfx/game.fxml delete mode 100644 src/main/resources/assets/jfx/main.fxml create mode 100644 src/main/resources/assets/jfx/storage_slot.fxml create mode 100644 src/main/resources/content/textures/products/package.png create mode 100644 src/main/resources/content/textures/products/package.svg diff --git a/packs/test/lang/de_de.lang b/packs/test/lang/de_de.lang index 9a082b2..0c9bc7f 100644 --- a/packs/test/lang/de_de.lang +++ b/packs/test/lang/de_de.lang @@ -9,6 +9,8 @@ game.trash = M\u00fclltonne game.trash.hover = Zerst\u00f6ren game.storage = Lager game.balance.history = Buchungen +game.balance.history.no-data = Noch keine Buchungen +game.balance.history.index = game.balance.history.change = Wert in \u20ac game.balance.history.type = Beschreibung game.balance.history.type.abandon = Auftrag abgelehnt diff --git a/src/main/java/de/siphalor/was/WhatAStorage.java b/src/main/java/de/siphalor/was/WhatAStorage.java index 5b4b80e..1dbc0b9 100644 --- a/src/main/java/de/siphalor/was/WhatAStorage.java +++ b/src/main/java/de/siphalor/was/WhatAStorage.java @@ -4,13 +4,18 @@ import de.siphalor.was.content.ContentManager; import de.siphalor.was.content.lang.I18n; import de.siphalor.was.content.pack.FileContentPack; import de.siphalor.was.content.pack.JarContentPack; +import de.siphalor.was.content.product.Product; import de.siphalor.was.content.product.ProductManager; import de.siphalor.was.content.quest.Quest; import de.siphalor.was.content.quest.QuestGenerator; import de.siphalor.was.content.quest.QuestManager; import de.siphalor.was.game.Balance; +import de.siphalor.was.game.Storage; import de.siphalor.was.visual.JFXVisual; import de.siphalor.was.visual.Visual; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.nio.file.Path; @@ -19,6 +24,7 @@ import java.util.List; public class WhatAStorage { private static final int MAX_QUESTS = 3; + private static final int GRID_SIZE = 3; public static final String TITLE = "What a Storage"; private static final WhatAStorage INSTANCE = new WhatAStorage(); @@ -39,6 +45,7 @@ public class WhatAStorage { private Balance balance; private QuestGenerator questGenerator; private final List quests = new ArrayList<>(MAX_QUESTS); + private Storage storage; private WhatAStorage() { contentManager = new ContentManager(); @@ -94,6 +101,7 @@ public class WhatAStorage { public void loadGame() { questGenerator = questManager.get("normal"); quests.clear(); + storage = new Storage(GRID_SIZE, GRID_SIZE, GRID_SIZE); balance = new Balance(); } @@ -138,6 +146,75 @@ public class WhatAStorage { Quest quest = quests.remove(index); addTransaction(Balance.Transaction.ABANDON, -quest.getReward()); - visual.onQuestAbandoned(index); + visual.onQuestRemoved(index); + } + + public boolean canStoreProduct(@Nullable Product product, int x, int y) { + return product != null && storage.get(x, y).fits(product) && product.testY(y); + } + + public boolean canStoreProduct(int questIndex, int x, int y) { + return canStoreProduct(quests.get(questIndex).getProduct(), x, y); + } + + public boolean canMoveProduct(int x1, int y1, int x2, int y2) { + return canStoreProduct(storage.get(x1, y1).front(), x2, y2); + } + + public void storeProduct(int questIndex, int x, int y) { + Quest quest = quests.get(questIndex); + if (quest != null && quest.getType() == Quest.Type.IN) { + Product product = quest.getProduct(); + + int z = storage.get(x, y).add(product); + if (z >= 0 && product.testY(y)) { + quests.remove(questIndex); + addTransaction(Balance.Transaction.STORE, quest.getReward()); + visual.onQuestRemoved(questIndex); + visual.onProductSet(x, y, z, quest.getProduct()); + } + } + } + + public boolean canSellProduct(int questIndex, int x, int y) { + Quest quest = quests.get(questIndex); + if (quest != null && quest.getType() == Quest.Type.OUT) { + return quest.getProduct().equals(storage.get(x, y).front()); + } + return false; + } + + public boolean sellProduct(int questIndex, int x, int y) { + if (canSellProduct(questIndex, x, y)) { + int z = storage.get(x, y).pop(); + addTransaction(Balance.Transaction.SELL, quests.get(questIndex).getReward()); + quests.remove(questIndex); + visual.onQuestRemoved(questIndex); + visual.onProductCleared(x, y, z); + return true; + } + return false; + } + + public void moveProduct(int x1, int y1, int x2, int y2) { + Product product = storage.get(x1, y1).front(); + if (product != null && product.testY(y2)) { + int z2 = storage.get(x2, y2).add(product); + if (z2 >= 0) { + int z1 = storage.get(x1, y1).pop(); + + addTransaction(Balance.Transaction.MOVE, -100); + visual.onProductCleared(x1, y1, z1); + visual.onProductSet(x2, y2, z2, product); + } + } + } + + public void destroyProduct(int x, int y) { + int z = storage.get(x, y).pop(); + if (z >= 0) { + addTransaction(Balance.Transaction.DESTROY, -500); + visual.onProductCleared(x, y, z); + } } } diff --git a/src/main/java/de/siphalor/was/assets/AssetsManager.java b/src/main/java/de/siphalor/was/assets/AssetsManager.java index 9208353..462e2bc 100644 --- a/src/main/java/de/siphalor/was/assets/AssetsManager.java +++ b/src/main/java/de/siphalor/was/assets/AssetsManager.java @@ -13,10 +13,6 @@ import java.util.Map; import java.util.Optional; public class AssetsManager { - private static final Map imageCache = new HashMap<>(); - - @NotNull - public static final Image MISSINGNO = getImage("textures/missingno.png"); @NotNull public static Optional getStream(@NotNull String path) { @@ -28,20 +24,4 @@ public class AssetsManager { return Optional.ofNullable(Thread.currentThread().getContextClassLoader().getResource(path)); } - @NotNull - public static Image getImage(@NotNull String path) { - Image image = imageCache.get(path); - if (image == null) { - image = getStream(path).map(inputStream -> { - try { - return (Image) ImageIO.read(inputStream); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - }).orElse(MISSINGNO); - imageCache.put(path, image); - } - return image; - } } diff --git a/src/main/java/de/siphalor/was/content/product/Product.java b/src/main/java/de/siphalor/was/content/product/Product.java index 84a7e63..2e55657 100644 --- a/src/main/java/de/siphalor/was/content/product/Product.java +++ b/src/main/java/de/siphalor/was/content/product/Product.java @@ -1,8 +1,9 @@ package de.siphalor.was.content.product; -import de.siphalor.was.content.lang.I18n; import org.jetbrains.annotations.NotNull; +import java.util.ResourceBundle; + public interface Product { @NotNull String getPropertySpecifier(); @@ -14,15 +15,20 @@ public interface Product { @NotNull ProductType getType(); + public boolean equals(Product product); + + @NotNull default String getTranslationKey() { return "products." + getType().getId(); } - default String getName(I18n i18n) { + @NotNull + default String getName(@NotNull ResourceBundle i18n) { return i18n.getString(getTranslationKey()); } - default String getDescription(I18n i18n) { + @NotNull + default String getDescription(@NotNull ResourceBundle i18n) { String[] props = getType().getProperties(); String[] values = getProperties(); @@ -39,4 +45,14 @@ public interface Product { return res.toString(); } + + @NotNull + default String getTextureLocation() { + return "textures/products/" + getType().getId() + "/" + getPropertySpecifier(); + } + + @NotNull + static String getPlaceholderTextureLocation() { + return "textures/products/package.png"; + } } diff --git a/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java b/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java index 00b2a0b..b904ab0 100644 --- a/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java +++ b/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java @@ -55,6 +55,11 @@ public class DynamicProduct implements Product { return type; } + @Override + public boolean equals(Product product) { + return super.equals(product); + } + public void setYPredicate(@NotNull IntPredicate yPredicate) { this.yPredicate = yPredicate; } diff --git a/src/main/java/de/siphalor/was/game/Storage.java b/src/main/java/de/siphalor/was/game/Storage.java index ea4cf16..965a735 100644 --- a/src/main/java/de/siphalor/was/game/Storage.java +++ b/src/main/java/de/siphalor/was/game/Storage.java @@ -21,12 +21,12 @@ public class Storage { return slots[x][y]; } - public boolean move(int x1, int y1, int x2, int y2) { + public int move(int x1, int y1, int x2, int y2) { StorageSlot slot1 = get(x1, y1); Product product = slot1.front(); if (product != null) { return get(x2, y2).add(product); } - return false; + return -1; } } diff --git a/src/main/java/de/siphalor/was/game/StorageSlot.java b/src/main/java/de/siphalor/was/game/StorageSlot.java index ca710a8..5ab57c3 100644 --- a/src/main/java/de/siphalor/was/game/StorageSlot.java +++ b/src/main/java/de/siphalor/was/game/StorageSlot.java @@ -29,16 +29,15 @@ public class StorageSlot { return null; } - @Nullable - public Product pop() { + public int pop() { for (int i = products.length - 1; i >= 0; i--) { Product product = products[i]; if (product != null) { products[i] = null; - return product; + return i; } } - return null; + return -1; } public boolean fits(@NotNull Product product) { @@ -51,7 +50,7 @@ public class StorageSlot { return product.getDepth() <= depth - blocked; } - public boolean add(@NotNull Product product) { + public int add(@NotNull Product product) { int blocked = 0; int i; for (i = 0; i < products.length; i++) { @@ -61,13 +60,13 @@ public class StorageSlot { } blocked += p.getDepth(); if (blocked >= depth) { - return false; + return -1; } } if (i < products.length && product.getDepth() <= depth - blocked) { products[i] = product; - return true; + return i; } - return false; + return -1; } } diff --git a/src/main/java/de/siphalor/was/util/PersistentInputStream.java b/src/main/java/de/siphalor/was/util/PersistentInputStream.java new file mode 100644 index 0000000..a7ed25d --- /dev/null +++ b/src/main/java/de/siphalor/was/util/PersistentInputStream.java @@ -0,0 +1,26 @@ +package de.siphalor.was.util; + +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Taken from here: https://stackoverflow.com/questions/924990/how-to-cache-inputstream-for-multiple-use#1303314 + */ +public class PersistentInputStream extends BufferedInputStream { + public PersistentInputStream(@NotNull InputStream in) { + super(in); + super.mark(Integer.MAX_VALUE); + } + + @Override + public void close() throws IOException { + super.reset(); + } + + public void realClose() throws IOException { + super.close(); + } +} diff --git a/src/main/java/de/siphalor/was/visual/CanvasVisual.java b/src/main/java/de/siphalor/was/visual/CanvasVisual.java index 5f08a9e..49e746d 100644 --- a/src/main/java/de/siphalor/was/visual/CanvasVisual.java +++ b/src/main/java/de/siphalor/was/visual/CanvasVisual.java @@ -1,17 +1,27 @@ package de.siphalor.was.visual; import de.siphalor.was.WhatAStorage; +import de.siphalor.was.assets.AssetsManager; +import de.siphalor.was.content.product.Product; import de.siphalor.was.content.quest.Quest; import de.siphalor.was.game.Balance; import de.siphalor.was.visual.canvas.layout.FixedAspectLayout; import de.siphalor.was.visual.canvas.layout.FulfillingLayout; +import org.jetbrains.annotations.NotNull; +import javax.imageio.ImageIO; import java.awt.*; import java.awt.event.*; import java.awt.image.BufferStrategy; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; public class CanvasVisual implements Visual { + @NotNull + public static final Image MISSINGNO = getImage("textures/missingno.png"); private static final double ASPECT_RATIO = 16.0 / 9.0; + private static final Map imageCache = new HashMap<>(); private WhatAStorage main; private final Frame frame = new Frame(WhatAStorage.TITLE); @@ -21,6 +31,23 @@ public class CanvasVisual implements Visual { private boolean fullScreen = false; + @NotNull + public static Image getImage(@NotNull String path) { + Image image = imageCache.get(path); + if (image == null) { + image = AssetsManager.getStream(path).map(inputStream -> { + try { + return (Image) ImageIO.read(inputStream); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + }).orElse(MISSINGNO); + imageCache.put(path, image); + } + return image; + } + @Override public void setup(WhatAStorage whatAStorage) { main = whatAStorage; @@ -138,7 +165,17 @@ public class CanvasVisual implements Visual { } @Override - public void onQuestAbandoned(int index) { + public void onQuestRemoved(int index) { + + } + + @Override + public void onProductSet(int x, int y, int z, Product product) { + + } + + @Override + public void onProductCleared(int x, int y, int z) { } } diff --git a/src/main/java/de/siphalor/was/visual/JFXVisual.java b/src/main/java/de/siphalor/was/visual/JFXVisual.java index 763d2dc..000fac4 100644 --- a/src/main/java/de/siphalor/was/visual/JFXVisual.java +++ b/src/main/java/de/siphalor/was/visual/JFXVisual.java @@ -2,14 +2,17 @@ package de.siphalor.was.visual; import de.siphalor.was.WhatAStorage; import de.siphalor.was.assets.AssetsManager; +import de.siphalor.was.content.ContentManager; import de.siphalor.was.content.lang.I18n; import de.siphalor.was.content.product.Product; import de.siphalor.was.content.quest.Quest; +import de.siphalor.was.content.resource.Resource; import de.siphalor.was.game.Balance; -import de.siphalor.was.util.Pair; +import de.siphalor.was.util.PersistentInputStream; import de.siphalor.was.visual.jfx.BalanceEntry; import de.siphalor.was.visual.jfx.MainController; import de.siphalor.was.visual.jfx.QuestController; +import de.siphalor.was.visual.jfx.StorageSlotController; import javafx.application.Application; import javafx.collections.ObservableList; import javafx.fxml.FXMLLoader; @@ -20,10 +23,14 @@ import javafx.scene.chart.XYChart; import javafx.scene.control.TableColumn; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.Image; +import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.stage.Stage; +import org.jetbrains.annotations.NotNull; import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; public class JFXVisual extends Application implements Visual { private static Stage primaryStage; @@ -33,6 +40,20 @@ public class JFXVisual extends Application implements Visual { private static MainController controller; + @NotNull + public static Image getProductImage(@NotNull Product product, @NotNull ContentManager contentManager) { + return loadImage(product.getTextureLocation(), contentManager).orElseGet(() -> loadImage(Product.getPlaceholderTextureLocation(), contentManager).orElseThrow()); + } + @NotNull + public static Optional loadImage(@NotNull String location, @NotNull ContentManager contentManager) { + Optional optResource = contentManager.getResource(location); + return optResource.map(resource -> { + InputStream inputStream = resource.getInputStream(); + if (inputStream == null) return null; + return new Image(inputStream); + }); + } + @Override public void setup(WhatAStorage whatAStorage) { } @@ -52,7 +73,7 @@ public class JFXVisual extends Application implements Visual { I18n i18n = I18n.getInstance(); controller.budgetLabel.setText(i18n.format("game.budget", budget)); - if (budget < 0) { + if (budget < 0 && !controller.budgetLabel.getStyleClass().contains("red")) { controller.budgetLabel.getStyleClass().add("red"); } else { controller.budgetLabel.getStyleClass().remove("red"); @@ -79,7 +100,7 @@ public class JFXVisual extends Application implements Visual { } @Override - public void onQuestAbandoned(int index) { + public void onQuestRemoved(int index) { controller.questBox.getChildren().remove(index); controller.nextQuestButton.setDisable(false); @@ -95,7 +116,7 @@ public class JFXVisual extends Application implements Visual { controller = new MainController(WhatAStorage.getInstance()); loader.setController(controller); - Pane pane = loader.load(AssetsManager.getStream("jfx/main.fxml").get()); + Pane pane = loader.load(AssetsManager.getStream("jfx/test.fxml").get()); mainScene = new Scene(pane); mainScene.getStylesheets().add("assets/jfx/main.css"); @@ -114,6 +135,8 @@ public class JFXVisual extends Application implements Visual { public void loadMainScene() { primaryStage.setScene(mainScene); + prepareStorageGrid(); + AssetsManager.getStream("textures/bin_closed.png").ifPresent(inputStream -> { controller.trash.setImage(new Image(inputStream)); }); @@ -122,11 +145,11 @@ public class JFXVisual extends Application implements Visual { onBalanceChanged(0, Balance.Transaction.NOOP, 0, 0, 0); } - public void addQuest(Quest quest) { + private void addQuest(Quest quest) { AssetsManager.getStream("jfx/quest_widget.fxml").ifPresentOrElse(is -> { try { FXMLLoader loader = new FXMLLoader(); - QuestController questController = new QuestController(WhatAStorage.getInstance()); + QuestController questController = new QuestController(quest.getType()); loader.setController(questController); Parent parent = loader.load(is); @@ -135,12 +158,17 @@ public class JFXVisual extends Application implements Visual { Product product = quest.getProduct(); I18n i18n = I18n.getInstance(); + questController.title.setText(product.getName(i18n)); - questController.description.setText(product.getDescription(i18n)); - questController.reward.setText(i18n.format("game.quest.reward", quest.getReward())); + questController.image.setImage(getProductImage(product, WhatAStorage.getInstance().getContentManager())); + + if (quest.getType() == Quest.Type.OUT) { + parent.getStyleClass().add("quest-out"); + } + } catch (IOException e) { e.printStackTrace(); } @@ -148,4 +176,89 @@ public class JFXVisual extends Application implements Visual { System.out.println("INTERNAL ERROR: Failed to load quest widget"); }); } + + private void prepareStorageGrid() { + GridPane storageGrid = controller.storageGrid; + storageGrid.getChildren().clear(); + + PersistentInputStream inputStream = new PersistentInputStream(AssetsManager.getStream("jfx/storage_slot.fxml").orElseThrow()); + + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + FXMLLoader loader = new FXMLLoader(); + StorageSlotController storageSlotController = new StorageSlotController(x, y); + loader.setController(storageSlotController); + loader.setResources(I18n.getInstance()); + + try { + Parent parent = loader.load(inputStream); + inputStream.reset(); + + storageGrid.add(parent, x, y); + controller.storageSlotControllers[y][x] = storageSlotController; + + // TODO + //storageSlotController.slot0.setImage(loadImage("textures/products/package.png", WhatAStorage.getInstance().getContentManager()).orElseThrow()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + try { + inputStream.realClose(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void onProductSet(int x, int y, int z, Product product) { + ContentManager contentManager = WhatAStorage.getInstance().getContentManager(); + I18n i18n = I18n.getInstance(); + + StorageSlotController slot = controller.storageSlotControllers[y][x]; + switch (z) { + default: + case 0: + slot.slot0.setImage(getProductImage(product, contentManager)); + slot.slot0Description.setText(product.getDescription(i18n)); + slot.slot0Title.setText(product.getName(i18n)); + break; + case 1: + slot.slot1.setImage(getProductImage(product, contentManager)); + slot.slot1Description.setText(product.getDescription(i18n)); + slot.slot1Title.setText(product.getName(i18n)); + break; + case 2: + slot.slot2.setImage(getProductImage(product, contentManager)); + slot.slot2Description.setText(product.getDescription(i18n)); + slot.slot2Title.setText(product.getName(i18n)); + break; + } + } + + @Override + public void onProductCleared(int x, int y, int z) { + StorageSlotController slot = controller.storageSlotControllers[y][x]; + String empty = I18n.getInstance().getString("game.storage.empty"); + switch (z) { + default: + case 0: + slot.slot0.setImage(null); + slot.slot0Description.setText(null); + slot.slot0Title.setText(empty); + break; + case 1: + slot.slot1.setImage(null); + slot.slot1Description.setText(null); + slot.slot1Title.setText(empty); + break; + case 2: + slot.slot2.setImage(null); + slot.slot2Description.setText(null); + slot.slot2Title.setText(empty); + break; + } + } } diff --git a/src/main/java/de/siphalor/was/visual/Visual.java b/src/main/java/de/siphalor/was/visual/Visual.java index 4813e2e..3101130 100644 --- a/src/main/java/de/siphalor/was/visual/Visual.java +++ b/src/main/java/de/siphalor/was/visual/Visual.java @@ -1,6 +1,7 @@ package de.siphalor.was.visual; import de.siphalor.was.WhatAStorage; +import de.siphalor.was.content.product.Product; import de.siphalor.was.content.quest.Quest; import de.siphalor.was.game.Balance; @@ -12,5 +13,9 @@ public interface Visual { void onBalanceChanged(int budget, Balance.Transaction transaction, int change, int totalIncome, int totalLoss); void onQuestAdded(Quest newQuest, boolean canCreateMore); - void onQuestAbandoned(int index); + void onQuestRemoved(int index); + + void onProductSet(int x, int y, int z, Product product); + + void onProductCleared(int x, int y, int z); } diff --git a/src/main/java/de/siphalor/was/visual/jfx/DraggedProduct.java b/src/main/java/de/siphalor/was/visual/jfx/DraggedProduct.java new file mode 100644 index 0000000..b5cde70 --- /dev/null +++ b/src/main/java/de/siphalor/was/visual/jfx/DraggedProduct.java @@ -0,0 +1,27 @@ +package de.siphalor.was.visual.jfx; + +import javafx.scene.input.DataFormat; + +import java.io.Serializable; + +interface DraggedProduct extends Serializable { + DataFormat FORMAT = new DataFormat("application/java-was-jfx-product-move"); + + class Quest implements DraggedProduct { + public final int index; + + public Quest(int index) { + this.index = index; + } + } + + class Slot implements DraggedProduct { + public final int x; + public final int y; + + public Slot(int x, int y) { + this.x = x; + this.y = y; + } + } +} diff --git a/src/main/java/de/siphalor/was/visual/jfx/JFXUtil.java b/src/main/java/de/siphalor/was/visual/jfx/JFXUtil.java new file mode 100644 index 0000000..46fe9ca --- /dev/null +++ b/src/main/java/de/siphalor/was/visual/jfx/JFXUtil.java @@ -0,0 +1,25 @@ +package de.siphalor.was.visual.jfx; + +import javafx.scene.image.Image; +import javafx.scene.image.PixelReader; +import javafx.scene.image.PixelWriter; +import javafx.scene.image.WritableImage; + +public class JFXUtil { + public static Image scaleTo(Image image, int width) { + double scale = image.getWidth() / (double) width; + int height = (int) (image.getHeight() / scale); + + WritableImage res = new WritableImage(width, height); + PixelReader reader = image.getPixelReader(); + PixelWriter writer = res.getPixelWriter(); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + writer.setColor(x, y, reader.getColor((int) (x * scale), (int) (y * scale))); + } + } + + return res; + } +} diff --git a/src/main/java/de/siphalor/was/visual/jfx/MainController.java b/src/main/java/de/siphalor/was/visual/jfx/MainController.java index f3f49cc..9a06b19 100644 --- a/src/main/java/de/siphalor/was/visual/jfx/MainController.java +++ b/src/main/java/de/siphalor/was/visual/jfx/MainController.java @@ -1,7 +1,6 @@ package de.siphalor.was.visual.jfx; import de.siphalor.was.WhatAStorage; -import de.siphalor.was.util.Pair; import javafx.fxml.FXML; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; @@ -10,8 +9,12 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableView; import javafx.scene.image.ImageView; +import javafx.scene.input.DragEvent; +import javafx.scene.input.TransferMode; +import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; +@SuppressWarnings("unused") public class MainController { private final WhatAStorage was; @@ -27,6 +30,9 @@ public class MainController { public VBox questBox; public Button nextQuestButton; + public GridPane storageGrid; + public StorageSlotController[][] storageSlotControllers = new StorageSlotController[3][3]; + public MainController(WhatAStorage was) { this.was = was; } @@ -40,4 +46,36 @@ public class MainController { private void nextQuest() { was.requestQuest(); } + + @FXML + private void onTrashDragOver(DragEvent dragEvent) { + if (dragEvent.getDragboard().hasContent(DraggedProduct.FORMAT)) { + dragEvent.acceptTransferModes(TransferMode.MOVE); + } + + dragEvent.consume(); + } + + @FXML + private void onTrashDragDropped(DragEvent dragEvent) { + if (dragEvent.getDragboard().hasContent(DraggedProduct.FORMAT)) { + DraggedProduct origin = (DraggedProduct) dragEvent.getDragboard().getContent(DraggedProduct.FORMAT); + + if (origin instanceof DraggedProduct.Quest) { + DraggedProduct.Quest questOrigin = (DraggedProduct.Quest) origin; + was.abandonQuest(questOrigin.index); + + dragEvent.setDropCompleted(true); + dragEvent.consume(); + return; + } else if (origin instanceof DraggedProduct.Slot) { + was.destroyProduct(((DraggedProduct.Slot) origin).x, ((DraggedProduct.Slot) origin).y); + + dragEvent.setDropCompleted(true); + dragEvent.consume(); + return; + } + } + dragEvent.setDropCompleted(false); + } } diff --git a/src/main/java/de/siphalor/was/visual/jfx/QuestController.java b/src/main/java/de/siphalor/was/visual/jfx/QuestController.java index d8cf9a8..475fbce 100644 --- a/src/main/java/de/siphalor/was/visual/jfx/QuestController.java +++ b/src/main/java/de/siphalor/was/visual/jfx/QuestController.java @@ -1,13 +1,16 @@ package de.siphalor.was.visual.jfx; import de.siphalor.was.WhatAStorage; +import de.siphalor.was.content.quest.Quest; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.image.ImageView; +import javafx.scene.input.*; import javafx.scene.layout.GridPane; +@SuppressWarnings("unused") public class QuestController { - private final WhatAStorage was; + private final Quest.Type type; @FXML private GridPane questContainer; @@ -18,13 +21,62 @@ public class QuestController { public ImageView image; - public QuestController(WhatAStorage was) { - this.was = was; + public QuestController(Quest.Type type) { + this.type = type; } @FXML private void abandon() { - int index = questContainer.getParent().getChildrenUnmodifiable().indexOf(questContainer); - was.abandonQuest(index); + WhatAStorage.getInstance().abandonQuest(getIndex()); + } + + @FXML + private void onDragDetected(MouseEvent event) { + if (type == Quest.Type.IN) { + Dragboard dragboard = questContainer.startDragAndDrop(TransferMode.MOVE); + dragboard.setDragView(JFXUtil.scaleTo(image.getImage(), 100)); + + ClipboardContent content = new ClipboardContent(); + content.put(DraggedProduct.FORMAT, new DraggedProduct.Quest(getIndex())); + dragboard.setContent(content); + } + + event.consume(); + } + + @FXML + private void onDragOver(DragEvent dragEvent) { + Dragboard dragboard = dragEvent.getDragboard(); + if (dragboard.hasContent(DraggedProduct.FORMAT)) { + DraggedProduct product = (DraggedProduct) dragboard.getContent(DraggedProduct.FORMAT); + if (product instanceof DraggedProduct.Slot) { + DraggedProduct.Slot slot = (DraggedProduct.Slot) product; + if (WhatAStorage.getInstance().canSellProduct(getIndex(), slot.x, slot.y)) { + dragEvent.acceptTransferModes(TransferMode.MOVE); + dragEvent.consume(); + } + } + } + } + + @FXML + private void onDragDropped(DragEvent dragEvent) { + Dragboard dragboard = dragEvent.getDragboard(); + if (dragboard.hasContent(DraggedProduct.FORMAT)) { + DraggedProduct product = (DraggedProduct) dragboard.getContent(DraggedProduct.FORMAT); + if (product instanceof DraggedProduct.Slot) { + DraggedProduct.Slot slot = (DraggedProduct.Slot) product; + if (WhatAStorage.getInstance().sellProduct(getIndex(), slot.x, slot.y)) { + dragEvent.setDropCompleted(true); + dragEvent.consume(); + return; + } + } + } + dragEvent.setDropCompleted(false); + } + + private int getIndex() { + return questContainer.getParent().getChildrenUnmodifiable().indexOf(questContainer); } } diff --git a/src/main/java/de/siphalor/was/visual/jfx/StorageSlotController.java b/src/main/java/de/siphalor/was/visual/jfx/StorageSlotController.java new file mode 100644 index 0000000..74fa1d9 --- /dev/null +++ b/src/main/java/de/siphalor/was/visual/jfx/StorageSlotController.java @@ -0,0 +1,94 @@ +package de.siphalor.was.visual.jfx; + +import de.siphalor.was.WhatAStorage; +import javafx.fxml.FXML; +import javafx.scene.Parent; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.*; + +@SuppressWarnings("unused") +public class StorageSlotController { + private final int x; + private final int y; + + @FXML + private Parent slot; + + public ImageView slot0; + public Label slot0Title; + public Label slot0Description; + + public ImageView slot1; + public Label slot1Title; + public Label slot1Description; + + public ImageView slot2; + public Label slot2Title; + public Label slot2Description; + + public StorageSlotController(int x, int y) { + this.x = x; + this.y = y; + } + + @FXML + private void onDragDetected(MouseEvent event) { + int index; + Image image; + if (slot2.getImage() != null) { + image = JFXUtil.scaleTo(slot2.getImage(), 100); + } else if (slot1.getImage() != null) { + image = JFXUtil.scaleTo(slot1.getImage(), 100); + } else if (slot0.getImage() != null){ + image = JFXUtil.scaleTo(slot0.getImage(), 100); + } else { + event.consume(); + return; + } + + Dragboard dragboard = slot.startDragAndDrop(TransferMode.MOVE); + dragboard.setDragView(image); + ClipboardContent content = new ClipboardContent(); + content.put(DraggedProduct.FORMAT, new DraggedProduct.Slot(x, y)); + dragboard.setContent(content); + + event.consume(); + } + + @FXML + private void onDragOver(DragEvent dragEvent) { + if (dragEvent.getGestureSource() != slot && dragEvent.getDragboard().hasContent(DraggedProduct.FORMAT)) { + DraggedProduct origin = (DraggedProduct) dragEvent.getDragboard().getContent(DraggedProduct.FORMAT); + if (origin instanceof DraggedProduct.Quest) { + if (WhatAStorage.getInstance().canStoreProduct(((DraggedProduct.Quest) origin).index, x, y)) { + dragEvent.acceptTransferModes(TransferMode.MOVE); + } + } else if (origin instanceof DraggedProduct.Slot){ + if (WhatAStorage.getInstance().canMoveProduct(((DraggedProduct.Slot) origin).x, ((DraggedProduct.Slot) origin).y, x, y)) { + dragEvent.acceptTransferModes(TransferMode.MOVE); + } + } + } + dragEvent.consume(); + } + + @FXML + private void onDragDropped(DragEvent dragEvent) { + if (dragEvent.getGestureSource() != slot && dragEvent.getDragboard().hasContent(DraggedProduct.FORMAT)) { + DraggedProduct origin = (DraggedProduct) dragEvent.getDragboard().getContent(DraggedProduct.FORMAT); + if (origin instanceof DraggedProduct.Quest) { + WhatAStorage.getInstance().storeProduct(((DraggedProduct.Quest) origin).index, x, y); + dragEvent.setDropCompleted(true); + return; + } else if (origin instanceof DraggedProduct.Slot) { + DraggedProduct.Slot slot = (DraggedProduct.Slot) origin; + WhatAStorage.getInstance().moveProduct(slot.x, slot.y, x, y); + dragEvent.setDropCompleted(true); + return; + } + } + dragEvent.setDropCompleted(false); + } +} diff --git a/src/main/resources/assets/jfx/game.fxml b/src/main/resources/assets/jfx/game.fxml new file mode 100644 index 0000000..7e08f6b --- /dev/null +++ b/src/main/resources/assets/jfx/game.fxml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/assets/jfx/main.css b/src/main/resources/assets/jfx/main.css index 6c72d3d..836e694 100644 --- a/src/main/resources/assets/jfx/main.css +++ b/src/main/resources/assets/jfx/main.css @@ -27,16 +27,21 @@ Label.green { } .side-pane { - -fx-border-width: 0 1; - -fx-border-color: #aaaaaa; -fx-background-color: linear-gradient(to bottom, #dddddd, #aaaaaa); } +.quest-bar { + -fx-border-width: 0 0 1 0; + -fx-border-color: #aaaaaa; +} .quest-container { - -fx-border-width: 1 0 0 0; + -fx-border-width: 0 0 1 0; -fx-border-color: #aaaaaa; -fx-background-color: #eeeeee; } +.quest-container.quest-out ImageView { + -fx-blend-mode: hard-light; +} .quest-reward { -fx-text-fill: #457229; } @@ -49,7 +54,31 @@ Label.green { .table-column { -fx-padding: 0 5; } +.split-pane:horizontal > .split-pane-divider { + -fx-padding: 0 1 0 1; + -fx-background-color: #aaaaaa; +} +.tab-pane:top > .tab-header-area { + -fx-min-height: 34; + -fx-pref-height: 34; +} +.tab-pane:top > .tab-header-area > .headers-region > .tab { + -fx-padding: 4; + -fx-end-margin: 5; +} +.tab-pane:top > .tab-header-area > .headers-region > .tab .focus-indicator { + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} #main { -fx-background-color: #dddddd; } + +#storage-grid > * { + -fx-border-color: #dddddd; + -fx-border-width: 1; +} +.storage-slot-item-title { + -fx-end-margin: 10; +} diff --git a/src/main/resources/assets/jfx/main.fxml b/src/main/resources/assets/jfx/main.fxml deleted file mode 100644 index 4d66a7a..0000000 --- a/src/main/resources/assets/jfx/main.fxml +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/assets/jfx/quest_widget.fxml b/src/main/resources/assets/jfx/quest_widget.fxml index 19c6a30..94dd3bd 100644 --- a/src/main/resources/assets/jfx/quest_widget.fxml +++ b/src/main/resources/assets/jfx/quest_widget.fxml @@ -10,7 +10,7 @@ - + @@ -20,9 +20,9 @@ - + - +