mirror of
https://github.com/schmaeddes/untitledTextAdventure.git
synced 2024-11-23 23:50:19 +01:00
GameLogic and GameState
* add Entity system * add Action system * implement first draft of TakeFrom, Open, and GoDirection actions
This commit is contained in:
parent
29e3bbe9bd
commit
b971d5a1ab
19 changed files with 643 additions and 41 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
target
|
target
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
4
pom.xml
4
pom.xml
|
@ -3,6 +3,10 @@
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
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>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<groupId>schmaeddes</groupId>
|
<groupId>schmaeddes</groupId>
|
||||||
<artifactId>untitledTextAdventure</artifactId>
|
<artifactId>untitledTextAdventure</artifactId>
|
||||||
|
|
|
@ -1,30 +1,24 @@
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import game.logic.GameLogic;
|
||||||
|
import game.logic.Parser;
|
||||||
|
import game.state.CircularLocationException;
|
||||||
import startup.Environment;
|
import startup.Environment;
|
||||||
import startup.LoadStuff;
|
import startup.LoadStuff;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
public static Environment environment = new Environment();
|
|
||||||
public static Parser parser = new Parser();
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
LoadStuff loadStuff = new LoadStuff();
|
LoadStuff loadStuff = new LoadStuff();
|
||||||
loadStuff.load(environment);
|
loadStuff.load(Environment.instance);
|
||||||
|
|
||||||
String greenPrompt = TextColors.BLUE.colorize(">");
|
|
||||||
Scanner scanner = new Scanner(System.in);
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
System.out.printf("%s ", greenPrompt);
|
|
||||||
List<String> input = Arrays.stream(scanner.nextLine().split(" ")).map(o -> o.toLowerCase(Locale.ROOT)).toList();
|
|
||||||
|
|
||||||
parser.parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Parser parser = new Parser();
|
||||||
|
try (GameLogic logic = new GameLogic(parser)) {
|
||||||
|
logic.loadGameState("games/damnCoolTextAdventureFTW.json");
|
||||||
|
logic.mainLoop();
|
||||||
|
} catch (CircularLocationException ex) {
|
||||||
|
System.err.println("You messed up you game state: " + ex.getMessage());
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Parser {
|
|
||||||
|
|
||||||
public void parse(List<String> parameter) {
|
|
||||||
String command = parameter.get(0);
|
|
||||||
|
|
||||||
switch (command) {
|
|
||||||
case "go" -> Commands.go(Main.environment.getAreaByString(parameter.get(1)));
|
|
||||||
case "info" -> Commands.info();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
27
src/main/java/game/logic/EntityDescription.java
Normal file
27
src/main/java/game/logic/EntityDescription.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package game.logic;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EntityDescription {
|
||||||
|
private final List<String> words;
|
||||||
|
|
||||||
|
public EntityDescription(List<String> words) {
|
||||||
|
this.words = new ArrayList<>(words);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMainWord() {
|
||||||
|
// TODO enities made up from multiple words. E.g., flat earth conspiracy
|
||||||
|
return this.words.get(this.words.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAttributes() {
|
||||||
|
return Collections.unmodifiableList(this.words.subList(0, this.words.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.join(" ", this.words);
|
||||||
|
}
|
||||||
|
}
|
169
src/main/java/game/logic/GameLogic.java
Normal file
169
src/main/java/game/logic/GameLogic.java
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package game.logic;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import game.logic.actionsystem.Action;
|
||||||
|
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.Entity;
|
||||||
|
import game.state.EntitySet;
|
||||||
|
import game.state.GameState;
|
||||||
|
|
||||||
|
public class GameLogic implements Closeable {
|
||||||
|
private final Parser parser;
|
||||||
|
private GameState state;
|
||||||
|
private Entity player;
|
||||||
|
private boolean discontinue = false;
|
||||||
|
|
||||||
|
public GameLogic(Parser parser) {
|
||||||
|
this.parser = parser;
|
||||||
|
this.state = new GameState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadGameState(String stateDescJsonFilePath) throws CircularLocationException {
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO setup code, load from json or so
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// final String NORTH = "north";
|
||||||
|
// final String SOUTH = "south";
|
||||||
|
final String EAST = "east";
|
||||||
|
final String WEST = "west";
|
||||||
|
final String INSIDE = "inside";
|
||||||
|
final String OUTSIDE = "outside";
|
||||||
|
|
||||||
|
Entity forestPath01 = this.state.createEntity("forest_path_01");
|
||||||
|
Entity clearing = this.state.createEntity("clearing");
|
||||||
|
Entity houseOutside = this.state.createEntity("house_outside");
|
||||||
|
Entity houseInside = this.state.createEntity("house_inside");
|
||||||
|
Entity houseMainDoor = this.state.createEntity("house_main_door");
|
||||||
|
houseMainDoor.setClosed(true);
|
||||||
|
|
||||||
|
forestPath01.connectBidirectional(EAST, null, WEST, clearing);
|
||||||
|
forestPath01.connectBidirectional(WEST, null, EAST, houseOutside);
|
||||||
|
houseOutside.connectBidirectional(INSIDE, houseMainDoor, OUTSIDE, houseInside);
|
||||||
|
|
||||||
|
this.player = this.state.createEntity("player");
|
||||||
|
Entity apple01 = this.state.createEntity("apple_01", "green");
|
||||||
|
Entity apple02 = this.state.createEntity("apple_02", "red");
|
||||||
|
|
||||||
|
this.player.setLocation(clearing);
|
||||||
|
apple01.setLocation(forestPath01);
|
||||||
|
apple02.setLocation(forestPath01);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO game specific action parsers
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
this.parser.pushActionParser(userInput -> userInput.get(0).equals("go")
|
||||||
|
? new GoDirection(userInput.size() < 2 ? null : userInput.get(1))
|
||||||
|
: null);
|
||||||
|
this.parser.pushActionParser(userInput -> {
|
||||||
|
if (userInput.get(0).equals("take")) {
|
||||||
|
int fromIdx = userInput.indexOf("from");
|
||||||
|
List<EntityDescription> whatToTake = this
|
||||||
|
.parseDescriptionList(userInput.subList(1, fromIdx == -1 ? userInput.size() : fromIdx));
|
||||||
|
List<EntityDescription> whereToTakeFrom = fromIdx == -1 ? null
|
||||||
|
: this.parseDescriptionList(userInput.subList(fromIdx + 1, userInput.size()));
|
||||||
|
return new TakeFrom(whatToTake, whereToTakeFrom);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.parser.pushActionParser(userInput -> {
|
||||||
|
if (userInput.get(0).equals("open")) {
|
||||||
|
return new Open(this.parseDescriptionList(userInput.subList(1, userInput.size())));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getPlayer() {
|
||||||
|
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() {
|
||||||
|
while (!this.discontinue) {
|
||||||
|
Action action = this.parser.readAction();
|
||||||
|
if (action != null) {
|
||||||
|
action.execute(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
public void close() throws IOException {
|
||||||
|
this.parser.close();
|
||||||
|
}
|
||||||
|
}
|
51
src/main/java/game/logic/Parser.java
Normal file
51
src/main/java/game/logic/Parser.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package game.logic;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import game.logic.actionsystem.Action;
|
||||||
|
import game.logic.actionsystem.ActionParser;
|
||||||
|
import startup.Environment;
|
||||||
|
import util.Commands;
|
||||||
|
import util.TextColors;
|
||||||
|
|
||||||
|
public class Parser implements Closeable {
|
||||||
|
private final Scanner scanner = new Scanner(System.in);
|
||||||
|
private final List<ActionParser> actionParsers = new LinkedList<>();
|
||||||
|
|
||||||
|
public Action readAction() {
|
||||||
|
String greenPrompt = TextColors.BLUE.colorize(">");
|
||||||
|
System.out.printf("%s ", greenPrompt);
|
||||||
|
List<String> input = Arrays.stream(scanner.nextLine().split("\\s+")).map(String::toLowerCase).toList();
|
||||||
|
|
||||||
|
for (ActionParser actionParser : this.actionParsers) {
|
||||||
|
Action action = actionParser.parseAction(input);
|
||||||
|
if (action != null) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(List<String> parameter) {
|
||||||
|
String command = parameter.get(0);
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case "go" -> Commands.go(Environment.instance.getAreaByString(parameter.get(1)));
|
||||||
|
case "info" -> Commands.info();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushActionParser(ActionParser actionParser) {
|
||||||
|
this.actionParsers.add(actionParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
this.scanner.close();
|
||||||
|
}
|
||||||
|
}
|
22
src/main/java/game/logic/actionsystem/Action.java
Normal file
22
src/main/java/game/logic/actionsystem/Action.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
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);
|
||||||
|
}
|
7
src/main/java/game/logic/actionsystem/ActionParser.java
Normal file
7
src/main/java/game/logic/actionsystem/ActionParser.java
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package game.logic.actionsystem;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ActionParser {
|
||||||
|
public Action parseAction(List<String> userInput);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/main/java/game/logic/actionsystem/actions/Open.java
Normal file
32
src/main/java/game/logic/actionsystem/actions/Open.java
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/main/java/game/logic/actionsystem/actions/TakeFrom.java
Normal file
50
src/main/java/game/logic/actionsystem/actions/TakeFrom.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/main/java/game/state/CircularLocationException.java
Normal file
7
src/main/java/game/state/CircularLocationException.java
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package game.state;
|
||||||
|
|
||||||
|
public class CircularLocationException extends Exception {
|
||||||
|
public CircularLocationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
125
src/main/java/game/state/Entity.java
Normal file
125
src/main/java/game/state/Entity.java
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
package game.state;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Entity {
|
||||||
|
public record EntityConnection(Entity to, Entity associatedEntity) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final Set<String> attributes = new HashSet<>();
|
||||||
|
private Entity location;
|
||||||
|
private boolean closed = false;
|
||||||
|
private final EntitySet contents = new EntitySet();
|
||||||
|
private final Map<String, EntityConnection> connections = new HashMap<>();
|
||||||
|
|
||||||
|
public Entity(String id, String... attributes) {
|
||||||
|
this.id = id;
|
||||||
|
this.attributes.addAll(Arrays.asList(attributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAttributes() {
|
||||||
|
return Collections.unmodifiableSet(this.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addAttribute(String attribute) {
|
||||||
|
return this.attributes.add(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAttribute(String attribute) {
|
||||||
|
return this.attributes.remove(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleAttribute(String attribute) {
|
||||||
|
if (!this.attributes.remove(attribute)) {
|
||||||
|
this.attributes.add(attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchAttribute(String attr1, String attr2) {
|
||||||
|
if (this.attributes.remove(attr1)) {
|
||||||
|
this.attributes.add(attr2);
|
||||||
|
} else if (this.attributes.remove(attr2)) {
|
||||||
|
this.attributes.add(attr1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocation(Entity location) throws CircularLocationException {
|
||||||
|
if (location == this) {
|
||||||
|
throw new CircularLocationException("setLocation.self");
|
||||||
|
} else if (this.contains(location, true)) {
|
||||||
|
throw new CircularLocationException("setLocation.circular");
|
||||||
|
} else if (this.location != location) {
|
||||||
|
if (this.location != null) {
|
||||||
|
this.location.contents.remove(this);
|
||||||
|
}
|
||||||
|
this.location = location;
|
||||||
|
if (this.location != null) {
|
||||||
|
this.location.contents.add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return this.closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosed(boolean closed) {
|
||||||
|
this.closed = closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Entity other, boolean recursive) {
|
||||||
|
return this.contents.getAll().stream().anyMatch(e -> e == other || (recursive && e.contains(other, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getLocation() {
|
||||||
|
return this.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Entity> getContents() {
|
||||||
|
return this.contents.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connectUnidirectional(String directionId, Entity to, Entity associatedEntity) {
|
||||||
|
this.connections.put(directionId, new EntityConnection(to, associatedEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connectBidirectional(String dirIdFromThisToOther, Entity associatedEntity, String dirIdFromOtherToThis, Entity to) {
|
||||||
|
this.connections.put(dirIdFromThisToOther, new EntityConnection(to, associatedEntity));
|
||||||
|
to.connections.put(dirIdFromOtherToThis, new EntityConnection(this, associatedEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSingleConnection(String directionId) {
|
||||||
|
this.connections.remove(directionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBidirectionalConnection(String dirIdFromThisToOther, String dirIdFromOtherToThis) {
|
||||||
|
EntityConnection c = this.connections.remove(dirIdFromThisToOther);
|
||||||
|
if (c != null) {
|
||||||
|
c.to.connections.remove(dirIdFromOtherToThis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityConnection getConnection(String directionId) {
|
||||||
|
return this.connections.get(directionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getConnections() {
|
||||||
|
return Collections.unmodifiableSet(this.connections.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
55
src/main/java/game/state/EntitySet.java
Normal file
55
src/main/java/game/state/EntitySet.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package game.state;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import game.logic.GameLogic;
|
||||||
|
|
||||||
|
public class EntitySet {
|
||||||
|
private final Set<Entity> entities;
|
||||||
|
|
||||||
|
public EntitySet(Entity... entities) {
|
||||||
|
this(Arrays.asList(entities));
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySet(Collection<Entity> entities) {
|
||||||
|
this.entities = new HashSet<>(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.entities.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Entity> getAll() {
|
||||||
|
return Collections.unmodifiableSet(this.entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean add(Entity entity) {
|
||||||
|
return this.entities.add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Entity entity) {
|
||||||
|
return this.entities.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Entity entity) {
|
||||||
|
return this.entities.contains(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity collapse(GameLogic logic) {
|
||||||
|
if(this.entities.size() <= 1) {
|
||||||
|
return this.entities.stream().findAny().orElse(null);
|
||||||
|
} else {
|
||||||
|
// TODO if more than 1 candidate, ask user to specify
|
||||||
|
return this.entities.stream().findAny().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySet getFiltered(Predicate<Entity> acceptFunction) {
|
||||||
|
return new EntitySet(this.entities.stream().filter(acceptFunction).toList());
|
||||||
|
}
|
||||||
|
}
|
25
src/main/java/game/state/GameState.java
Normal file
25
src/main/java/game/state/GameState.java
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package game.state;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import game.logic.EntityDescription;
|
||||||
|
|
||||||
|
public class GameState {
|
||||||
|
private final List<Entity> entities = new LinkedList<>();
|
||||||
|
|
||||||
|
public GameState() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity createEntity(String id, String... attributes) {
|
||||||
|
Entity e = new Entity(id, attributes);
|
||||||
|
this.entities.add(e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntitySet searchForEntity(EntityDescription description) {
|
||||||
|
return new EntitySet(this.entities.stream().filter(e -> e.getId().equals(description.getMainWord())
|
||||||
|
&& e.getAttributes().containsAll(description.getAttributes())).toList());
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
package startup;
|
package startup;
|
||||||
|
|
||||||
import areas.Area;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import areas.Area;
|
||||||
|
|
||||||
public class Environment {
|
public class Environment {
|
||||||
|
public static final Environment instance = new Environment();
|
||||||
|
|
||||||
private Area currentArea;
|
private Area currentArea;
|
||||||
private List<Area> gameAreas;
|
private List<Area> gameAreas;
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
|
package util;
|
||||||
|
|
||||||
import areas.Area;
|
import areas.Area;
|
||||||
|
import startup.Environment;
|
||||||
|
|
||||||
public class Commands {
|
public class Commands {
|
||||||
|
|
||||||
public static void go(Area area) {
|
public static void go(Area area) {
|
||||||
Main.environment.setArea(area);
|
Environment.instance.setArea(area);
|
||||||
area.enterArea();
|
area.enterArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void info() {
|
public static void info() {
|
||||||
String infoText = TextColors.PURPLE.colorize("Du bist hier: " +
|
String infoText = TextColors.PURPLE.colorize("Du bist hier: " +
|
||||||
Main.environment.getCurrentArea().getNameWithArticle()) + "\n" +
|
Environment.instance.getCurrentArea().getNameWithArticle()) + "\n" +
|
||||||
"Du kannst folgende Bereiche von hier erreichen: " +
|
"Du kannst folgende Bereiche von hier erreichen: " +
|
||||||
String.join(", ", Main.environment.getCurrentArea().getReachableAreas());
|
String.join(", ", Environment.instance.getCurrentArea().getReachableAreas());
|
||||||
|
|
||||||
System.out.println(infoText);
|
System.out.println(infoText);
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import org.w3c.dom.Text;
|
package util;
|
||||||
|
|
||||||
public enum TextColors {
|
public enum TextColors {
|
||||||
|
|
||||||
RESET("\u001B[0m"),
|
RESET("\u001B[0m"),
|
Loading…
Reference in a new issue