mirror of
https://github.com/schmaeddes/untitledTextAdventure.git
synced 2024-11-23 21:30:17 +01:00
Even more simplification and documentation
This commit is contained in:
parent
976acb8d41
commit
3036fd5619
11 changed files with 458 additions and 364 deletions
|
@ -12,7 +12,6 @@ import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
|
||||||
import game.logic.GameLogic;
|
import game.logic.GameLogic;
|
||||||
import game.logic.Parser;
|
import game.logic.Parser;
|
||||||
import game.state.CircularLocationException;
|
import game.state.CircularLocationException;
|
||||||
import game.state.Entity;
|
|
||||||
import startup.Environment;
|
import startup.Environment;
|
||||||
import startup.LoadStuff;
|
import startup.LoadStuff;
|
||||||
|
|
||||||
|
@ -26,8 +25,8 @@ public class Main {
|
||||||
Engine.newBuilder().option("engine.WarnInterpreterOnly", "false").build(),
|
Engine.newBuilder().option("engine.WarnInterpreterOnly", "false").build(),
|
||||||
Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowHostClassLookup(s -> true));
|
Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowHostClassLookup(s -> true));
|
||||||
try {
|
try {
|
||||||
Entity t = new Entity("test");
|
// Entity t = new Entity("test");
|
||||||
jse.put("test", t);
|
jse.put("test", new Object());
|
||||||
jse.eval("console.log(test.toString());");
|
jse.eval("console.log(test.toString());");
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
|
@ -36,7 +35,8 @@ public class Main {
|
||||||
|
|
||||||
Parser parser = new Parser();
|
Parser parser = new Parser();
|
||||||
try (GameLogic logic = new GameLogic(parser)) {
|
try (GameLogic logic = new GameLogic(parser)) {
|
||||||
logic.loadGameState("games/damnCoolTextAdventureFTW.json");
|
logic.loadGameState("games/damnCoolTextAdventureFTW/game.json",
|
||||||
|
"games/damnCoolTextAdventureFTW/initialSave.json");
|
||||||
logic.mainLoop();
|
logic.mainLoop();
|
||||||
} catch (CircularLocationException ex) {
|
} catch (CircularLocationException ex) {
|
||||||
System.err.println("You messed up you game state: " + ex.getMessage());
|
System.err.println("You messed up you game state: " + ex.getMessage());
|
||||||
|
|
|
@ -7,7 +7,9 @@ import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import game.logic.actionsystem.PlayerAction;
|
import game.logic.actionsystem.Action;
|
||||||
|
import game.logic.actionsystem.ActionExecutor;
|
||||||
|
import game.logic.actionsystem.ActionSignature;
|
||||||
import game.state.CircularLocationException;
|
import game.state.CircularLocationException;
|
||||||
import game.state.Entity;
|
import game.state.Entity;
|
||||||
import game.state.EntitySet;
|
import game.state.EntitySet;
|
||||||
|
@ -15,128 +17,111 @@ import game.state.GameState;
|
||||||
|
|
||||||
public class GameLogic implements Closeable {
|
public class GameLogic implements Closeable {
|
||||||
private final Parser parser;
|
private final Parser parser;
|
||||||
private GameState state;
|
private GameState gameState;
|
||||||
private Entity player;
|
private Entity player;
|
||||||
private boolean discontinue = false;
|
private boolean discontinue = false;
|
||||||
private Map<String, List<PlayerAction>> playerActions = new HashMap<>();
|
private Map<String, List<Action>> playerActions = new HashMap<>();
|
||||||
|
|
||||||
public GameLogic(Parser parser) {
|
public GameLogic(Parser parser) {
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
this.state = new GameState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadGameState(String stateDescJsonFilePath) throws CircularLocationException {
|
public void loadGameState(String gameDescriptionJsonPath, String savegameJsonPath) throws CircularLocationException {
|
||||||
////////////////////////////////////////////////////////////////////////
|
this.gameState = new GameState(savegameJsonPath);
|
||||||
// TODO setup code, load from json or so
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
Entity east = this.state.createEntity("east");
|
/*
|
||||||
Entity west = this.state.createEntity("west");
|
* TODO: load from game specific JSON
|
||||||
Entity inside = this.state.createEntity("inside");
|
*/
|
||||||
Entity outside = this.state.createEntity("outside");
|
EntitySet collectibles = this.gameState.getEntitySetById("collectibles");
|
||||||
EntitySet genericDirections = EntitySet.createPersistent("genericDirections");
|
EntitySet genericDirections = this.gameState.getEntitySetById("genericDirections");
|
||||||
genericDirections.add(east, west);
|
EntitySet houseDirections = this.gameState.getEntitySetById("houseDirections");
|
||||||
EntitySet houseDirections = EntitySet.createPersistent("houseDirections");
|
this.player = this.gameState.getEntityById("player");
|
||||||
houseDirections.add(inside, outside);
|
Entity houseInside = this.gameState.getEntityById("houseInside");
|
||||||
|
Entity houseOutside = this.gameState.getEntityById("houseOutside");
|
||||||
|
Entity houseMainDoor = this.gameState.getEntityById("houseMainDoor");
|
||||||
|
|
||||||
Entity forestPath01 = this.state.createEntity("forest_path_01");
|
ActionSignature takeSignature = new ActionSignature();
|
||||||
Entity clearing = this.state.createEntity("clearing");
|
takeSignature.pushEntry(collectibles, 1);
|
||||||
Entity houseOutside = this.state.createEntity("house_outside");
|
this.createAction("take", takeSignature, (logic, actor, args) -> {
|
||||||
Entity houseInside = this.state.createEntity("house_inside");
|
|
||||||
Entity houseMainDoor = this.state.createEntity("house_main_door");
|
|
||||||
|
|
||||||
EntitySet locations = EntitySet.createPersistent("locations");
|
|
||||||
locations.add(forestPath01, clearing, houseInside, houseOutside);
|
|
||||||
|
|
||||||
forestPath01.connectBidirectional(east, west, clearing);
|
|
||||||
forestPath01.connectBidirectional(west, east, houseOutside);
|
|
||||||
houseOutside.connectBidirectional(inside, outside, houseInside);
|
|
||||||
|
|
||||||
this.player = clearing.createContainedEntity(this, "player");
|
|
||||||
Entity apple01 = forestPath01.createContainedEntity(this, "apple_01");
|
|
||||||
Entity apple02 = forestPath01.createContainedEntity(this, "apple_02");
|
|
||||||
|
|
||||||
EntitySet collectibles = EntitySet.createPersistent("collectibles");
|
|
||||||
collectibles.add(apple01, apple02);
|
|
||||||
|
|
||||||
collectibles.pushPlayerAction("take", (logic, args) -> {
|
|
||||||
Entity collectible = args[0];
|
Entity collectible = args[0];
|
||||||
if (logic.getPlayer().getLocation() == collectible.getLocation()) {
|
if (actor.getLocation() == collectible.getLocation()) {
|
||||||
try {
|
try {
|
||||||
collectible.setLocation(logic.getPlayer());
|
collectible.setLocation(actor);
|
||||||
logic.printRaw("Du nimmst %s, du Schuft.\n", collectible);
|
logic.printRaw("%s: Du nimmst %s, du Schuft.\n", actor, collectible);
|
||||||
|
return true;
|
||||||
} catch (CircularLocationException ex) {
|
} catch (CircularLocationException ex) {
|
||||||
// Should not happpen
|
// Should not happpen
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// this.player becomes first argument by calling pushPlayerAction on it
|
ActionSignature goActionSignature = new ActionSignature();
|
||||||
PlayerAction goAction = this.player.pushPlayerAction("go", (logic, args) -> {
|
goActionSignature.pushEntry(genericDirections, 1);
|
||||||
Entity character = args[0];
|
this.createAction("go", goActionSignature, (logic, actor, args) -> {
|
||||||
Entity direction = args[1];
|
Entity direction = args[0];
|
||||||
Entity newLocation = character.getLocation().getConnectedEntity(direction);
|
Entity newLocation = actor.getLocation().getConnectedEntity(direction);
|
||||||
if (newLocation != null) {
|
if (newLocation != null) {
|
||||||
try {
|
try {
|
||||||
logic.getPlayer().setLocation(newLocation);
|
logic.getPlayer().setLocation(newLocation);
|
||||||
logic.printRaw("Du gehst Richtung %s und landest hier: %s, du Lutscher!\n", direction, newLocation);
|
logic.printRaw("%s: Du gehst Richtung %s und landest hier: %s, du Lutscher!\n", actor, direction,
|
||||||
|
newLocation);
|
||||||
} catch (CircularLocationException ex) {
|
} catch (CircularLocationException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logic.printRaw("Hier geht es nicht nach %s, du Nichtsnutz.\n", direction);
|
logic.printRaw("%s: Hier geht es nicht nach %s, du Nichtsnutz.\n", actor, direction);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
// second argument must be exactly 1 of the entites contained in genericDirections
|
|
||||||
goAction.pushVaryingNeededEntites(genericDirections, 1);
|
|
||||||
|
|
||||||
// again first argument becomes this.player by calling pushPlayerAction on it
|
ActionSignature goHouseActionSignature = new ActionSignature();
|
||||||
PlayerAction goHouseAction = this.player.pushPlayerAction("go", (logic, args) -> {
|
goHouseActionSignature.pushEntry(houseDirections, 1);
|
||||||
Entity character = args[0];
|
this.createAction("go", goHouseActionSignature, (logic, actor, args) -> {
|
||||||
Entity direction = args[1];
|
Entity direction = args[0];
|
||||||
Entity newLocation = character.getLocation().getConnectedEntity(direction);
|
Entity newLocation = actor.getLocation().getConnectedEntity(direction);
|
||||||
if (newLocation != null && character.getLocation() == houseInside
|
if (newLocation != null && actor.getLocation() == houseInside || actor.getLocation() == houseOutside) {
|
||||||
|| character.getLocation() == houseOutside) {
|
|
||||||
if (houseMainDoor.getBoolAttribute("open")) {
|
if (houseMainDoor.getBoolAttribute("open")) {
|
||||||
try {
|
try {
|
||||||
character.setLocation(newLocation);
|
actor.setLocation(newLocation);
|
||||||
} catch (CircularLocationException ex) {
|
} catch (CircularLocationException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
logic.printRaw("Du gehst durch die Tür, du Eumel.\n");
|
logic.printRaw("%s: Du gehst durch %s, du Eumel.\n", actor, houseMainDoor);
|
||||||
} else {
|
} else {
|
||||||
logic.printRaw("Die Tür ist zu, du Dödel.\n");
|
logic.printRaw("%s: %s ist zu, du Dödel.\n", actor, houseMainDoor);
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
// second argument must be exactly 1 of the entites contained in houseDirections
|
|
||||||
goHouseAction.pushVaryingNeededEntites(houseDirections, 1);
|
|
||||||
|
|
||||||
houseMainDoor.pushPlayerAction("open", (logic, args) -> {
|
|
||||||
if (logic.getPlayer().getLocation() == houseInside || logic.getPlayer().getLocation() == houseOutside) {
|
|
||||||
if (houseMainDoor.getBoolAttribute("open")) {
|
|
||||||
logic.printRaw("Die Tür ist schon offen, du Hammel.\n");
|
|
||||||
} else {
|
|
||||||
houseMainDoor.setAttribute("open", true);
|
|
||||||
logic.printRaw("Du öffnest die Tür, du Dummbatz.\n");
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
houseMainDoor.pushPlayerAction("close", (logic, args) -> {
|
ActionSignature openCloseSignature = new ActionSignature();
|
||||||
if (logic.getPlayer().getLocation() == houseInside || logic.getPlayer().getLocation() == houseOutside) {
|
openCloseSignature.pushEntry(houseMainDoor);
|
||||||
if (!houseMainDoor.getBoolAttribute("open")) {
|
|
||||||
logic.printRaw("Die Tür ist schon geschlossen, du Mummenschanz.\n");
|
this.createAction("open", openCloseSignature, (logic, actor, args) -> {
|
||||||
|
Entity door = args[0];
|
||||||
|
if (actor.getLocation() == houseInside || actor.getLocation() == houseOutside) {
|
||||||
|
if (door.getBoolAttribute("open")) {
|
||||||
|
logic.printRaw("%s: Die Tür ist schon offen, du Hammel.\n", actor);
|
||||||
} else {
|
} else {
|
||||||
houseMainDoor.setAttribute("open", false);
|
door.setAttribute("open", true);
|
||||||
logic.printRaw("Du schließt die Tür, du Angsthase.\n");
|
logic.printRaw("%s: Du öffnest die Tür, du Dummbatz.\n", actor);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.createAction("close", openCloseSignature, (logic, actor, args) -> {
|
||||||
|
Entity door = args[0];
|
||||||
|
if (actor.getLocation() == houseInside || actor.getLocation() == houseOutside) {
|
||||||
|
if (!door.getBoolAttribute("open")) {
|
||||||
|
logic.printRaw("%s: Die Tür ist schon geschlossen, du Mummenschanz.\n", actor);
|
||||||
|
} else {
|
||||||
|
door.setAttribute("open", false);
|
||||||
|
logic.printRaw("%s: Du schließt die Tür, du Angsthase.\n", actor);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +130,7 @@ public class GameLogic implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameState getState() {
|
public GameState getState() {
|
||||||
return this.state;
|
return this.gameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity getPlayer() {
|
public Entity getPlayer() {
|
||||||
|
@ -162,23 +147,24 @@ public class GameLogic implements Closeable {
|
||||||
System.out.printf(rawMessage, args);
|
System.out.printf(rawMessage, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerPlayerAction(PlayerAction action) {
|
public void createAction(String id, ActionSignature signature, ActionExecutor executor) {
|
||||||
List<PlayerAction> l = this.playerActions.get(action.getId());
|
List<Action> l = this.playerActions.get(id);
|
||||||
if (l == null) {
|
if (l == null) {
|
||||||
this.playerActions.put(action.getId(), l = new LinkedList<>());
|
this.playerActions.put(id, l = new LinkedList<>());
|
||||||
}
|
}
|
||||||
l.add(action);
|
l.add(new Action(id, signature, executor));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for the first player action that does not use any entity and
|
* Searches for the first player action that does not use any entity and
|
||||||
* executes it if one is found.
|
* executes it if one is found.
|
||||||
*
|
*
|
||||||
* @param id The action id
|
* @param id The action id
|
||||||
|
* @param actor The actor who should execute the action
|
||||||
* @return <code>true</code>, if an action was found and executed
|
* @return <code>true</code>, if an action was found and executed
|
||||||
*/
|
*/
|
||||||
public boolean tryExecutePlayerAction(String id) {
|
public boolean tryExecuteAction(String id, Entity actor) {
|
||||||
return this.tryExecutePlayerAction(id, null);
|
return this.tryExecuteAction(id, actor, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,14 +172,15 @@ public class GameLogic implements Closeable {
|
||||||
* entities and executes it if one is found.
|
* entities and executes it if one is found.
|
||||||
*
|
*
|
||||||
* @param id The action id
|
* @param id The action id
|
||||||
|
* @param actor The actor who should execute the action
|
||||||
* @param entities The entities on which the action shoud operate
|
* @param entities The entities on which the action shoud operate
|
||||||
* @return <code>true</code>, if an action was found and executed
|
* @return <code>true</code>, if an action was found and executed
|
||||||
*/
|
*/
|
||||||
public boolean tryExecutePlayerAction(String id, EntitySet entities) {
|
public boolean tryExecuteAction(String id, Entity actor, EntitySet entities) {
|
||||||
List<PlayerAction> l = this.playerActions.get(id);
|
List<Action> l = this.playerActions.get(id);
|
||||||
if (l != null) {
|
if (l != null) {
|
||||||
for (PlayerAction a : l) {
|
for (Action a : l) {
|
||||||
if (a.tryExecute(null, entities, this)) {
|
if (a.tryExecute(actor, entities, this)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class Parser implements Closeable {
|
||||||
|
|
||||||
String greenPrompt = TextColors.BLUE.colorize(">");
|
String greenPrompt = TextColors.BLUE.colorize(">");
|
||||||
System.out.printf("%s ", greenPrompt);
|
System.out.printf("%s ", greenPrompt);
|
||||||
List<String> input = Arrays.stream(scanner.nextLine().split("\\s+")).map(String::toLowerCase).toList();
|
List<String> input = Arrays.asList(scanner.nextLine().split("\\s+"));
|
||||||
if (!input.isEmpty()) {
|
if (!input.isEmpty()) {
|
||||||
String actionId = input.get(0);
|
String actionId = input.get(0);
|
||||||
List<Entity> args = new ArrayList<>(input.size() - 1);
|
List<Entity> args = new ArrayList<>(input.size() - 1);
|
||||||
|
@ -34,23 +34,12 @@ public class Parser implements Closeable {
|
||||||
}
|
}
|
||||||
args.add(e);
|
args.add(e);
|
||||||
}
|
}
|
||||||
Entity primaryEntity = this.getPrimaryEntity(logic, actionId, args);
|
if (!logic.tryExecuteAction(actionId, logic.getPlayer(), new EntitySet(args))) {
|
||||||
if (primaryEntity != null) {
|
|
||||||
primaryEntity.tryExecutePlayerAction(actionId, EntitySet.createTemporary(args), logic);
|
|
||||||
} else if(logic.tryExecutePlayerAction(actionId, EntitySet.createTemporary(args))) {
|
|
||||||
logic.printRaw("Das geht doch so nicht.\n", args);
|
logic.printRaw("Das geht doch so nicht.\n", args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Entity getPrimaryEntity(GameLogic logic, String actionId, List<Entity> arguments) {
|
|
||||||
return switch (actionId) {
|
|
||||||
case "go" -> logic.getPlayer();
|
|
||||||
case "take", "open", "close" -> arguments.remove(0);
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void parse(List<String> parameter) {
|
public void parse(List<String> parameter) {
|
||||||
String command = parameter.get(0);
|
String command = parameter.get(0);
|
||||||
|
|
||||||
|
|
64
src/main/java/game/logic/actionsystem/Action.java
Normal file
64
src/main/java/game/logic/actionsystem/Action.java
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package game.logic.actionsystem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import game.logic.GameLogic;
|
||||||
|
import game.state.Entity;
|
||||||
|
import game.state.EntitySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player action represents a single action which operates on any number of
|
||||||
|
* entities.
|
||||||
|
*
|
||||||
|
* It has an identifier, e.g. {@code "combine"}, a signature of needed entities,
|
||||||
|
* and an executor. The signature consists of multiple entries, each one being
|
||||||
|
* either a single entity or a group of entities. To execute an action a
|
||||||
|
* {@link game.state.EntitySet} must be given which can be matched against its
|
||||||
|
* signature.
|
||||||
|
*/
|
||||||
|
public class Action {
|
||||||
|
private final String id;
|
||||||
|
private final ActionExecutor executor;
|
||||||
|
private final ActionSignatureMatcher signatureMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a player action by providing an id and an executor.
|
||||||
|
*
|
||||||
|
* @param id The action's id
|
||||||
|
* @param signature The action's signature
|
||||||
|
* @param executor The executor which is executed if a matching entity set is
|
||||||
|
* provided to
|
||||||
|
* {@link Action#tryExecute(Entity, EntitySet, GameLogic)}.
|
||||||
|
*/
|
||||||
|
public Action(String id, ActionSignature signature, ActionExecutor executor) {
|
||||||
|
this.id = id;
|
||||||
|
this.signatureMatcher = new ActionSignatureMatcher(signature);
|
||||||
|
this.executor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for the action's id.
|
||||||
|
*
|
||||||
|
* @return The action id
|
||||||
|
*/
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to match the given entities to this action's signature and tries to
|
||||||
|
* execute it if match was successful.
|
||||||
|
*
|
||||||
|
* @param actor The actor which executes the action
|
||||||
|
* @param entities A set of entities which will be matched against the signature
|
||||||
|
* (excl. the first entry if <code>primaryEntity</code> was not
|
||||||
|
* <code>null</code>)
|
||||||
|
* @param logic The game logic
|
||||||
|
* @return <code>true</code>, if the given entities match the signature and the
|
||||||
|
* action was executed successfully
|
||||||
|
*/
|
||||||
|
public boolean tryExecute(Entity actor, EntitySet entities, GameLogic logic) {
|
||||||
|
List<Entity> matched = this.signatureMatcher.tryMatch(entities);
|
||||||
|
return matched != null && this.executor.execute(logic, actor, matched.toArray(new Entity[0]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,16 +4,17 @@ import game.logic.GameLogic;
|
||||||
import game.state.Entity;
|
import game.state.Entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A player action executor provides specific instructions how to manipulate the
|
* An action executor provides specific instructions how to manipulate the game
|
||||||
* game logic.
|
* logic.
|
||||||
*/
|
*/
|
||||||
public interface PlayerActionExecutor {
|
public interface ActionExecutor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the game logic manupulation.
|
* Executes the game logic manupulation.
|
||||||
*
|
*
|
||||||
* @param logic The game logic
|
* @param logic The game logic
|
||||||
|
* @param actor The actor
|
||||||
* @param args Arguments
|
* @param args Arguments
|
||||||
*/
|
*/
|
||||||
public boolean execute(GameLogic logic, Entity... args);
|
public boolean execute(GameLogic logic, Entity actor, Entity... args);
|
||||||
}
|
}
|
83
src/main/java/game/logic/actionsystem/ActionSignature.java
Normal file
83
src/main/java/game/logic/actionsystem/ActionSignature.java
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package game.logic.actionsystem;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import game.state.Entity;
|
||||||
|
import game.state.EntitySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action signature describes which entities are needed for an action to
|
||||||
|
* execute. Note that even if the signature matches the execution itself can
|
||||||
|
* still fail.
|
||||||
|
*/
|
||||||
|
public class ActionSignature {
|
||||||
|
private record SignatureEntry(EntitySet entities, int size) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<SignatureEntry> entries = new LinkedList<>();
|
||||||
|
private int totalEntityCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a new entry to the signature consisting of a single entity which is
|
||||||
|
* needed for execution.
|
||||||
|
*
|
||||||
|
* @param entity The needed entity to push
|
||||||
|
*/
|
||||||
|
public void pushEntry(Entity entity) {
|
||||||
|
this.pushEntry(new EntitySet(entity), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a new entry to the signature consisting of a set of entities of which
|
||||||
|
* a specific count is needed for execution.
|
||||||
|
*
|
||||||
|
* @param entities The set of needed entities
|
||||||
|
* @param size How many entities of the given set are needed for execution
|
||||||
|
*/
|
||||||
|
public void pushEntry(EntitySet entities, int size) {
|
||||||
|
this.entries.add(new SignatureEntry(entities, size));
|
||||||
|
this.totalEntityCount += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total entity count is the sum of all entry sizes.
|
||||||
|
*
|
||||||
|
* @return The total entity count
|
||||||
|
*/
|
||||||
|
public int getTotalEntityCount() {
|
||||||
|
return this.totalEntityCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of entries pushed to the signature.
|
||||||
|
*
|
||||||
|
* @return The entry count
|
||||||
|
*/
|
||||||
|
public int getEntryCount() {
|
||||||
|
return this.entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of an entry is the size paramter given when the entry was pushed to
|
||||||
|
* the signature.
|
||||||
|
*
|
||||||
|
* @param entryIndex The index of the entry
|
||||||
|
* @return The size of the entry
|
||||||
|
*/
|
||||||
|
public int getEntrySize(int entryIndex) {
|
||||||
|
return this.entries.get(entryIndex).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The allowed entities of an entry were given when the entry was pushed to the
|
||||||
|
* signature.
|
||||||
|
*
|
||||||
|
* @param entryIndex The entry's index
|
||||||
|
* @return The allowed entities of an entry
|
||||||
|
*/
|
||||||
|
public Set<Entity> getEntryEntitySet(int entryIndex) {
|
||||||
|
return this.entries.get(entryIndex).entities().getAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package game.logic.actionsystem;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import game.state.Entity;
|
||||||
|
import game.state.EntitySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The action signature matcher matches a given set of entities against a
|
||||||
|
* signature.
|
||||||
|
*/
|
||||||
|
public class ActionSignatureMatcher {
|
||||||
|
private final ActionSignature signature;
|
||||||
|
private final int signatureEntries[];
|
||||||
|
private final Entity matchedEntities[];
|
||||||
|
private EntitySet workingSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param signature The signature
|
||||||
|
*/
|
||||||
|
public ActionSignatureMatcher(ActionSignature signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
this.signatureEntries = new int[signature.getTotalEntityCount()];
|
||||||
|
this.matchedEntities = new Entity[signature.getTotalEntityCount()];
|
||||||
|
int fillFromIdx = 0;
|
||||||
|
for (int i = 0; i < signature.getEntryCount(); ++i) {
|
||||||
|
int fillToIndex = fillFromIdx + signature.getEntrySize(i);
|
||||||
|
Arrays.fill(this.signatureEntries, fillFromIdx, fillToIndex, i);
|
||||||
|
fillFromIdx = fillToIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to match the given set of entities to the signature which was provided
|
||||||
|
* during construction. If a match was found the matching entities are returned
|
||||||
|
* in the order they match the signature.
|
||||||
|
*
|
||||||
|
* @param entities The set of entities to match
|
||||||
|
* @return The matching entities in order of the signature or <code>null</code>
|
||||||
|
* if no match was found
|
||||||
|
*/
|
||||||
|
public List<Entity> tryMatch(EntitySet entities) {
|
||||||
|
int givenEntityCount = entities == null ? 0 : entities.getSize();
|
||||||
|
if (givenEntityCount != this.signature.getTotalEntityCount()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this.workingSet = new EntitySet(entities.getAll());
|
||||||
|
return this.match(0) ? Arrays.asList(this.matchedEntities) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive matching algorithm.
|
||||||
|
*
|
||||||
|
* @param idx The index to start the match
|
||||||
|
* @return <code>true</code> if match was found
|
||||||
|
*/
|
||||||
|
private boolean match(int idx) {
|
||||||
|
if (idx == this.matchedEntities.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int currentEntryIndex = this.signatureEntries[idx];
|
||||||
|
Set<Entity> allowedEntities = this.signature.getEntryEntitySet(currentEntryIndex);
|
||||||
|
List<Entity> matchingEntities = this.workingSet.getAll().stream().filter(allowedEntities::contains).toList();
|
||||||
|
for (Entity e : matchingEntities) {
|
||||||
|
this.workingSet.remove(e);
|
||||||
|
this.matchedEntities[idx] = e;
|
||||||
|
if (this.match(idx + 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.workingSet.add(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,121 +0,0 @@
|
||||||
package game.logic.actionsystem;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import game.logic.GameLogic;
|
|
||||||
import game.state.Entity;
|
|
||||||
import game.state.EntitySet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A player action represents a single action which operates on any number of
|
|
||||||
* entities.
|
|
||||||
*
|
|
||||||
* It has an identifier, e.g. <code>"combine"</code>, a signature of needed
|
|
||||||
* entities, and an executor. The signature consists multiple entries, each one
|
|
||||||
* being either a single entity or a group of entities. To execute a player
|
|
||||||
* action an <a href="#EntitySet"><code>EntitySet</code></a> must be given which
|
|
||||||
* can be matched against its signature.
|
|
||||||
*/
|
|
||||||
public class PlayerAction {
|
|
||||||
private record SignatureEntry(EntitySet entities, int count) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final PlayerActionExecutor executor;
|
|
||||||
private final List<SignatureEntry> signatureEntries = new LinkedList<>();
|
|
||||||
private int neededEntitiesTotalCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a player action by providing an id and an executor.
|
|
||||||
*
|
|
||||||
* @param id The action id
|
|
||||||
* @param executor The executor which is called when
|
|
||||||
* <a href="#tryExecute">tryExecute</a> is called with an entity
|
|
||||||
* set which can be matched against the signature.
|
|
||||||
*/
|
|
||||||
public PlayerAction(String id, PlayerActionExecutor executor) {
|
|
||||||
this.id = id;
|
|
||||||
this.executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter for the action's id.
|
|
||||||
*
|
|
||||||
* @return The action id
|
|
||||||
*/
|
|
||||||
public String getId() {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes a new entry to this action' signature consisting of a single entity
|
|
||||||
* which is needed for execution.
|
|
||||||
*
|
|
||||||
* @param entity The needed entity to push
|
|
||||||
*/
|
|
||||||
public void pushNeededEntity(Entity entity) {
|
|
||||||
this.signatureEntries.add(new SignatureEntry(EntitySet.createTemporary(entity), 1));
|
|
||||||
++this.neededEntitiesTotalCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes a new entry to this action's signature consisting of a set of entities
|
|
||||||
* of which a specific count is needed for execution.
|
|
||||||
*
|
|
||||||
* @param entities The set of needed entities
|
|
||||||
* @param count How many entities of the given set are needed for execution
|
|
||||||
*/
|
|
||||||
public void pushVaryingNeededEntites(EntitySet entities, int count) {
|
|
||||||
this.signatureEntries.add(new SignatureEntry(entities, count));
|
|
||||||
this.neededEntitiesTotalCount += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to match the given entities to this action's signature and executes it
|
|
||||||
* if successful.
|
|
||||||
*
|
|
||||||
* @param primaryEntity An optional primary entity which will be matched
|
|
||||||
* against the first signature entry.
|
|
||||||
* @param secondaryEntities A set of entities which will be matched against the
|
|
||||||
* signature (excl. the first entry if
|
|
||||||
* <code>primaryEntity</code> was not
|
|
||||||
* <code>null</code>)
|
|
||||||
* @param logic The game logic
|
|
||||||
* @return <code>true</code>, if given entities match the signature and the
|
|
||||||
* action was executed
|
|
||||||
*/
|
|
||||||
public boolean tryExecute(Entity primaryEntity, EntitySet secondaryEntities, GameLogic logic) {
|
|
||||||
if ((primaryEntity == null ? 0 : 1)
|
|
||||||
+ (secondaryEntities == null ? 0 : secondaryEntities.getSize()) != this.neededEntitiesTotalCount) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int entityCounts[] = new int[this.signatureEntries.size()];
|
|
||||||
Entity entitiesToUse[] = new Entity[this.neededEntitiesTotalCount];
|
|
||||||
if (primaryEntity != null) {
|
|
||||||
if (this.signatureEntries.isEmpty() || !this.signatureEntries.get(0).entities().contains(primaryEntity)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
entitiesToUse[entityCounts[0]++] = primaryEntity;
|
|
||||||
}
|
|
||||||
if (secondaryEntities != null) {
|
|
||||||
for (Entity e : secondaryEntities.getAll()) {
|
|
||||||
int idx = 0;
|
|
||||||
for (int i = 0; i < this.signatureEntries.size(); ++i) {
|
|
||||||
SignatureEntry signatureEntry = this.signatureEntries.get(i);
|
|
||||||
if (entityCounts[i] != signatureEntry.count() && signatureEntry.entities().contains(e)) {
|
|
||||||
entitiesToUse[idx + entityCounts[i]++] = e;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
idx += signatureEntry.count();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Arrays.stream(entitiesToUse).anyMatch(Objects::isNull)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.executor.execute(logic, entitiesToUse);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,15 +3,9 @@ package game.state;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import game.logic.GameLogic;
|
|
||||||
import game.logic.actionsystem.PlayerAction;
|
|
||||||
import game.logic.actionsystem.PlayerActionExecutor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entities are the building blocks of the game logic's world.
|
* Entities are the building blocks of the game logic's world.
|
||||||
*
|
*
|
||||||
|
@ -23,17 +17,28 @@ import game.logic.actionsystem.PlayerActionExecutor;
|
||||||
* can store generic attributes.
|
* can store generic attributes.
|
||||||
*/
|
*/
|
||||||
public class Entity {
|
public class Entity {
|
||||||
|
private final GameState gameState;
|
||||||
private final String id;
|
private final String id;
|
||||||
final Set<EntitySet> containingPersistentSets = new HashSet<>();
|
final Set<EntitySet> containingPersistentSets = new HashSet<>();
|
||||||
private final Map<String, String> attributes = new HashMap<>();
|
private final Map<String, String> attributes = new HashMap<>();
|
||||||
private Entity location;
|
private Entity location;
|
||||||
private final EntitySet contents;
|
private final EntitySet contents;
|
||||||
private final Map<Entity, Entity> connections = new HashMap<>();
|
private final Map<Entity, Entity> connections = new HashMap<>();
|
||||||
private final Map<String, List<PlayerAction>> playerActions = new HashMap<>();
|
|
||||||
|
|
||||||
public Entity(String id) {
|
/**
|
||||||
|
* Creates an entity and registers it to the given game state. DO NOT CALL THIS
|
||||||
|
* CONSTRUCTOR DIRECTLY! Use {@link GameState#createEntity(String)},
|
||||||
|
* {@link EntitySet#createEntity(String)}, or
|
||||||
|
* {@link EntitySet#createEntity(String, Entity)} instead.
|
||||||
|
*
|
||||||
|
* @param state The game sate to register the entity to
|
||||||
|
* @param id The entity id
|
||||||
|
*/
|
||||||
|
public Entity(GameState gameState, String id) {
|
||||||
|
this.gameState = gameState;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.contents = EntitySet.createPersistent(this.id + "::contents");
|
this.gameState.registerEntity(this);
|
||||||
|
this.contents = this.gameState.createEntitySet(this.id + "::contents");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -81,17 +86,6 @@ public class Entity {
|
||||||
return this.contents.getAll().stream().anyMatch(e -> e == other || (recursive && e.contains(other, true)));
|
return this.contents.getAll().stream().anyMatch(e -> e == other || (recursive && e.contains(other, true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity createContainedEntity(GameLogic logic, String id) {
|
|
||||||
Entity e = logic.getState().createEntity(id);
|
|
||||||
try {
|
|
||||||
e.setLocation(this);
|
|
||||||
} catch (CircularLocationException ex) {
|
|
||||||
// should not happen
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity getLocation() {
|
public Entity getLocation() {
|
||||||
return this.location;
|
return this.location;
|
||||||
}
|
}
|
||||||
|
@ -128,60 +122,6 @@ public class Entity {
|
||||||
return Collections.unmodifiableSet(this.connections.keySet());
|
return Collections.unmodifiableSet(this.connections.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerAction pushPlayerAction(String id, PlayerActionExecutor executor) {
|
|
||||||
List<PlayerAction> l = this.playerActions.get(id);
|
|
||||||
if (l == null) {
|
|
||||||
this.playerActions.put(id, l = new LinkedList<>());
|
|
||||||
}
|
|
||||||
PlayerAction action = new PlayerAction(id, executor);
|
|
||||||
action.pushNeededEntity(this);
|
|
||||||
l.add(action);
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for the first player action that can operate on this entity and the
|
|
||||||
* given secondary entities and executes it if one is found.
|
|
||||||
*
|
|
||||||
* @param id The action id
|
|
||||||
* @param secondaryEntities The set of secondary entities on which the action
|
|
||||||
* should operate
|
|
||||||
* @return <code>true</code>, if an action was found and executed
|
|
||||||
*/
|
|
||||||
public boolean tryExecutePlayerAction(String id, EntitySet secondaryEntities, GameLogic logic) {
|
|
||||||
if (!this.tryExecutePlayerAction(this.playerActions.get(id), secondaryEntities, logic)) {
|
|
||||||
for (EntitySet set : this.containingPersistentSets) {
|
|
||||||
if (this.tryExecutePlayerAction(set.getPlayerActions(id), secondaryEntities, logic)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for the first player action that can operate on this entity and no
|
|
||||||
* secondary entities executes it if one is found.
|
|
||||||
*
|
|
||||||
* @param id The action id
|
|
||||||
* @return <code>true</code>, if an action was found and executed
|
|
||||||
*/
|
|
||||||
public boolean tryExecutePlayerAction(String id, GameLogic logic) {
|
|
||||||
return this.tryExecutePlayerAction(id, null, logic);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean tryExecutePlayerAction(List<PlayerAction> actions, EntitySet secondaryEntities, GameLogic logic) {
|
|
||||||
if (actions != null) {
|
|
||||||
for (PlayerAction a : actions) {
|
|
||||||
if (a.tryExecute(this, secondaryEntities, logic)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.id;
|
return this.id;
|
||||||
|
|
|
@ -3,42 +3,55 @@ package game.state;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import game.logic.GameLogic;
|
import game.logic.GameLogic;
|
||||||
import game.logic.actionsystem.PlayerAction;
|
|
||||||
import game.logic.actionsystem.PlayerActionExecutor;
|
|
||||||
|
|
||||||
public class EntitySet {
|
public class EntitySet {
|
||||||
public static EntitySet createTemporary(Entity... entities) {
|
private final GameState gameState;
|
||||||
return new EntitySet(null, Arrays.asList(entities));
|
private final String id;
|
||||||
}
|
|
||||||
|
|
||||||
public static EntitySet createTemporary(Collection<Entity> entities) {
|
|
||||||
return new EntitySet(null, entities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EntitySet createPersistent(String name, Entity... entities) {
|
|
||||||
return new EntitySet(name, Arrays.asList(entities));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EntitySet createPersistent(String name, Collection<Entity> entities) {
|
|
||||||
return new EntitySet(name, entities);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final Set<Entity> entities = new HashSet<>();
|
private final Set<Entity> entities = new HashSet<>();
|
||||||
private final Map<String, List<PlayerAction>> playerActions = new HashMap<>();
|
|
||||||
|
|
||||||
private EntitySet(String name, Collection<Entity> entities) {
|
/**
|
||||||
this.name = name;
|
* Creates a persistent, named entity set. DO NOT CALL THIS CONSTRUCTOR
|
||||||
this.add(entities.toArray(new Entity[0]));
|
* DIRECTLY. Use {@link GameState#createEntitySet(String)} instead.
|
||||||
|
*
|
||||||
|
* @param state The game state to register the entity set to
|
||||||
|
* @param id The entity set id
|
||||||
|
*/
|
||||||
|
public EntitySet(GameState gameState, String id) {
|
||||||
|
this.gameState = gameState;
|
||||||
|
this.id = id;
|
||||||
|
this.gameState.registerEntitySet(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a temporary entity set.
|
||||||
|
*
|
||||||
|
* @param entities Initial entities contained in the set
|
||||||
|
*/
|
||||||
|
public EntitySet(Collection<Entity> entities) {
|
||||||
|
this.id = null;
|
||||||
|
this.gameState = null;
|
||||||
|
this.entities.addAll(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a temporary entity set.
|
||||||
|
*
|
||||||
|
* @param entities Initial entities contained in the set
|
||||||
|
*/
|
||||||
|
public EntitySet(Entity... entities) {
|
||||||
|
this(Arrays.asList(entities));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPersistent() {
|
||||||
|
return this.id != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
|
@ -53,17 +66,21 @@ public class EntitySet {
|
||||||
return this.entities.size();
|
return this.entities.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Entity... entities) {
|
public void add(Collection<Entity> entities) {
|
||||||
for (Entity e : entities) {
|
for (Entity e : entities) {
|
||||||
if (this.entities.add(e) && this.name != null) {
|
if (this.entities.add(e) && this.id != null) {
|
||||||
e.containingPersistentSets.add(this);
|
e.containingPersistentSets.add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void add(Entity... entities) {
|
||||||
|
this.add(Arrays.asList(entities));
|
||||||
|
}
|
||||||
|
|
||||||
public void remove(Entity... entities) {
|
public void remove(Entity... entities) {
|
||||||
for (Entity e : entities) {
|
for (Entity e : entities) {
|
||||||
if (this.entities.remove(e) && this.name != null) {
|
if (this.entities.remove(e) && this.id != null) {
|
||||||
e.containingPersistentSets.remove(this);
|
e.containingPersistentSets.remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +90,18 @@ public class EntitySet {
|
||||||
return this.entities.contains(entity);
|
return this.entities.contains(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Entity createEntity(String id) {
|
||||||
|
Entity e = this.gameState.createEntity(id);
|
||||||
|
this.add(e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity createEntity(String id, Entity location) throws CircularLocationException {
|
||||||
|
Entity e = this.createEntity(id);
|
||||||
|
e.setLocation(location);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
public Entity collapse(GameLogic logic) {
|
public Entity collapse(GameLogic logic) {
|
||||||
if (this.entities.size() <= 1) {
|
if (this.entities.size() <= 1) {
|
||||||
return this.entities.stream().findAny().orElse(null);
|
return this.entities.stream().findAny().orElse(null);
|
||||||
|
@ -81,24 +110,4 @@ public class EntitySet {
|
||||||
return this.entities.stream().findAny().orElse(null);
|
return this.entities.stream().findAny().orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySet getFiltered(Predicate<Entity> acceptFunction) {
|
|
||||||
return EntitySet.createTemporary(this.entities.stream().filter(acceptFunction).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerAction pushPlayerAction(String id, PlayerActionExecutor executor) {
|
|
||||||
List<PlayerAction> l = this.playerActions.get(id);
|
|
||||||
if (l == null) {
|
|
||||||
this.playerActions.put(id, l = new LinkedList<>());
|
|
||||||
}
|
|
||||||
PlayerAction action = new PlayerAction(id, executor);
|
|
||||||
action.pushVaryingNeededEntites(this, 1);
|
|
||||||
l.add(action);
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PlayerAction> getPlayerActions(String id) {
|
|
||||||
List<PlayerAction> l = this.playerActions.get(id);
|
|
||||||
return l == null ? Collections.emptyList() : Collections.unmodifiableList(this.playerActions.get(id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,82 @@ import java.util.Map;
|
||||||
|
|
||||||
public class GameState {
|
public class GameState {
|
||||||
private final Map<String, Entity> entities = new HashMap<>();
|
private final Map<String, Entity> entities = new HashMap<>();
|
||||||
|
private final Map<String, EntitySet> entitySets = new HashMap<>();
|
||||||
|
|
||||||
public GameState() {
|
public GameState(String savegameJsonPath) {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* TODO: load from savegame JSON
|
||||||
|
*/
|
||||||
|
EntitySet genericDirections = this.createEntitySet("genericDirections");
|
||||||
|
Entity west = genericDirections.createEntity("west");
|
||||||
|
Entity east = genericDirections.createEntity("east");
|
||||||
|
|
||||||
|
EntitySet houseDirections = this.createEntitySet("houseDirections");
|
||||||
|
Entity inside = houseDirections.createEntity("inside");
|
||||||
|
Entity outside = houseDirections.createEntity("outside");
|
||||||
|
|
||||||
|
EntitySet locations = this.createEntitySet("locations");
|
||||||
|
Entity forestPath01 = locations.createEntity("forestPath01");
|
||||||
|
Entity clearing = locations.createEntity("clearing");
|
||||||
|
Entity houseOutside = locations.createEntity("houseOutside");
|
||||||
|
Entity houseInside = locations.createEntity("houseInside");
|
||||||
|
locations.createEntity("houseMainDoor");
|
||||||
|
|
||||||
|
forestPath01.connectBidirectional(east, west, clearing);
|
||||||
|
forestPath01.connectBidirectional(west, east, houseOutside);
|
||||||
|
houseOutside.connectBidirectional(inside, outside, houseInside);
|
||||||
|
|
||||||
|
EntitySet characters = this.createEntitySet("characters");
|
||||||
|
characters.createEntity("player", clearing);
|
||||||
|
|
||||||
|
EntitySet collectibles = this.createEntitySet("collectibles");
|
||||||
|
collectibles.createEntity("apple01", forestPath01);
|
||||||
|
collectibles.createEntity("apple02", forestPath01);
|
||||||
|
} catch (CircularLocationException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity createEntity(String id) {
|
public Entity createEntity(String id) {
|
||||||
Entity e = new Entity(id);
|
if (this.entities.containsKey(id)) {
|
||||||
this.entities.put(id, e);
|
throw new UnsupportedOperationException("duplicate entity id: " + id);
|
||||||
return e;
|
}
|
||||||
|
return new Entity(this, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity getEntityById(String id) {
|
public Entity getEntityById(String id) {
|
||||||
return this.entities.get(id);
|
return this.entities.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntitySet createEntitySet(String id) {
|
||||||
|
if (this.entitySets.containsKey(id)) {
|
||||||
|
throw new UnsupportedOperationException("duplicate entity set id: " + id);
|
||||||
|
}
|
||||||
|
return new EntitySet(this, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySet getEntitySetById(String id) {
|
||||||
|
return this.entitySets.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an entity to the game state. DO NOT CALL THIS OUTSIDE OF THE ENTITY
|
||||||
|
* CONSTRUCTOR!
|
||||||
|
*
|
||||||
|
* @param entity The entity to register
|
||||||
|
*/
|
||||||
|
public void registerEntity(Entity entity) {
|
||||||
|
this.entities.put(entity.getId(), entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an entity set to the game state. DO NOT CALL THIS OUTSIDE OF THE
|
||||||
|
* ENTITY SET CONSTRUCTOR!
|
||||||
|
*
|
||||||
|
* @param entitySet The entity set to register
|
||||||
|
*/
|
||||||
|
public void registerEntitySet(EntitySet entitySet) {
|
||||||
|
this.entitySets.put(entitySet.getId(), entitySet);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue