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 super MouseEvent> 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 @@
-