mirror of
https://github.com/schmaeddes/untitledTextAdventure.git
synced 2024-11-25 08:50:18 +01:00
Compare commits
2 commits
b971d5a1ab
...
976acb8d41
Author | SHA1 | Date | |
---|---|---|---|
|
976acb8d41 | ||
|
75eb4771c3 |
14 changed files with 549 additions and 354 deletions
29
pom.xml
29
pom.xml
|
@ -1,7 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>17</maven.compiler.source>
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
@ -11,4 +9,29 @@
|
||||||
<groupId>schmaeddes</groupId>
|
<groupId>schmaeddes</groupId>
|
||||||
<artifactId>untitledTextAdventure</artifactId>
|
<artifactId>untitledTextAdventure</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.graalvm.js/js -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.js</groupId>
|
||||||
|
<artifactId>js</artifactId>
|
||||||
|
<version>22.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.graalvm.js/js-scriptengine -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.js</groupId>
|
||||||
|
<artifactId>js-scriptengine</artifactId>
|
||||||
|
<version>22.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.graalvm.truffle/truffle-api -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.truffle</groupId>
|
||||||
|
<artifactId>truffle-api</artifactId>
|
||||||
|
<version>22.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -1,8 +1,18 @@
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
|
import org.graalvm.polyglot.Context;
|
||||||
|
import org.graalvm.polyglot.Engine;
|
||||||
|
import org.graalvm.polyglot.HostAccess;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -12,6 +22,18 @@ public class Main {
|
||||||
LoadStuff loadStuff = new LoadStuff();
|
LoadStuff loadStuff = new LoadStuff();
|
||||||
loadStuff.load(Environment.instance);
|
loadStuff.load(Environment.instance);
|
||||||
|
|
||||||
|
ScriptEngine jse = GraalJSScriptEngine.create(
|
||||||
|
Engine.newBuilder().option("engine.WarnInterpreterOnly", "false").build(),
|
||||||
|
Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowHostClassLookup(s -> true));
|
||||||
|
try {
|
||||||
|
Entity t = new Entity("test");
|
||||||
|
jse.put("test", t);
|
||||||
|
jse.eval("console.log(test.toString());");
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
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.json");
|
||||||
|
|
|
@ -2,14 +2,12 @@ package game.logic;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.Map;
|
||||||
|
|
||||||
import game.logic.actionsystem.Action;
|
import game.logic.actionsystem.PlayerAction;
|
||||||
import game.logic.actionsystem.actions.GoDirection;
|
|
||||||
import game.logic.actionsystem.actions.Open;
|
|
||||||
import game.logic.actionsystem.actions.TakeFrom;
|
|
||||||
import game.state.CircularLocationException;
|
import game.state.CircularLocationException;
|
||||||
import game.state.Entity;
|
import game.state.Entity;
|
||||||
import game.state.EntitySet;
|
import game.state.EntitySet;
|
||||||
|
@ -20,6 +18,7 @@ public class GameLogic implements Closeable {
|
||||||
private GameState state;
|
private GameState state;
|
||||||
private Entity player;
|
private Entity player;
|
||||||
private boolean discontinue = false;
|
private boolean discontinue = false;
|
||||||
|
private Map<String, List<PlayerAction>> playerActions = new HashMap<>();
|
||||||
|
|
||||||
public GameLogic(Parser parser) {
|
public GameLogic(Parser parser) {
|
||||||
this.parser = parser;
|
this.parser = parser;
|
||||||
|
@ -31,135 +30,175 @@ public class GameLogic implements Closeable {
|
||||||
// TODO setup code, load from json or so
|
// TODO setup code, load from json or so
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// final String NORTH = "north";
|
Entity east = this.state.createEntity("east");
|
||||||
// final String SOUTH = "south";
|
Entity west = this.state.createEntity("west");
|
||||||
final String EAST = "east";
|
Entity inside = this.state.createEntity("inside");
|
||||||
final String WEST = "west";
|
Entity outside = this.state.createEntity("outside");
|
||||||
final String INSIDE = "inside";
|
EntitySet genericDirections = EntitySet.createPersistent("genericDirections");
|
||||||
final String OUTSIDE = "outside";
|
genericDirections.add(east, west);
|
||||||
|
EntitySet houseDirections = EntitySet.createPersistent("houseDirections");
|
||||||
|
houseDirections.add(inside, outside);
|
||||||
|
|
||||||
Entity forestPath01 = this.state.createEntity("forest_path_01");
|
Entity forestPath01 = this.state.createEntity("forest_path_01");
|
||||||
Entity clearing = this.state.createEntity("clearing");
|
Entity clearing = this.state.createEntity("clearing");
|
||||||
Entity houseOutside = this.state.createEntity("house_outside");
|
Entity houseOutside = this.state.createEntity("house_outside");
|
||||||
Entity houseInside = this.state.createEntity("house_inside");
|
Entity houseInside = this.state.createEntity("house_inside");
|
||||||
Entity houseMainDoor = this.state.createEntity("house_main_door");
|
Entity houseMainDoor = this.state.createEntity("house_main_door");
|
||||||
houseMainDoor.setClosed(true);
|
|
||||||
|
|
||||||
forestPath01.connectBidirectional(EAST, null, WEST, clearing);
|
EntitySet locations = EntitySet.createPersistent("locations");
|
||||||
forestPath01.connectBidirectional(WEST, null, EAST, houseOutside);
|
locations.add(forestPath01, clearing, houseInside, houseOutside);
|
||||||
houseOutside.connectBidirectional(INSIDE, houseMainDoor, OUTSIDE, houseInside);
|
|
||||||
|
|
||||||
this.player = this.state.createEntity("player");
|
forestPath01.connectBidirectional(east, west, clearing);
|
||||||
Entity apple01 = this.state.createEntity("apple_01", "green");
|
forestPath01.connectBidirectional(west, east, houseOutside);
|
||||||
Entity apple02 = this.state.createEntity("apple_02", "red");
|
houseOutside.connectBidirectional(inside, outside, houseInside);
|
||||||
|
|
||||||
this.player.setLocation(clearing);
|
this.player = clearing.createContainedEntity(this, "player");
|
||||||
apple01.setLocation(forestPath01);
|
Entity apple01 = forestPath01.createContainedEntity(this, "apple_01");
|
||||||
apple02.setLocation(forestPath01);
|
Entity apple02 = forestPath01.createContainedEntity(this, "apple_02");
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
EntitySet collectibles = EntitySet.createPersistent("collectibles");
|
||||||
// TODO game specific action parsers
|
collectibles.add(apple01, apple02);
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
this.parser.pushActionParser(userInput -> userInput.get(0).equals("go")
|
collectibles.pushPlayerAction("take", (logic, args) -> {
|
||||||
? new GoDirection(userInput.size() < 2 ? null : userInput.get(1))
|
Entity collectible = args[0];
|
||||||
: null);
|
if (logic.getPlayer().getLocation() == collectible.getLocation()) {
|
||||||
this.parser.pushActionParser(userInput -> {
|
try {
|
||||||
if (userInput.get(0).equals("take")) {
|
collectible.setLocation(logic.getPlayer());
|
||||||
int fromIdx = userInput.indexOf("from");
|
logic.printRaw("Du nimmst %s, du Schuft.\n", collectible);
|
||||||
List<EntityDescription> whatToTake = this
|
} catch (CircularLocationException ex) {
|
||||||
.parseDescriptionList(userInput.subList(1, fromIdx == -1 ? userInput.size() : fromIdx));
|
// Should not happpen
|
||||||
List<EntityDescription> whereToTakeFrom = fromIdx == -1 ? null
|
ex.printStackTrace();
|
||||||
: this.parseDescriptionList(userInput.subList(fromIdx + 1, userInput.size()));
|
}
|
||||||
return new TakeFrom(whatToTake, whereToTakeFrom);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
this.parser.pushActionParser(userInput -> {
|
|
||||||
if (userInput.get(0).equals("open")) {
|
// this.player becomes first argument by calling pushPlayerAction on it
|
||||||
return new Open(this.parseDescriptionList(userInput.subList(1, userInput.size())));
|
PlayerAction goAction = this.player.pushPlayerAction("go", (logic, args) -> {
|
||||||
|
Entity character = args[0];
|
||||||
|
Entity direction = args[1];
|
||||||
|
Entity newLocation = character.getLocation().getConnectedEntity(direction);
|
||||||
|
if (newLocation != null) {
|
||||||
|
try {
|
||||||
|
logic.getPlayer().setLocation(newLocation);
|
||||||
|
logic.printRaw("Du gehst Richtung %s und landest hier: %s, du Lutscher!\n", direction, newLocation);
|
||||||
|
} catch (CircularLocationException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
logic.printRaw("Hier geht es nicht nach %s, du Nichtsnutz.\n", direction);
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
PlayerAction goHouseAction = this.player.pushPlayerAction("go", (logic, args) -> {
|
||||||
|
Entity character = args[0];
|
||||||
|
Entity direction = args[1];
|
||||||
|
Entity newLocation = character.getLocation().getConnectedEntity(direction);
|
||||||
|
if (newLocation != null && character.getLocation() == houseInside
|
||||||
|
|| character.getLocation() == houseOutside) {
|
||||||
|
if (houseMainDoor.getBoolAttribute("open")) {
|
||||||
|
try {
|
||||||
|
character.setLocation(newLocation);
|
||||||
|
} catch (CircularLocationException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
logic.printRaw("Du gehst durch die Tür, du Eumel.\n");
|
||||||
|
} else {
|
||||||
|
logic.printRaw("Die Tür ist zu, du Dödel.\n");
|
||||||
|
}
|
||||||
|
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 false;
|
||||||
|
});
|
||||||
|
|
||||||
|
houseMainDoor.pushPlayerAction("close", (logic, args) -> {
|
||||||
|
if (logic.getPlayer().getLocation() == houseInside || logic.getPlayer().getLocation() == houseOutside) {
|
||||||
|
if (!houseMainDoor.getBoolAttribute("open")) {
|
||||||
|
logic.printRaw("Die Tür ist schon geschlossen, du Mummenschanz.\n");
|
||||||
|
} else {
|
||||||
|
houseMainDoor.setAttribute("open", false);
|
||||||
|
logic.printRaw("Du schließt die Tür, du Angsthase.\n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameState getState() {
|
||||||
|
return this.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity getPlayer() {
|
public Entity getPlayer() {
|
||||||
return this.player;
|
return this.player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySet searchForEntity(EntityDescription description) {
|
|
||||||
return this.state.searchForEntity(description);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntitySet searchForEntity(EntityDescription description, Predicate<Entity> acceptFunction) {
|
|
||||||
return this.state.searchForEntity(description).getFiltered(acceptFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntitySet searchForNearbyEntity(EntityDescription description) {
|
|
||||||
return this.searchForEntity(description, e -> {
|
|
||||||
if (this.player == null) {
|
|
||||||
return false;
|
|
||||||
} else if (this.player.contains(e, false)) {
|
|
||||||
return true;
|
|
||||||
} else if (this.player.getLocation() != null && this.player.getLocation().contains(e, false)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
for (Entity.EntityConnection c : this.player.getLocation().getConnections().stream()
|
|
||||||
.map(this.player.getLocation()::getConnection).toList()) {
|
|
||||||
if (c.associatedEntity() == e) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mainLoop() {
|
public void mainLoop() {
|
||||||
while (!this.discontinue) {
|
while (!this.discontinue) {
|
||||||
Action action = this.parser.readAction();
|
this.parser.executeUserInput(this);
|
||||||
if (action != null) {
|
}
|
||||||
action.execute(this);
|
}
|
||||||
|
|
||||||
|
public void printRaw(String rawMessage, Object... args) {
|
||||||
|
System.out.printf(rawMessage, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerPlayerAction(PlayerAction action) {
|
||||||
|
List<PlayerAction> l = this.playerActions.get(action.getId());
|
||||||
|
if (l == null) {
|
||||||
|
this.playerActions.put(action.getId(), l = new LinkedList<>());
|
||||||
|
}
|
||||||
|
l.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for the first player action that does not use any entity and
|
||||||
|
* 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) {
|
||||||
|
return this.tryExecutePlayerAction(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for the first player action that can operate on the given set of
|
||||||
|
* entities and executes it if one is found.
|
||||||
|
*
|
||||||
|
* @param id The action id
|
||||||
|
* @param entities The entities on which the action shoud operate
|
||||||
|
* @return <code>true</code>, if an action was found and executed
|
||||||
|
*/
|
||||||
|
public boolean tryExecutePlayerAction(String id, EntitySet entities) {
|
||||||
|
List<PlayerAction> l = this.playerActions.get(id);
|
||||||
|
if (l != null) {
|
||||||
|
for (PlayerAction a : l) {
|
||||||
|
if (a.tryExecute(null, entities, this)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
|
||||||
private List<EntityDescription> parseDescriptionList(List<String> words) {
|
|
||||||
List<EntityDescription> descriptions = new LinkedList<>();
|
|
||||||
List<String> desc = new LinkedList<>();
|
|
||||||
for (String word : words) {
|
|
||||||
if (word.equals("and")) {
|
|
||||||
descriptions.add(new EntityDescription(desc));
|
|
||||||
desc = new LinkedList<>();
|
|
||||||
} else {
|
|
||||||
desc.add(word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!desc.isEmpty()) {
|
|
||||||
descriptions.add(new EntityDescription(desc));
|
|
||||||
}
|
|
||||||
return descriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void printToUser(String messageId, Object... args) {
|
|
||||||
System.out.print(messageId);
|
|
||||||
for (int i = 0; i < args.length; ++i) {
|
|
||||||
System.out.print(i == 0 ? " " : ", ");
|
|
||||||
System.out.print(args[i]);
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canPlayerOpen(Entity entity) {
|
|
||||||
// TODO
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canPlayerTake(Entity entity) {
|
|
||||||
// TODO
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,33 +2,53 @@ package game.logic;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
import game.logic.actionsystem.Action;
|
import game.state.Entity;
|
||||||
import game.logic.actionsystem.ActionParser;
|
import game.state.EntitySet;
|
||||||
import startup.Environment;
|
import startup.Environment;
|
||||||
import util.Commands;
|
import util.Commands;
|
||||||
import util.TextColors;
|
import util.TextColors;
|
||||||
|
|
||||||
public class Parser implements Closeable {
|
public class Parser implements Closeable {
|
||||||
private final Scanner scanner = new Scanner(System.in);
|
private final Scanner scanner = new Scanner(System.in);
|
||||||
private final List<ActionParser> actionParsers = new LinkedList<>();
|
|
||||||
|
|
||||||
public Action readAction() {
|
public void executeUserInput(GameLogic logic) {
|
||||||
|
// Big TODO
|
||||||
|
// currently just reads an action id followed by entity ids
|
||||||
|
|
||||||
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.stream(scanner.nextLine().split("\\s+")).map(String::toLowerCase).toList();
|
||||||
|
if (!input.isEmpty()) {
|
||||||
for (ActionParser actionParser : this.actionParsers) {
|
String actionId = input.get(0);
|
||||||
Action action = actionParser.parseAction(input);
|
List<Entity> args = new ArrayList<>(input.size() - 1);
|
||||||
if (action != null) {
|
for (int i = 1; i < input.size(); ++i) {
|
||||||
return action;
|
Entity e = logic.getState().getEntityById(input.get(i));
|
||||||
|
if (e == null) {
|
||||||
|
logic.printRaw("Keine Ahnung, was du mit %s meinst, du Tölpel.\n", input.get(i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
args.add(e);
|
||||||
|
}
|
||||||
|
Entity primaryEntity = this.getPrimaryEntity(logic, actionId, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
@ -40,10 +60,6 @@ public class Parser implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushActionParser(ActionParser actionParser) {
|
|
||||||
this.actionParsers.add(actionParser);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
this.scanner.close();
|
this.scanner.close();
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
package game.logic.actionsystem;
|
|
||||||
|
|
||||||
import game.logic.GameLogic;
|
|
||||||
|
|
||||||
public abstract class Action {
|
|
||||||
public enum Type {
|
|
||||||
TAKE, DROP,
|
|
||||||
COMBINE_WITH, USE,
|
|
||||||
GIVE_TO, TAKE_FROM,
|
|
||||||
PUT_ON,
|
|
||||||
PUSH, PULL, ROLL, ROLL_TO,
|
|
||||||
TRACE_RAY_TO,
|
|
||||||
KILL, KILL_WITH,
|
|
||||||
SEARCH, SEARCH_FOR,
|
|
||||||
TALK_TO, TELL_TO, ANNOY,
|
|
||||||
LOOK_AT, EXAMINE, READ, WRITE_ON_WITH,
|
|
||||||
HIT, HIT_WITH,
|
|
||||||
GO_TO,
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void execute(GameLogic logic);
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package game.logic.actionsystem;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface ActionParser {
|
|
||||||
public Action parseAction(List<String> userInput);
|
|
||||||
}
|
|
121
src/main/java/game/logic/actionsystem/PlayerAction.java
Normal file
121
src/main/java/game/logic/actionsystem/PlayerAction.java
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package game.logic.actionsystem;
|
||||||
|
|
||||||
|
import game.logic.GameLogic;
|
||||||
|
import game.state.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A player action executor provides specific instructions how to manipulate the
|
||||||
|
* game logic.
|
||||||
|
*/
|
||||||
|
public interface PlayerActionExecutor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the game logic manupulation.
|
||||||
|
*
|
||||||
|
* @param logic The game logic
|
||||||
|
* @param args Arguments
|
||||||
|
*/
|
||||||
|
public boolean execute(GameLogic logic, Entity... args);
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
package game.logic.actionsystem.actions;
|
|
||||||
|
|
||||||
import game.logic.GameLogic;
|
|
||||||
import game.logic.actionsystem.Action;
|
|
||||||
import game.state.CircularLocationException;
|
|
||||||
import game.state.Entity;
|
|
||||||
|
|
||||||
public class GoDirection extends Action {
|
|
||||||
private final String directionId;
|
|
||||||
|
|
||||||
public GoDirection(String directionId) {
|
|
||||||
this.directionId = directionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDirectionId() {
|
|
||||||
return this.directionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(GameLogic logic) {
|
|
||||||
if (logic.getPlayer() == null) {
|
|
||||||
logic.printToUser("go.player.null");
|
|
||||||
} else {
|
|
||||||
Entity location = logic.getPlayer().getLocation();
|
|
||||||
if (location == null) {
|
|
||||||
logic.printToUser("go.player.location.null", logic.getPlayer());
|
|
||||||
} else {
|
|
||||||
Entity.EntityConnection connection = location.getConnection(this.directionId);
|
|
||||||
if (connection == null || connection.to() == null) {
|
|
||||||
logic.printToUser("go.direction.unknown", logic.getPlayer().getLocation(), this.directionId);
|
|
||||||
} else if (connection.associatedEntity() != null && connection.associatedEntity().isClosed()) {
|
|
||||||
logic.printToUser("go.location.closed", connection.associatedEntity());
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
logic.getPlayer().setLocation(connection.to());
|
|
||||||
logic.printToUser("go.success", location, connection.to());
|
|
||||||
} catch (CircularLocationException ex) {
|
|
||||||
logic.printToUser("go.locationCircular", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package game.logic.actionsystem.actions;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import game.logic.EntityDescription;
|
|
||||||
import game.logic.GameLogic;
|
|
||||||
import game.logic.actionsystem.Action;
|
|
||||||
import game.state.Entity;
|
|
||||||
import game.state.EntitySet;
|
|
||||||
|
|
||||||
public class Open extends Action {
|
|
||||||
private final List<EntityDescription> descriptions;
|
|
||||||
|
|
||||||
public Open(List<EntityDescription> descriptions) {
|
|
||||||
this.descriptions = descriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(GameLogic logic) {
|
|
||||||
for (EntitySet set : this.descriptions.stream().map(logic::searchForNearbyEntity).toList()) {
|
|
||||||
Entity e = set.collapse(logic);
|
|
||||||
if(e == null) {
|
|
||||||
logic.printToUser("open.noEntity");
|
|
||||||
} else if (logic.canPlayerOpen(e)) {
|
|
||||||
e.setClosed(false);
|
|
||||||
logic.printToUser("open.success", e);
|
|
||||||
} else {
|
|
||||||
logic.printToUser("open.failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package game.logic.actionsystem.actions;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import game.logic.EntityDescription;
|
|
||||||
import game.logic.GameLogic;
|
|
||||||
import game.logic.actionsystem.Action;
|
|
||||||
import game.state.CircularLocationException;
|
|
||||||
import game.state.Entity;
|
|
||||||
import game.state.EntitySet;
|
|
||||||
|
|
||||||
public class TakeFrom extends Action {
|
|
||||||
private final List<EntityDescription> what;
|
|
||||||
private final List<EntityDescription> fromWhere;
|
|
||||||
|
|
||||||
public TakeFrom(List<EntityDescription> what, List<EntityDescription> fromWhere) {
|
|
||||||
this.what = what;
|
|
||||||
this.fromWhere = fromWhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EntityDescription> getWhat() {
|
|
||||||
return this.what;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EntityDescription> getFromWhere() {
|
|
||||||
return this.fromWhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(GameLogic logic) {
|
|
||||||
// TODO incorporate fromWhere
|
|
||||||
for (EntityDescription ed : this.what) {
|
|
||||||
EntitySet es = logic.searchForNearbyEntity(ed);
|
|
||||||
if(es.isEmpty()) {
|
|
||||||
logic.printToUser("entity.notFound", ed);
|
|
||||||
}
|
|
||||||
Entity e = es.collapse(logic);
|
|
||||||
if (e != null && logic.canPlayerTake(e)) {
|
|
||||||
try {
|
|
||||||
e.setLocation(logic.getPlayer());
|
|
||||||
logic.printToUser("take.success", e);
|
|
||||||
} catch (CircularLocationException ex) {
|
|
||||||
logic.printToUser("take.error", ex);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logic.printToUser("take.failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +1,64 @@
|
||||||
package game.state;
|
package game.state;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
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.
|
||||||
|
*
|
||||||
|
* They can represent objects, creatures, the player(s), locations, and more.
|
||||||
|
* Each entity can also contain zero or more other entities, working as an
|
||||||
|
* abstract container. It can be connected to other entities through keywords,
|
||||||
|
* e.g. north, south, west, east. A connection can also be associated with
|
||||||
|
* another entity to model doors or other types of portals. Finally, an entity
|
||||||
|
* can store generic attributes.
|
||||||
|
*/
|
||||||
public class Entity {
|
public class Entity {
|
||||||
public record EntityConnection(Entity to, Entity associatedEntity) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final Set<String> attributes = new HashSet<>();
|
final Set<EntitySet> containingPersistentSets = new HashSet<>();
|
||||||
|
private final Map<String, String> attributes = new HashMap<>();
|
||||||
private Entity location;
|
private Entity location;
|
||||||
private boolean closed = false;
|
private final EntitySet contents;
|
||||||
private final EntitySet contents = new EntitySet();
|
private final Map<Entity, Entity> connections = new HashMap<>();
|
||||||
private final Map<String, EntityConnection> connections = new HashMap<>();
|
private final Map<String, List<PlayerAction>> playerActions = new HashMap<>();
|
||||||
|
|
||||||
public Entity(String id, String... attributes) {
|
public Entity(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.attributes.addAll(Arrays.asList(attributes));
|
this.contents = EntitySet.createPersistent(this.id + "::contents");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getAttributes() {
|
public String getAttribute(String key) {
|
||||||
return Collections.unmodifiableSet(this.attributes);
|
String value = this.attributes.get(key);
|
||||||
|
return value == null ? "" : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addAttribute(String attribute) {
|
public boolean getBoolAttribute(String key) {
|
||||||
return this.attributes.add(attribute);
|
return Boolean.parseBoolean(this.getAttribute(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeAttribute(String attribute) {
|
public void setAttribute(String key, Object value) {
|
||||||
return this.attributes.remove(attribute);
|
if (value == null) {
|
||||||
}
|
this.attributes.remove(key);
|
||||||
|
} else {
|
||||||
public void toggleAttribute(String attribute) {
|
this.attributes.put(key, value.toString());
|
||||||
if (!this.attributes.remove(attribute)) {
|
|
||||||
this.attributes.add(attribute);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchAttribute(String attr1, String attr2) {
|
public void removeAttribute(String key) {
|
||||||
if (this.attributes.remove(attr1)) {
|
this.setAttribute(key, null);
|
||||||
this.attributes.add(attr2);
|
|
||||||
} else if (this.attributes.remove(attr2)) {
|
|
||||||
this.attributes.add(attr1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocation(Entity location) throws CircularLocationException {
|
public void setLocation(Entity location) throws CircularLocationException {
|
||||||
|
@ -70,18 +77,21 @@ public class Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isClosed() {
|
|
||||||
return this.closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClosed(boolean closed) {
|
|
||||||
this.closed = closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean contains(Entity other, boolean recursive) {
|
public boolean contains(Entity other, boolean recursive) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -90,34 +100,88 @@ public class Entity {
|
||||||
return this.contents.getAll();
|
return this.contents.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectUnidirectional(String directionId, Entity to, Entity associatedEntity) {
|
public void connectUnidirectional(Entity direction, Entity to) {
|
||||||
this.connections.put(directionId, new EntityConnection(to, associatedEntity));
|
this.connections.put(direction, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectBidirectional(String dirIdFromThisToOther, Entity associatedEntity, String dirIdFromOtherToThis, Entity to) {
|
public void connectBidirectional(Entity dirFromThisToOther, Entity dirFromOtherToThis, Entity other) {
|
||||||
this.connections.put(dirIdFromThisToOther, new EntityConnection(to, associatedEntity));
|
this.connections.put(dirFromThisToOther, other);
|
||||||
to.connections.put(dirIdFromOtherToThis, new EntityConnection(this, associatedEntity));
|
other.connections.put(dirFromOtherToThis, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSingleConnection(String directionId) {
|
public void removeSingleConnection(Entity direction) {
|
||||||
this.connections.remove(directionId);
|
this.connections.remove(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeBidirectionalConnection(String dirIdFromThisToOther, String dirIdFromOtherToThis) {
|
public void removeBidirectionalConnection(Entity dirFromThisToOther, Entity dirFromOtherToThis) {
|
||||||
EntityConnection c = this.connections.remove(dirIdFromThisToOther);
|
Entity other = this.connections.remove(dirFromThisToOther);
|
||||||
if (c != null) {
|
if (other != null && other.connections.get(dirFromOtherToThis) == this) {
|
||||||
c.to.connections.remove(dirIdFromOtherToThis);
|
other.connections.remove(dirFromOtherToThis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityConnection getConnection(String directionId) {
|
public Entity getConnectedEntity(Entity direction) {
|
||||||
return this.connections.get(directionId);
|
return this.connections.get(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getConnections() {
|
public Set<Entity> getConnectionDirections() {
|
||||||
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,21 +3,42 @@ 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 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 {
|
||||||
private final Set<Entity> entities;
|
public static EntitySet createTemporary(Entity... entities) {
|
||||||
|
return new EntitySet(null, Arrays.asList(entities));
|
||||||
public EntitySet(Entity... entities) {
|
|
||||||
this(Arrays.asList(entities));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySet(Collection<Entity> entities) {
|
public static EntitySet createTemporary(Collection<Entity> entities) {
|
||||||
this.entities = new HashSet<>(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 Map<String, List<PlayerAction>> playerActions = new HashMap<>();
|
||||||
|
|
||||||
|
private EntitySet(String name, Collection<Entity> entities) {
|
||||||
|
this.name = name;
|
||||||
|
this.add(entities.toArray(new Entity[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
|
@ -28,12 +49,24 @@ public class EntitySet {
|
||||||
return Collections.unmodifiableSet(this.entities);
|
return Collections.unmodifiableSet(this.entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean add(Entity entity) {
|
public int getSize() {
|
||||||
return this.entities.add(entity);
|
return this.entities.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean remove(Entity entity) {
|
public void add(Entity... entities) {
|
||||||
return this.entities.remove(entity);
|
for (Entity e : entities) {
|
||||||
|
if (this.entities.add(e) && this.name != null) {
|
||||||
|
e.containingPersistentSets.add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Entity... entities) {
|
||||||
|
for (Entity e : entities) {
|
||||||
|
if (this.entities.remove(e) && this.name != null) {
|
||||||
|
e.containingPersistentSets.remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(Entity entity) {
|
public boolean contains(Entity entity) {
|
||||||
|
@ -41,7 +74,7 @@ public class EntitySet {
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
} else {
|
} else {
|
||||||
// TODO if more than 1 candidate, ask user to specify
|
// TODO if more than 1 candidate, ask user to specify
|
||||||
|
@ -50,6 +83,22 @@ public class EntitySet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySet getFiltered(Predicate<Entity> acceptFunction) {
|
public EntitySet getFiltered(Predicate<Entity> acceptFunction) {
|
||||||
return new EntitySet(this.entities.stream().filter(acceptFunction).toList());
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,22 @@
|
||||||
package game.state;
|
package game.state;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
|
|
||||||
import game.logic.EntityDescription;
|
|
||||||
|
|
||||||
public class GameState {
|
public class GameState {
|
||||||
private final List<Entity> entities = new LinkedList<>();
|
private final Map<String, Entity> entities = new HashMap<>();
|
||||||
|
|
||||||
public GameState() {
|
public GameState() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity createEntity(String id, String... attributes) {
|
public Entity createEntity(String id) {
|
||||||
Entity e = new Entity(id, attributes);
|
Entity e = new Entity(id);
|
||||||
this.entities.add(e);
|
this.entities.put(id, e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntitySet searchForEntity(EntityDescription description) {
|
public Entity getEntityById(String id) {
|
||||||
return new EntitySet(this.entities.stream().filter(e -> e.getId().equals(description.getMainWord())
|
return this.entities.get(id);
|
||||||
&& e.getAttributes().containsAll(description.getAttributes())).toList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue