Skip to content

Commit

Permalink
feat: RPG-129 conditional actions (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
kkafar authored Jun 3, 2022
1 parent 8df797d commit ba49bef
Show file tree
Hide file tree
Showing 24 changed files with 324 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
{ "tag": "mul-1", "position": { "row": 6, "col": 9 }, "assetPath": "assets/bush.png" },
{ "tag": "mul-1", "position": { "row": 7, "col": 9 }, "assetPath": "assets/bush.png" },
{ "tag": "mul-1", "position": { "row": 8, "col": 9 }, "assetPath": "assets/bush.png" },
{ "tag": "object-1", "position": { "row": 7, "col": 8 }, "assetPath": "assets/bunny.png" },
{ "tag": "object-2", "position": { "row": 6, "col": 3 }, "assetPath": "assets/key.png" },
{ "tag": "object-3", "position": { "row": 2, "col": 0 }, "assetPath": "assets/zombie.png" },
{ "tag": "tp-bunny", "position": { "row": 7, "col": 8 }, "assetPath": "assets/bunny.png" },
{ "tag": "key", "position": { "row": 6, "col": 3 }, "assetPath": "assets/key.png" },
{ "tag": "bandit", "position": { "row": 2, "col": 0 }, "assetPath": "assets/zombie.png" },
{ "tag": "object-4", "position": { "row": 3, "col": 7 }, "assetPath": "assets/zombie.png" },
{ "tag": "object-5", "position": { "row": 9, "col": 8 }, "assetPath": "assets/chest.png" },
{ "tag": "object-6", "position": { "row": 9, "col": 9 }, "assetPath": "assets/bush.png" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"tag": "object-3",
"tag": "bandit",
"assetPath": "assets/bandit.png",
"type": "dialog",
"onLeftClick": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"tag": "object-2",
"tag": "key",
"onLeftClick": {"tag": "dialogue-action", "type": "dialogue", "statements": ["A key.,A, keyA, key.A, key.A, key.A, key.A, key.A, key.A key."], "assetPath": "assets/key.png"}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"tag": "tp-bunny",
"onLeftClick": {
"tag": "change-location",
"type": "location-change",
"target-location": "location-2",
"target-position": {"row": 9, "col": 0},
"condition": {
"type": "item-required",
"item-tag": "key"
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/io/rpg/Initializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public Result<Game, Exception> initialize() {
Game.Builder gameBuilder = new Game.Builder();
Game game = gameBuilder
.setController(controller)
.setOnStartAction(new LocationChangeAction(gameWorldConfig.getRootLocation(), player.getPosition()))
.setOnStartAction(new LocationChangeAction(gameWorldConfig.getRootLocation(), player.getPosition(), null))
.build();

return Result.ok(game);
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/io/rpg/config/model/ActionConfigBundle.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,15 @@ public class ActionConfigBundle implements ConfigWithValidation {
*/
@Nullable
@SerializedName("type")
// private String actionType;
private ActionType actionType;

/**
* Condition that needs to be satisfied before the action can be executed.
*/
@Nullable
@SerializedName(value = "condition", alternate = {"requires"})
private ConditionConfigBundle condition;

/**
* Action to be triggered before the proper action is executed.
*/
Expand Down Expand Up @@ -149,6 +155,11 @@ public ActionType getActionType() {
return actionType;
}

@Nullable
public ConditionConfigBundle getCondition() {
return condition;
}

@Nullable
public ActionConfigBundle getBeforeAction() {
return beforeAction;
Expand Down Expand Up @@ -263,6 +274,9 @@ Result<Void, Exception> validateBasic() {
if (actionType == null) {
builder.append("No action type or invalid action type provided");
}
if (condition != null) {
condition.validate().ifErr(ex -> builder.append(ex.getMessage()));
}

return builder.isEmpty() ? Result.ok() :
Result.err(new IllegalStateException(builder.toString()));
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/io/rpg/config/model/ConditionConfigBundle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.rpg.config.model;

import com.google.gson.annotations.SerializedName;
import com.kkafara.rt.Result;
import io.rpg.model.actions.ConditionType;
import io.rpg.util.ErrorMessageBuilder;
import org.jetbrains.annotations.Nullable;

public class ConditionConfigBundle implements ConfigWithValidation {

@Nullable
private ConditionType type;

@Nullable
@SerializedName(value = "item-tag", alternate = {"itemTag"})
private String itemTag;

@Nullable
public ConditionType getType() {
return type;
}

@Nullable
public String getItemTag() {
return itemTag;
}

Result<Void, Exception> validateItemRequired() {
ErrorMessageBuilder builder = new ErrorMessageBuilder();

if (itemTag == null || itemTag.isBlank()) {
builder.append("No or invalid item tag provided");
}

return builder.isEmpty() ? Result.ok() : Result.err(new Exception(builder.toString()));
}

@Override
public Result<Void, Exception> validate() {
ErrorMessageBuilder builder = new ErrorMessageBuilder();

if (type == null) {
builder.append("No or invalid type provided");
}

if (!builder.isEmpty()) {
return Result.err(new Exception(builder.toString()));
}

switch (type) {
case ITEM_REQUIRED -> { return validateItemRequired(); }
default -> throw new IllegalArgumentException("Not implemented condition type!");
}
}
}
113 changes: 68 additions & 45 deletions src/main/java/io/rpg/controller/Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.kkafara.rt.Result;
import io.rpg.model.actions.*;
import io.rpg.model.actions.condition.ConditionEngine;
import io.rpg.model.data.KeyboardEvent;
import io.rpg.model.data.MouseClickedEvent;
import io.rpg.model.data.Position;
Expand All @@ -13,10 +14,6 @@
import io.rpg.view.GameEndView;
import io.rpg.view.GameObjectView;
import io.rpg.view.LocationView;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
Expand All @@ -26,6 +23,11 @@
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;

public class Controller implements KeyboardEvent.Observer, MouseClickedEvent.Observer, ActionConsumer {
private Scene currentView;
private LinkedHashMap<String, LocationModel> tagToLocationModelMap;
Expand All @@ -36,13 +38,16 @@ public class Controller implements KeyboardEvent.Observer, MouseClickedEvent.Obs
private PlayerController playerController;
private GameEndController gameEndController;
private Stage mainStage;
private final ConditionEngine conditionEngine;


public Controller() {
logger = LogManager.getLogger(Controller.class);

tagToLocationModelMap = new LinkedHashMap<>();
tagToLocationViewMap = new LinkedHashMap<>();

conditionEngine = new ConditionEngine(this);
gameEndController = new GameEndController();
}

Expand All @@ -57,6 +62,8 @@ public Controller(LinkedHashMap<String, LocationModel> tagToLocationModelMap,
// TODO: handle errors
this.currentModel = this.tagToLocationModelMap.get(rootTag);
this.currentView = this.tagToLocationViewMap.get(rootTag);

conditionEngine = new ConditionEngine(this);
}

public void setMainStage(Stage mainStage) {
Expand Down Expand Up @@ -107,40 +114,48 @@ public void consumeAction(Action action) {
}

private void onAction(LocationChangeAction action) {
if (!this.tagToLocationModelMap.containsKey(action.destinationLocationTag)) {
logger.error("Unknown location tag");
return;
}
actionGuard(action, () -> {
if (!this.tagToLocationModelMap.containsKey(action.destinationLocationTag)) {
logger.error("Unknown location tag");
return;
}

LocationView nextView = this.tagToLocationViewMap.get(action.destinationLocationTag);
LocationModel nextModel = this.tagToLocationModelMap.get(action.destinationLocationTag);
LocationView nextView = this.tagToLocationViewMap.get(action.destinationLocationTag);
LocationModel nextModel = this.tagToLocationModelMap.get(action.destinationLocationTag);

playerController.teleportTo(nextModel, nextView, action.playerPosition);
playerController.teleportTo(nextModel, nextView, action.playerPosition);

this.currentModel = nextModel;
this.currentView = nextView;
mainStage.setScene(nextView);
this.currentModel = nextModel;
this.currentView = nextView;
mainStage.setScene(nextView);
});
}

private void onAction(DialogueAction action) {
popupController.openDialoguePopup(action.text, action.image, getWindowCenterX(), getWindowCenterY());
actionGuard(action, () -> {
popupController.openDialoguePopup(action.text, action.image, getWindowCenterX(), getWindowCenterY());
});
}

private void onAction(ShowDescriptionAction action) {
if (!action.description.isEmpty()) {
popupController.openTextImagePopup(action.description, action.image, getWindowCenterX(), getWindowCenterY());
}
actionGuard(action, () -> {
if (!action.description.isEmpty()) {
popupController.openTextImagePopup(action.description, action.image, getWindowCenterX(), getWindowCenterY());
}
});
}

private void onAction(QuizAction action) {
int pointsCount = action.getPointsToEarn();
popupController.openQuestionPopup(
action.question,
getWindowCenterX(), getWindowCenterY(),
() -> acceptQuizResult(true, pointsCount),
() -> acceptQuizResult(false, 0)
);
action.setPointsToEarn(0);
actionGuard(action, () -> {
int pointsCount = action.getPointsToEarn();
popupController.openQuestionPopup(
action.question,
getWindowCenterX(), getWindowCenterY(),
() -> acceptQuizResult(true, pointsCount),
() -> acceptQuizResult(false, 0)
);
action.setPointsToEarn(0);
});
}

public void acceptQuizResult(boolean correct, int pointsCount) {
Expand All @@ -156,24 +171,35 @@ public void acceptQuizResult(boolean correct, int pointsCount) {
}

private void onAction(GameEndAction action) {
gameEndController.showGameEnd(mainStage, action.description);

actionGuard(action, () -> {
gameEndController.showGameEnd(mainStage, action.description);
});
}

private void onAction(BattleAction action) {
Player player = playerController.getPlayer();
GameObject opponent = action.getOpponent();
int reward = action.getReward();
BattleResult result;
if (player.getPoints() > opponent.getStrength()) {
player.addPoints(reward);
result = new BattleResult(BattleResult.Result.VICTORY, reward);
} else if (player.getStrength() < opponent.getStrength()) {
result = new BattleResult(BattleResult.Result.DEFEAT, 0);
} else {
result = new BattleResult(BattleResult.Result.DRAW, 0);
actionGuard(action, () -> {
Player player = playerController.getPlayer();
GameObject opponent = action.getOpponent();
int reward = action.getReward();
BattleResult result;
if (player.getPoints() > opponent.getStrength()) {
player.addPoints(reward);
result = new BattleResult(BattleResult.Result.VICTORY, reward);
} else if (player.getStrength() < opponent.getStrength()) {
result = new BattleResult(BattleResult.Result.DEFEAT, 0);
} else {
result = new BattleResult(BattleResult.Result.DRAW, 0);
}
popupController.openTextPopup(result.getMessage(), getWindowCenterX(), getWindowCenterY());
});
}

private void actionGuard(ConditionalAction action, Runnable actionLogic) {
if (action.getCondition() != null && !action.getCondition().acceptEngine(conditionEngine)) {
logger.info("Action not executed due to condition being not satisfied");
return;
}
popupController.openTextPopup(result.getMessage(), getWindowCenterX(), getWindowCenterY());
actionLogic.run();
}

public Scene getView() {
Expand Down Expand Up @@ -220,13 +246,10 @@ public void onKeyboardEvent(KeyboardEvent event) {
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 -> consumeAction(new LocationChangeAction("location-2", new Position(1, 2)));
case U -> consumeAction(new GameEndAction("You have pressed the forbidden button"));
case L -> consumeAction(new LocationChangeAction("location-2", new Position(1, 2), null));
case U -> consumeAction(new GameEndAction("You have pressed the forbidden button", null));
}
}
// } else if (payload.getEventType() == KeyEvent.KEY_RELEASED) {
//
// }
}

private int getWindowCenterX() {
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/rpg/model/actions/BattleAction.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.rpg.model.actions;

import io.rpg.model.actions.condition.Condition;
import io.rpg.model.object.GameObject;

public class BattleAction implements Action {
public class BattleAction extends ConditionalAction {
private GameObject opponent;
private final int reward;

public BattleAction(int reward) {
public BattleAction(int reward, Condition condition) {
super(condition);
this.reward = reward;
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/rpg/model/actions/ConditionType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.rpg.model.actions;

import com.google.gson.annotations.SerializedName;

public enum ConditionType {
@SerializedName("item-required")
ITEM_REQUIRED
}
18 changes: 18 additions & 0 deletions src/main/java/io/rpg/model/actions/ConditionalAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.rpg.model.actions;

import io.rpg.model.actions.condition.Condition;
import org.jetbrains.annotations.Nullable;

public abstract class ConditionalAction implements Action {
@Nullable
private final Condition condition;

public ConditionalAction(@Nullable Condition condition) {
this.condition = condition;
}

@Nullable
public Condition getCondition() {
return condition;
}
}
Loading

0 comments on commit ba49bef

Please sign in to comment.