diff --git a/assets/close-button.png b/assets/close-button.png new file mode 100644 index 00000000..a4461576 Binary files /dev/null and b/assets/close-button.png differ diff --git a/assets/left-arrow.png b/assets/left-arrow.png new file mode 100644 index 00000000..22ce5a4a Binary files /dev/null and b/assets/left-arrow.png differ diff --git a/assets/npc-frame.png b/assets/npc-frame.png new file mode 100644 index 00000000..134b70b7 Binary files /dev/null and b/assets/npc-frame.png differ diff --git a/assets/right-arrow.png b/assets/right-arrow.png new file mode 100644 index 00000000..c5f8a802 Binary files /dev/null and b/assets/right-arrow.png differ diff --git a/src/main/java/io/rpg/controller/Controller.java b/src/main/java/io/rpg/controller/Controller.java index dc5a7cf0..6599e3e9 100644 --- a/src/main/java/io/rpg/controller/Controller.java +++ b/src/main/java/io/rpg/controller/Controller.java @@ -2,6 +2,7 @@ import io.rpg.model.actions.Action; import io.rpg.model.actions.ActionConsumer; +import io.rpg.model.actions.DialogueAction; import io.rpg.model.actions.ShowDescriptionAction; import io.rpg.model.actions.GameEndAction; import io.rpg.model.actions.LocationChangeAction; @@ -108,6 +109,10 @@ private void onAction(LocationChangeAction action) { mainStage.setScene(nextView); } + private void onAction(DialogueAction action) { + popupController.openDialoguePopup(action.text, action.image, getWindowCenterX(), getWindowCenterY()); //TODO: load text from config + } + private void onAction(ShowDescriptionAction action) { if (!action.description.isEmpty()) { popupController.openTextImagePopup(action.description, action.image, getWindowCenterX(), getWindowCenterY()); diff --git a/src/main/java/io/rpg/controller/PopupController.java b/src/main/java/io/rpg/controller/PopupController.java index 212c8ad1..1aa3e67b 100644 --- a/src/main/java/io/rpg/controller/PopupController.java +++ b/src/main/java/io/rpg/controller/PopupController.java @@ -1,6 +1,7 @@ package io.rpg.controller; import io.rpg.model.object.Question; +import io.rpg.view.popups.DialoguePopup; import io.rpg.view.popups.QuestionPopup; import io.rpg.view.popups.TextImagePopup; import io.rpg.view.popups.TextPopup; @@ -8,7 +9,6 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; - public class PopupController { private final Stage popupStage = new Stage(StageStyle.TRANSPARENT); @@ -68,11 +68,21 @@ public void openQuestionPopup(Question question, int x, int y) { popupStage.setX(x - popupScene.getWidth() / 2); popupStage.setY(y - popupScene.getHeight() / 2); } + + public void openDialoguePopup(String text, Image npcImage, int x, int y) { + DialoguePopup popupScene = new DialoguePopup(text, npcImage); + popupStage.setScene(popupScene); + + popupStage.show(); + + popupStage.setX(x - popupScene.getWidth() / 2); + popupStage.setY(y - popupScene.getHeight() / 2); + + popupScene.setCloseButtonCallback(event -> popupStage.hide()); + } public void hidePopup() { popupStage.hide(); } - } - diff --git a/src/main/java/io/rpg/model/actions/DialogueAction.java b/src/main/java/io/rpg/model/actions/DialogueAction.java new file mode 100644 index 00000000..153c5827 --- /dev/null +++ b/src/main/java/io/rpg/model/actions/DialogueAction.java @@ -0,0 +1,16 @@ +package io.rpg.model.actions; + +import javafx.scene.image.Image; + +/** + * Class for storing local data needed to perform a dialogue action. + */ +public class DialogueAction implements Action { + public final String text; + public final Image image; + + public DialogueAction(String text, Image image) { + this.text = text; + this.image = image; + } +} diff --git a/src/main/java/io/rpg/model/object/GameObject.java b/src/main/java/io/rpg/model/object/GameObject.java index aceefc8a..ff53b35c 100644 --- a/src/main/java/io/rpg/model/object/GameObject.java +++ b/src/main/java/io/rpg/model/object/GameObject.java @@ -2,6 +2,7 @@ import io.rpg.model.actions.Action; import io.rpg.model.actions.BaseActionEmitter; +import io.rpg.model.actions.DialogueAction; import io.rpg.model.actions.QuizAction; import io.rpg.model.data.GameObjectStateChange; import io.rpg.model.data.Position; @@ -12,6 +13,7 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.geometry.Point2D; +import javafx.scene.image.Image; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/io/rpg/view/popups/DialoguePopup.java b/src/main/java/io/rpg/view/popups/DialoguePopup.java new file mode 100644 index 00000000..4276bbd1 --- /dev/null +++ b/src/main/java/io/rpg/view/popups/DialoguePopup.java @@ -0,0 +1,76 @@ +package io.rpg.view.popups; + +import io.rpg.viewmodel.DialoguePopupViewModel; +import javafx.event.EventHandler; +import javafx.fxml.FXMLLoader; +import javafx.scene.Group; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.input.MouseEvent; +import javafx.scene.paint.Color; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +public class DialoguePopup extends Scene { + + private final DialoguePopupViewModel viewModel; + private final List textPages; + private int currentPage = 0; + + public DialoguePopup(String text, Image npcImage, String backgroundPath, String npcFramePath) { + this(text, npcImage); + viewModel.setBackgroundImage(backgroundPath); + viewModel.setNpcFrameImage(npcFramePath); + } + + public DialoguePopup(String text, Image npcImage) { + super(new Group(), Color.TRANSPARENT); + FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(DialoguePopupViewModel.class.getResource("dialogue-popup-view.fxml"))); + Parent root = null; + + try { + root = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + this.setRoot(root); + + viewModel = loader.getController(); + viewModel.setNpcImage(npcImage); + this.setFill(Color.TRANSPARENT); + + textPages = List.of(text.split("(?<=\\G.{200})")); //split text into 200-letter pages + viewModel.setText(textPages.get(currentPage)); + if (textPages.size() == 1) { + viewModel.setNextVisibility(false); + } + viewModel.setPreviousVisibility(false); + viewModel.setNextButtonOnClick(event -> nextPage()); + viewModel.setPreviousButtonOnClick(event -> previousPage()); + } + + public void setCloseButtonCallback(EventHandler callback) { + viewModel.setCloseButtonOnClick(callback); + } + + public void nextPage() { + currentPage++; + viewModel.setText(textPages.get(currentPage)); + if (currentPage == textPages.size() - 1) { + viewModel.setNextVisibility(false); + } + viewModel.setPreviousVisibility(true); + } + + public void previousPage() { + currentPage--; + viewModel.setText(textPages.get(currentPage)); + if (currentPage == 0) { + viewModel.setPreviousVisibility(false); + } + viewModel.setNextVisibility(true); + } +} diff --git a/src/main/java/io/rpg/viewmodel/DialoguePopupViewModel.java b/src/main/java/io/rpg/viewmodel/DialoguePopupViewModel.java new file mode 100644 index 00000000..3e138757 --- /dev/null +++ b/src/main/java/io/rpg/viewmodel/DialoguePopupViewModel.java @@ -0,0 +1,84 @@ +package io.rpg.viewmodel; + +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; + +public class DialoguePopupViewModel { + + @FXML private Pane backgroundPane; + @FXML private StackPane npcFramePane; + @FXML private ImageView backgroundImage; + @FXML private ImageView npcImage; + @FXML private ImageView npcFrameImage; + @FXML private Text text; + @FXML private Button closeButton; + @FXML private Button nextButton; + @FXML private Button previousButton; + + public void setText(String text) { + this.text.setText(text); + } + + public void setNpcImage(Image image) { + npcImage.setImage(image); + } + + public void setBackgroundImage(String url) { + Image image = new Image(url); + backgroundImage.setImage(image); + } + + public void setCloseButtonImage(String url) { + ImageView imageView = new ImageView(url); + imageView.setFitWidth(16); + imageView.setPreserveRatio(true); + closeButton.setGraphic(imageView); + } + + public void setNextButtonImage(String url) { + ImageView imageView = new ImageView(url); + imageView.setFitWidth(26); + imageView.setPreserveRatio(true); + nextButton.setGraphic(imageView); + } + + public void setPreviousButtonImage(String url) { + ImageView imageView = new ImageView(url); + imageView.setFitWidth(26); + imageView.setPreserveRatio(true); + previousButton.setGraphic(imageView); + } + + public void setNpcFrameImage(String url) { + Image image = new Image(url); + npcFrameImage.setImage(image); + } + + public void setCloseButtonOnClick(EventHandler callback) { + closeButton.setOnMouseClicked(callback); + } + + public void setNextVisibility(boolean value) { + nextButton.setVisible(value); + } + + public void setPreviousVisibility(boolean value) { + previousButton.setVisible(value); + } + + public void setNextButtonOnClick(EventHandler callback) { + nextButton.setOnMouseClicked(callback); + } + + public void setPreviousButtonOnClick(EventHandler callback) { + previousButton.setOnMouseClicked(callback); + } +} diff --git a/src/main/resources/io/rpg/viewmodel/dialogue-popup-view.fxml b/src/main/resources/io/rpg/viewmodel/dialogue-popup-view.fxml new file mode 100644 index 00000000..21d2a3c5 --- /dev/null +++ b/src/main/resources/io/rpg/viewmodel/dialogue-popup-view.fxml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +