diff --git a/assets/popup-background-3.png b/assets/popup-background-3.png new file mode 100644 index 00000000..c35951ba Binary files /dev/null and b/assets/popup-background-3.png differ diff --git a/src/main/java/io/rpg/controller/Controller.java b/src/main/java/io/rpg/controller/Controller.java index 13d258b0..c4bebae8 100644 --- a/src/main/java/io/rpg/controller/Controller.java +++ b/src/main/java/io/rpg/controller/Controller.java @@ -7,10 +7,7 @@ import io.rpg.model.data.Position; import io.rpg.model.data.Vector; import io.rpg.model.location.LocationModel; -import io.rpg.model.object.CollectibleGameObject; -import io.rpg.model.object.GameObject; -import io.rpg.model.object.InteractiveGameObject; -import io.rpg.model.object.Player; +import io.rpg.model.object.*; import io.rpg.util.Result; import io.rpg.view.GameObjectView; import io.rpg.view.LocationView; @@ -147,6 +144,7 @@ public void onKeyboardEvent(KeyboardEvent event) { switch (payload.getCode()) { case F -> popupController.openPointsPopup(5, getWindowCenterX(), getWindowCenterY()); case G -> popupController.openTextPopup("Hello!", getWindowCenterX(), getWindowCenterY()); + case Q -> popupController.openQuestionPopup(new Question("How many bits are there in one byte?", new String[]{"1/8", "1024", "8", "256"}, 'C'), getWindowCenterX(), getWindowCenterY()); case L -> onAction((Action) new LocationChangeAction("location-2", new Position(1, 2))); } } diff --git a/src/main/java/io/rpg/controller/PopupController.java b/src/main/java/io/rpg/controller/PopupController.java index 53dc0b01..2a0b2c65 100644 --- a/src/main/java/io/rpg/controller/PopupController.java +++ b/src/main/java/io/rpg/controller/PopupController.java @@ -1,5 +1,7 @@ package io.rpg.controller; +import io.rpg.model.object.Question; +import io.rpg.view.popups.QuestionPopup; import io.rpg.view.popups.TextImagePopup; import io.rpg.view.popups.TextPopup; import javafx.scene.image.Image; @@ -49,6 +51,13 @@ public void openPointsPopup(int pointsCount, int x, int y) { openTextImagePopup("You earned " + pointsCount + " points!", coinImage, x, y); } + public void openQuestionPopup(Question question, int x, int y) { + QuestionPopup popupScene = new QuestionPopup(question); + popupStage.setScene(popupScene); + popupStage.show(); + popupStage.setX(x - popupScene.getWidth() / 2); + popupStage.setY(y - popupScene.getHeight() / 2); + } public void hidePopup() { popupStage.hide(); diff --git a/src/main/java/io/rpg/model/object/Question.java b/src/main/java/io/rpg/model/object/Question.java new file mode 100644 index 00000000..c866bbc8 --- /dev/null +++ b/src/main/java/io/rpg/model/object/Question.java @@ -0,0 +1,7 @@ +package io.rpg.model.object; + +public record Question( + String question, + String [] answers, + char correctAnswer +) {} diff --git a/src/main/java/io/rpg/view/popups/QuestionPopup.java b/src/main/java/io/rpg/view/popups/QuestionPopup.java new file mode 100644 index 00000000..a78d8956 --- /dev/null +++ b/src/main/java/io/rpg/view/popups/QuestionPopup.java @@ -0,0 +1,73 @@ +package io.rpg.view.popups; + +import io.rpg.model.object.Question; +import io.rpg.viewmodel.QuestionPopupViewModel; +import javafx.fxml.FXMLLoader; +import javafx.scene.Group; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.paint.Color; + +import java.io.IOException; +import java.util.Objects; + +public class QuestionPopup extends Scene { + + private final QuestionPopupViewModel viewModel; + private final Question question; + + public QuestionPopup(Question question, String backgroundPath) { + this(question); + viewModel.setBackgroundImage(backgroundPath); + } + + public QuestionPopup(Question question) { + super(new Group(), Color.TRANSPARENT); + + this.question = question; + + FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(QuestionPopupViewModel.class.getResource("question-popup-view.fxml"))); + Parent root = null; + + try { + root = loader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + this.setRoot(root); + + viewModel = loader.getController(); + viewModel.setQuestion(question.question(), question.answers()); + + viewModel.setButtonCallback('A', event -> this.answerSelected('A')); + viewModel.setButtonCallback('B', event -> this.answerSelected('B')); + viewModel.setButtonCallback('C', event -> this.answerSelected('C')); + viewModel.setButtonCallback('D', event -> this.answerSelected('D')); + + this.setFill(Color.TRANSPARENT); + } + + public void answerSelected(char answer) { + char correctAnswer = question.correctAnswer(); + if (answer == correctAnswer){ + viewModel.setQuestionLabel("Correct!"); + } else { + viewModel.highlightWrong(answer); + viewModel.setQuestionLabel("Answer " + answer + " is incorrect. The correct answer is " + correctAnswer + ": " + question.answers()[getAnswerIndex(correctAnswer)]); + } + + viewModel.highlightCorrect(correctAnswer); + viewModel.removeButtonCallbacks(); + } + + private int getAnswerIndex(char answerCode) { + return switch (answerCode) { + case 'A' -> 0; + case 'B' -> 1; + case 'C' -> 2; + case 'D' -> 3; + default -> throw new IllegalStateException("Unexpected answer code: " + answerCode); + }; + } +} diff --git a/src/main/java/io/rpg/viewmodel/QuestionPopupViewModel.java b/src/main/java/io/rpg/viewmodel/QuestionPopupViewModel.java new file mode 100644 index 00000000..ddf350b4 --- /dev/null +++ b/src/main/java/io/rpg/viewmodel/QuestionPopupViewModel.java @@ -0,0 +1,66 @@ +package io.rpg.viewmodel; + +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Pane; + +public class QuestionPopupViewModel { + + @FXML private Label questionLabel; + @FXML private Pane backgroundPane; + @FXML private ImageView backgroundImage; + @FXML private Button aButton, bButton, cButton, dButton; + + public void setQuestion(String question, String[] answers) { + questionLabel.setText(question); + aButton.setText("A: " + answers[0]); + bButton.setText("B: " + answers[1]); + cButton.setText("C: " + answers[2]); + dButton.setText("D: " + answers[3]); + } + + public void setQuestionLabel(String text) { + questionLabel.setText(text); + } + + public void setBackgroundImage(String url) { + Image image = new Image(url); + backgroundImage.setImage(image); + } + + private Button getButtonFromCode(char buttonCode) { + return switch (buttonCode) { + case 'A' -> aButton; + case 'B' -> bButton; + case 'C' -> cButton; + case 'D' -> dButton; + default -> throw new IllegalStateException("Unexpected value: " + buttonCode); + }; + } + + public void setButtonCallback(char buttonCode, EventHandler callback) { + getButtonFromCode(buttonCode).setOnMouseClicked(callback); + } + + public void removeButtonCallbacks() { + aButton.setOnMouseClicked(null); + bButton.setOnMouseClicked(null); + cButton.setOnMouseClicked(null); + dButton.setOnMouseClicked(null); + } + + public void highlightCorrect(char buttonCode) { + Button button = getButtonFromCode(buttonCode); + button.setStyle(button.getStyle() + "-fx-border-color: #3b803b; -fx-border-width: 5px;"); + } + + public void highlightWrong(char buttonCode) { + Button button = getButtonFromCode(buttonCode); + button.setStyle(button.getStyle() + "-fx-border-color: #a93e3e; -fx-border-width: 5px;"); + } +} diff --git a/src/main/java/io/rpg/viewmodel/TextImagePopupViewModel.java b/src/main/java/io/rpg/viewmodel/TextImagePopupViewModel.java index a0a93704..63569fe8 100644 --- a/src/main/java/io/rpg/viewmodel/TextImagePopupViewModel.java +++ b/src/main/java/io/rpg/viewmodel/TextImagePopupViewModel.java @@ -30,7 +30,7 @@ public void setImage(Image image) { } public void setTextSize(int size) { - label.setStyle("-fx-font-family: Monospaced; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: " + size); + label.setStyle("-fx-font-family: Monospaced; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: " + size + ";"); } public void setBackgroundImage(String url) { diff --git a/src/main/java/io/rpg/viewmodel/TextPopupViewModel.java b/src/main/java/io/rpg/viewmodel/TextPopupViewModel.java index 8bdf21e3..b150d42e 100644 --- a/src/main/java/io/rpg/viewmodel/TextPopupViewModel.java +++ b/src/main/java/io/rpg/viewmodel/TextPopupViewModel.java @@ -25,7 +25,7 @@ public void setText(String text) { } public void setTextSize(int size) { - label.setStyle("-fx-font-family: Monospaced; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: " + size); + label.setStyle("-fx-font-family: Monospaced; -fx-text-fill: white; -fx-font-weight: bold; -fx-font-size: " + size + ";"); } public void setBackgroundImage(String url) { diff --git a/src/main/resources/css/styles.css b/src/main/resources/css/styles.css index ad42b5f0..272bb6a7 100644 --- a/src/main/resources/css/styles.css +++ b/src/main/resources/css/styles.css @@ -1,3 +1,8 @@ -.button:hover { +.ok_button:hover { -fx-text-fill: white; +} + +.answer_button:hover { + -fx-border-color: white; + -fx-border-width: 5px; } \ No newline at end of file diff --git a/src/main/resources/io/rpg/viewmodel/question-popup-view.fxml b/src/main/resources/io/rpg/viewmodel/question-popup-view.fxml new file mode 100644 index 00000000..0befa4e9 --- /dev/null +++ b/src/main/resources/io/rpg/viewmodel/question-popup-view.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/io/rpg/viewmodel/text-image-popup-view.fxml b/src/main/resources/io/rpg/viewmodel/text-image-popup-view.fxml index 2b65b0c1..13bae838 100644 --- a/src/main/resources/io/rpg/viewmodel/text-image-popup-view.fxml +++ b/src/main/resources/io/rpg/viewmodel/text-image-popup-view.fxml @@ -19,7 +19,7 @@ -