switch from harcoded string to enum and remove complexity in some actions
This commit is contained in:
parent
52d98a4ece
commit
42732e0fdd
25 changed files with 190 additions and 139 deletions
6
pom.xml
6
pom.xml
|
@ -20,6 +20,12 @@
|
||||||
<version>1.2.1</version>
|
<version>1.2.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains</groupId>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<version>23.0.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -11,11 +11,14 @@ import com.github.dtschust.zork.repl.ActionDispatcher;
|
||||||
import com.github.dtschust.zork.types.ZorkContainer;
|
import com.github.dtschust.zork.types.ZorkContainer;
|
||||||
import com.github.dtschust.zork.types.ZorkObject;
|
import com.github.dtschust.zork.types.ZorkObject;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.*;
|
||||||
|
|
||||||
/* And away we go*/
|
/* And away we go*/
|
||||||
public class Zork {
|
public class Zork {
|
||||||
|
public enum Type {ROOM, ITEM, CONTAINER, CREATURE}
|
||||||
public String userInput;
|
public String userInput;
|
||||||
|
|
||||||
public ZorkGame game;
|
public ZorkGame game;
|
||||||
|
@ -85,7 +88,7 @@ public class Zork {
|
||||||
private boolean doTriggersContainersInRoom() {
|
private boolean doTriggersContainersInRoom() {
|
||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
for (String key : game.getCurrentRoom().container) {
|
for (String key : game.getCurrentRoom().container) {
|
||||||
skip = skip || doZorkTriggers(game.get("container", key));
|
skip = skip || doZorkTriggers(game.get(CONTAINER, key));
|
||||||
}
|
}
|
||||||
return skip;
|
return skip;
|
||||||
}
|
}
|
||||||
|
@ -93,9 +96,9 @@ public class Zork {
|
||||||
private boolean doTriggersItemsInContainersInRoom() {
|
private boolean doTriggersItemsInContainersInRoom() {
|
||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
for (String key : game.getCurrentRoom().container) {
|
for (String key : game.getCurrentRoom().container) {
|
||||||
ZorkContainer tempContainer = (ZorkContainer) game.get("container", key);
|
ZorkContainer tempContainer = (ZorkContainer) game.get(CONTAINER, key);
|
||||||
for (String key2 : tempContainer.item) {
|
for (String key2 : tempContainer.item) {
|
||||||
skip = skip || doZorkTriggers(game.get("item", key2));
|
skip = skip || doZorkTriggers(game.get(ITEM, key2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return skip;
|
return skip;
|
||||||
|
@ -104,7 +107,7 @@ public class Zork {
|
||||||
private boolean doTriggersItemsInRoom() {
|
private boolean doTriggersItemsInRoom() {
|
||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
for (String key : game.getCurrentRoom().item) {
|
for (String key : game.getCurrentRoom().item) {
|
||||||
skip = skip || doZorkTriggers(game.get("item", key));
|
skip = skip || doZorkTriggers(game.get(ITEM, key));
|
||||||
}
|
}
|
||||||
return skip;
|
return skip;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +115,7 @@ public class Zork {
|
||||||
private boolean doTriggersItemsInInventory() {
|
private boolean doTriggersItemsInInventory() {
|
||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
for (String key : game.inventory) {
|
for (String key : game.inventory) {
|
||||||
skip = skip || doZorkTriggers(game.get("item", key));
|
skip = skip || doZorkTriggers(game.get(ITEM, key));
|
||||||
}
|
}
|
||||||
return skip;
|
return skip;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +123,7 @@ public class Zork {
|
||||||
private boolean doTriggersCreaturesInRoom() {
|
private boolean doTriggersCreaturesInRoom() {
|
||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
for (String key : game.getCurrentRoom().creature) {
|
for (String key : game.getCurrentRoom().creature) {
|
||||||
skip = skip || doZorkTriggers(game.get("creature", key));
|
skip = skip || doZorkTriggers(game.get(CREATURE, key));
|
||||||
}
|
}
|
||||||
return skip;
|
return skip;
|
||||||
}
|
}
|
||||||
|
@ -132,8 +135,9 @@ public class Zork {
|
||||||
|
|
||||||
private boolean doZorkTriggers(ZorkObject zorkObject) {
|
private boolean doZorkTriggers(ZorkObject zorkObject) {
|
||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
for (int x = zorkObject.trigger.size() - 1; x >= 0; x--) {
|
Iterator<ZorkTrigger> iterator = zorkObject.trigger.iterator();
|
||||||
ZorkTrigger tempTrigger = zorkObject.trigger.get(x);
|
while (iterator.hasNext()) {
|
||||||
|
ZorkTrigger tempTrigger = iterator.next();
|
||||||
if (tempTrigger.evaluate(this)) {
|
if (tempTrigger.evaluate(this)) {
|
||||||
for (String print : tempTrigger.print) {
|
for (String print : tempTrigger.print) {
|
||||||
System.out.println(print);
|
System.out.println(print);
|
||||||
|
@ -144,7 +148,7 @@ public class Zork {
|
||||||
}
|
}
|
||||||
skip = skip || tempTrigger.hasCommand();
|
skip = skip || tempTrigger.hasCommand();
|
||||||
if (tempTrigger.type.equals("single")) {
|
if (tempTrigger.type.equals("single")) {
|
||||||
zorkObject.trigger.remove(x);
|
iterator.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import com.github.dtschust.zork.parser.ZorkGame;
|
||||||
import com.github.dtschust.zork.types.ZorkContainer;
|
import com.github.dtschust.zork.types.ZorkContainer;
|
||||||
import com.github.dtschust.zork.types.ZorkRoom;
|
import com.github.dtschust.zork.types.ZorkRoom;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.*;
|
||||||
|
|
||||||
|
|
||||||
/* Has conditions*/
|
/* Has conditions*/
|
||||||
public class ZorkConditionHas extends ZorkCondition {
|
public class ZorkConditionHas extends ZorkCondition {
|
||||||
|
@ -23,13 +25,13 @@ public class ZorkConditionHas extends ZorkCondition {
|
||||||
return evaluateCondition(game.inventory.contains(object));
|
return evaluateCondition(game.inventory.contains(object));
|
||||||
} else {
|
} else {
|
||||||
/* is it a room?*/
|
/* is it a room?*/
|
||||||
ZorkRoom roomObject = (ZorkRoom) game.get("room", owner);
|
ZorkRoom roomObject = (ZorkRoom) game.get(ROOM, owner);
|
||||||
if (roomObject != null) {
|
if (roomObject != null) {
|
||||||
return evaluateCondition(roomObject.item.contains(object));
|
return evaluateCondition(roomObject.item.contains(object));
|
||||||
}
|
}
|
||||||
/* is it a container?*/
|
/* is it a container?*/
|
||||||
else {
|
else {
|
||||||
ZorkContainer containerObject = (ZorkContainer) game.get("container", owner);
|
ZorkContainer containerObject = (ZorkContainer) game.get(CONTAINER, owner);
|
||||||
if (containerObject != null) {
|
if (containerObject != null) {
|
||||||
return evaluateCondition(containerObject.containsItem(object));
|
return evaluateCondition(containerObject.containsItem(object));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class ZorkConditionStatus extends ZorkCondition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean evaluate(ZorkGame game) {
|
public boolean evaluate(ZorkGame game) {
|
||||||
ZorkObject tested = game.getListThroughLookup(object).get(object);
|
ZorkObject tested = game.getListThroughLookup(ZorkObject.class, object).get(object);
|
||||||
return tested != null && tested.isStatusEqualTo(status);
|
return tested != null && tested.isStatusEqualTo(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.github.dtschust.zork.parser;
|
package com.github.dtschust.zork.parser;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.Zork.Type;
|
||||||
import com.github.dtschust.zork.types.*;
|
import com.github.dtschust.zork.types.*;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -15,7 +16,7 @@ public class ZorkGame {
|
||||||
protected ZorkMap<ZorkItem> items = new ZorkMap<>();
|
protected ZorkMap<ZorkItem> items = new ZorkMap<>();
|
||||||
protected ZorkMap<ZorkContainer> containers = new ZorkMap<>();
|
protected ZorkMap<ZorkContainer> containers = new ZorkMap<>();
|
||||||
protected ZorkMap<ZorkCreature> creatures = new ZorkMap<>();
|
protected ZorkMap<ZorkCreature> creatures = new ZorkMap<>();
|
||||||
protected HashMap<String, String> objectLookup = new HashMap<>();
|
protected HashMap<String, Type> objectLookup = new HashMap<>();
|
||||||
|
|
||||||
public ZorkRoom getCurrentRoom() {
|
public ZorkRoom getCurrentRoom() {
|
||||||
return rooms.get(currentRoom);
|
return rooms.get(currentRoom);
|
||||||
|
@ -38,58 +39,59 @@ public class ZorkGame {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTypeFromLookup(String object) {
|
public Type getTypeFromLookup(String object) {
|
||||||
return objectLookup.get(object);
|
return objectLookup.get(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addObjectThroughLookup(String name, ZorkObject object) {
|
public void addObjectThroughLookup(Type type, ZorkObject object) {
|
||||||
putInMapGivenType(name, object);
|
putInMapGivenType(type, object);
|
||||||
objectLookup.put(object.name, name);
|
objectLookup.put(object.name, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZorkMap<? extends ZorkObject> getListThroughLookup(String name) {
|
public <T extends ZorkObject> ZorkMap<T> getListThroughLookup(Class<T> cast, String name) {
|
||||||
return getMapFromType(objectLookup.get(name));
|
return values(cast, objectLookup.get(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZorkObject get(String type, String key) {
|
public <T extends ZorkObject> ZorkMap<T> values(Class<T> cast, Type type) {
|
||||||
|
return (ZorkMap<T>) getMapFromType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZorkObject get(Type type, String key) {
|
||||||
return getMapFromType(type).get(key);
|
return getMapFromType(type).get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(String type, ZorkObject object) {
|
public void put(Type type, ZorkObject object) {
|
||||||
putInMapGivenType(type, object);
|
putInMapGivenType(type, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<? extends ZorkObject> values(String type) {
|
|
||||||
return getMapFromType(type).values();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ZorkMap<? extends ZorkObject> getMapFromType(String type) {
|
private ZorkMap<? extends ZorkObject> getMapFromType(Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "room":
|
case ROOM:
|
||||||
return rooms;
|
return rooms;
|
||||||
case "container":
|
case CONTAINER:
|
||||||
return containers;
|
return containers;
|
||||||
case "creature":
|
case CREATURE:
|
||||||
return creatures;
|
return creatures;
|
||||||
case "item":
|
case ITEM:
|
||||||
return items;
|
return items;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unexpected value: " + type);
|
throw new IllegalStateException("Unexpected value: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putInMapGivenType(String type, ZorkObject object) {
|
private void putInMapGivenType(Type type, ZorkObject object) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "room":
|
case ROOM:
|
||||||
rooms.put((ZorkRoom) object);
|
rooms.put((ZorkRoom) object);
|
||||||
break;
|
break;
|
||||||
case "container":
|
case CONTAINER:
|
||||||
containers.put((ZorkContainer) object);
|
containers.put((ZorkContainer) object);
|
||||||
break;
|
break;
|
||||||
case "creature":
|
case CREATURE:
|
||||||
creatures.put((ZorkCreature) object);
|
creatures.put((ZorkCreature) object);
|
||||||
break;
|
break;
|
||||||
case "item":
|
case ITEM:
|
||||||
items.put((ZorkItem) object);
|
items.put((ZorkItem) object);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -19,6 +19,9 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.nio.channels.NonReadableChannelException;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.*;
|
||||||
|
|
||||||
public class ZorkReader {
|
public class ZorkReader {
|
||||||
|
|
||||||
|
@ -72,14 +75,14 @@ public class ZorkReader {
|
||||||
tempCreature.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
|
tempCreature.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
|
||||||
|
|
||||||
/* Put each creature in the creatures hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
/* Put each creature in the creatures hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
||||||
data.addObjectThroughLookup("creature", tempCreature);
|
data.addObjectThroughLookup(CREATURE, tempCreature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addContainer(ZorkGame data, Element element) {
|
private static void addContainer(ZorkGame data, Element element) {
|
||||||
final ZorkContainer tempCont = Parsers.container.parse(element);
|
final ZorkContainer tempCont = Parsers.container.parse(element);
|
||||||
|
|
||||||
/* Put each container in the containers hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
/* Put each container in the containers hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
||||||
data.addObjectThroughLookup("container", tempCont);
|
data.addObjectThroughLookup(CONTAINER, tempCont);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addItem(ZorkGame data, Element element) {
|
private static void addItem(ZorkGame data, Element element) {
|
||||||
|
@ -111,7 +114,7 @@ public class ZorkReader {
|
||||||
tempItem.trigger.addAll(triggers);
|
tempItem.trigger.addAll(triggers);
|
||||||
|
|
||||||
/* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */
|
/* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */
|
||||||
data.addObjectThroughLookup("item", tempItem);
|
data.addObjectThroughLookup(ITEM, tempItem);
|
||||||
tempItem.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
|
tempItem.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +155,7 @@ public class ZorkReader {
|
||||||
tempRoom.border.put(borderDirection, borderName);
|
tempRoom.border.put(borderDirection, borderName);
|
||||||
}
|
}
|
||||||
/*Add this room to the rooms hashmap, put it in the generic objects hashmap, and store it's type in the objectlookup hashmap*/
|
/*Add this room to the rooms hashmap, put it in the generic objects hashmap, and store it's type in the objectlookup hashmap*/
|
||||||
data.addObjectThroughLookup("room", tempRoom);
|
data.addObjectThroughLookup(ROOM, tempRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZorkGame build() {
|
public ZorkGame build() {
|
||||||
|
@ -161,7 +164,7 @@ public class ZorkReader {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
if (!file.canRead()) {
|
if (!file.canRead()) {
|
||||||
System.out.println("Error opening file. Exiting...");
|
System.out.println("Error opening file. Exiting...");
|
||||||
throw new RuntimeException();
|
throw new NonReadableChannelException();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -198,6 +201,8 @@ public class ZorkReader {
|
||||||
case "creature":
|
case "creature":
|
||||||
addCreature(data, element);
|
addCreature(data, element);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected value: " + tagType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -206,7 +211,6 @@ public class ZorkReader {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.out.println("Invalid XML file, exiting");
|
System.out.println("Invalid XML file, exiting");
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
//e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
|
@ -4,16 +4,16 @@ import com.github.dtschust.zork.parser.ZorkGame;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class Action {
|
public interface Action {
|
||||||
public abstract boolean matchesInput(final List<String> arguments);
|
boolean matchesInput(final List<String> arguments);
|
||||||
|
|
||||||
public int getMinimumArgCount() {
|
default int getMinimumArgCount() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaximumArgCount() {
|
default int getMaximumArgCount() {
|
||||||
return Integer.MAX_VALUE;
|
return Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void run(final ZorkGame game, final List<String> arguments);
|
void run(final ZorkGame game, final List<String> arguments);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
package com.github.dtschust.zork.repl.actions;
|
package com.github.dtschust.zork.repl.actions;
|
||||||
|
|
||||||
import com.github.dtschust.zork.parser.ZorkGame;
|
import com.github.dtschust.zork.parser.ZorkGame;
|
||||||
|
import com.github.dtschust.zork.Zork.Type;
|
||||||
import com.github.dtschust.zork.repl.Action;
|
import com.github.dtschust.zork.repl.Action;
|
||||||
import com.github.dtschust.zork.types.ZorkContainer;
|
import com.github.dtschust.zork.types.HasSetOfCollectable;
|
||||||
import com.github.dtschust.zork.types.ZorkRoom;
|
import com.github.dtschust.zork.types.ZorkObject;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add: figure out what type the destination is, then what type the object is. Then add object to destination if it makes sense
|
* Add: figure out what type the destination is, then what type the object is. Then add object to destination if it makes sense
|
||||||
*/
|
*/
|
||||||
public class AddAction extends Action {
|
public class AddAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("Add");
|
return arguments.get(0).equals("Add");
|
||||||
|
@ -26,33 +28,13 @@ public class AddAction extends Action {
|
||||||
final String object = arguments.get(1);
|
final String object = arguments.get(1);
|
||||||
final String destination = arguments.get(3);
|
final String destination = arguments.get(3);
|
||||||
|
|
||||||
final String objectType = game.getTypeFromLookup(object);
|
try {
|
||||||
final String destinationType = game.getTypeFromLookup(destination);
|
Type destType = game.getTypeFromLookup(destination);
|
||||||
if (destinationType.equals("room")) {
|
Type objType = game.getTypeFromLookup(object);
|
||||||
ZorkRoom tempRoom = (ZorkRoom) game.get("room", destination);
|
ZorkObject tempObject = game.get(destType, destination);
|
||||||
switch (objectType) {
|
((HasSetOfCollectable) tempObject).getSetFromType(objType).add(object);
|
||||||
case "item":
|
game.put(destType, tempObject);
|
||||||
tempRoom.item.add(object);
|
} catch (Exception e) {
|
||||||
break;
|
|
||||||
case "creature":
|
|
||||||
tempRoom.creature.add(object);
|
|
||||||
break;
|
|
||||||
case "container":
|
|
||||||
tempRoom.container.add(object);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
System.out.println("Error");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
game.put("room", tempRoom);
|
|
||||||
} else if (destinationType.equals("container")) {
|
|
||||||
final ZorkContainer tempContainer = (ZorkContainer) game.get("container", destination);
|
|
||||||
if (objectType.equals("item"))
|
|
||||||
tempContainer.item.add(object);
|
|
||||||
else
|
|
||||||
System.out.println("Error");
|
|
||||||
game.put("container", tempContainer);
|
|
||||||
} else {
|
|
||||||
System.out.println("Error");
|
System.out.println("Error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ import com.github.dtschust.zork.types.ZorkCreature;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.CREATURE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt an attack, do you feel lucky?
|
* Attempt an attack, do you feel lucky?
|
||||||
*/
|
*/
|
||||||
public class AttackAction extends Action {
|
public class AttackAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("attack");
|
return arguments.get(0).equals("attack");
|
||||||
|
@ -26,15 +28,13 @@ public class AttackAction extends Action {
|
||||||
final String weapon = arguments.get(3);
|
final String weapon = arguments.get(3);
|
||||||
|
|
||||||
if (game.getCurrentRoom().creature.contains(tempString)) {
|
if (game.getCurrentRoom().creature.contains(tempString)) {
|
||||||
ZorkCreature tempCreature = (ZorkCreature) game.get("creature", tempString);
|
ZorkCreature tempCreature = (ZorkCreature) game.get(CREATURE, tempString);
|
||||||
if (tempCreature != null && game.inventory.contains(weapon)) {
|
if (tempCreature != null && game.inventory.contains(weapon) && tempCreature.isAttackSuccessful(game, weapon)) {
|
||||||
if (tempCreature.isAttackSuccessful(game, weapon)) {
|
|
||||||
System.out.println("You assault the " + tempString + " with the " + weapon + ".");
|
System.out.println("You assault the " + tempString + " with the " + weapon + ".");
|
||||||
tempCreature.printAndExecuteActions(game);
|
tempCreature.printAndExecuteActions(game);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Error");
|
System.out.println("Error");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package com.github.dtschust.zork.repl.actions;
|
package com.github.dtschust.zork.repl.actions;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.Zork.Type;
|
||||||
import com.github.dtschust.zork.parser.ZorkGame;
|
import com.github.dtschust.zork.parser.ZorkGame;
|
||||||
import com.github.dtschust.zork.repl.Action;
|
import com.github.dtschust.zork.repl.Action;
|
||||||
import com.github.dtschust.zork.types.ZorkContainer;
|
import com.github.dtschust.zork.types.HasSetOfCollectable;
|
||||||
|
import com.github.dtschust.zork.types.ZorkObject;
|
||||||
import com.github.dtschust.zork.types.ZorkRoom;
|
import com.github.dtschust.zork.types.ZorkRoom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete: figure out what object it is and delete it accordingly. Rooms are especially tricky
|
* Delete: figure out what object it is and delete it accordingly. Rooms are especially tricky
|
||||||
*/
|
*/
|
||||||
public class DeleteAction extends Action {
|
public class DeleteAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("Delete");
|
return arguments.get(0).equals("Delete");
|
||||||
|
@ -25,48 +30,37 @@ public class DeleteAction extends Action {
|
||||||
public void run(ZorkGame game, List<String> arguments) {
|
public void run(ZorkGame game, List<String> arguments) {
|
||||||
final String object = arguments.get(1);
|
final String object = arguments.get(1);
|
||||||
|
|
||||||
String objectType = game.getTypeFromLookup(object);
|
switch (game.getTypeFromLookup(object)) {
|
||||||
switch (objectType) {
|
case ROOM:
|
||||||
case "room":
|
for (ZorkRoom tempRoom :game.values(ZorkRoom.class, ROOM)) {
|
||||||
for (ZorkRoom tempRoom : (Iterable<ZorkRoom>) game.values("room")) {
|
|
||||||
for (String key : tempRoom.border.keySet()) {
|
for (String key : tempRoom.border.keySet()) {
|
||||||
if (tempRoom.border.get(key).equals(object)) {
|
if (tempRoom.border.get(key).equals(object)) {
|
||||||
tempRoom.border.remove(key);
|
tempRoom.border.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.put("room", tempRoom);
|
game.put(ROOM, tempRoom);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "item":
|
case ITEM:
|
||||||
for (ZorkRoom tempRoom : (Iterable<ZorkRoom>) game.values("room")) {
|
deleteElementFromSpace(game, ROOM, ITEM, object);
|
||||||
if (tempRoom.item.contains(object)) {
|
deleteElementFromSpace(game, CONTAINER, ITEM, object);
|
||||||
tempRoom.item.remove(object);
|
|
||||||
game.put("room", tempRoom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (ZorkContainer tempContainer : (Iterable<ZorkContainer>) game.values("container")) {
|
|
||||||
if (tempContainer.item.contains(object)) {
|
|
||||||
tempContainer.item.remove(object);
|
|
||||||
game.put("container", tempContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "container":
|
case CONTAINER:
|
||||||
for (ZorkRoom tempRoom : (Iterable<ZorkRoom>) game.values("room")) {
|
deleteElementFromSpace(game, ROOM, CONTAINER, object);
|
||||||
if (tempRoom.container.contains(object)) {
|
|
||||||
tempRoom.container.remove(object);
|
|
||||||
game.put("room", tempRoom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "creature":
|
case CREATURE:
|
||||||
for (ZorkRoom tempRoom : (Iterable<ZorkRoom>) game.values("room")) {
|
deleteElementFromSpace(game, ROOM, CREATURE, object);
|
||||||
if (tempRoom.creature.contains(object)) {
|
|
||||||
tempRoom.creature.remove(object);
|
|
||||||
game.put("room", tempRoom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void deleteElementFromSpace(ZorkGame game, Type space, Type element, String object) {
|
||||||
|
for (ZorkObject tempObject : game.values(ZorkObject.class, space)) {
|
||||||
|
Set<String> set = ((HasSetOfCollectable) tempObject).getSetFromType(element);
|
||||||
|
if (set.contains(object)) {
|
||||||
|
set.remove(object);
|
||||||
|
game.put(space, tempObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ import com.github.dtschust.zork.types.ZorkRoom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DropItemAction extends Action {
|
import static com.github.dtschust.zork.Zork.Type.ROOM;
|
||||||
|
|
||||||
|
public class DropItemAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("drop");
|
return arguments.get(0).equals("drop");
|
||||||
|
@ -24,7 +26,7 @@ public class DropItemAction extends Action {
|
||||||
if (game.inventory.contains(what)) {
|
if (game.inventory.contains(what)) {
|
||||||
ZorkRoom tempRoom = game.getCurrentRoom();
|
ZorkRoom tempRoom = game.getCurrentRoom();
|
||||||
tempRoom.item.add(what);
|
tempRoom.item.add(what);
|
||||||
game.put("room", tempRoom);
|
game.put(ROOM, tempRoom);
|
||||||
game.inventory.remove(what);
|
game.inventory.remove(what);
|
||||||
System.out.println(what + " dropped.");
|
System.out.println(what + " dropped.");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* The "Game Over" action marks the end of the game.
|
* The "Game Over" action marks the end of the game.
|
||||||
*/
|
*/
|
||||||
public class GameOverAction extends Action {
|
public class GameOverAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("Game") && arguments.get(1).equals("Over");
|
return arguments.get(0).equals("Game") && arguments.get(1).equals("Over");
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.github.dtschust.zork.repl.Action;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class InventoryAction extends Action {
|
public class InventoryAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("i");
|
return arguments.get(0).equals("i");
|
||||||
|
|
|
@ -13,7 +13,7 @@ import static java.util.Map.entry;
|
||||||
* Execute a user action or an action command from some <action> element that is not one of the "Special Commands"
|
* Execute a user action or an action command from some <action> element that is not one of the "Special Commands"
|
||||||
* Movement
|
* Movement
|
||||||
*/
|
*/
|
||||||
public class MoveAction extends Action {
|
public class MoveAction implements Action {
|
||||||
private static final Map<String, String> fullDirections = Map.ofEntries(
|
private static final Map<String, String> fullDirections = Map.ofEntries(
|
||||||
entry("n", "north"),
|
entry("n", "north"),
|
||||||
entry("s", "south"),
|
entry("s", "south"),
|
||||||
|
|
|
@ -6,7 +6,9 @@ import com.github.dtschust.zork.types.ZorkContainer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class OpenAction extends Action {
|
import static com.github.dtschust.zork.Zork.Type.CONTAINER;
|
||||||
|
|
||||||
|
public class OpenAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("open");
|
return arguments.get(0).equals("open");
|
||||||
|
@ -26,7 +28,7 @@ public class OpenAction extends Action {
|
||||||
} else {
|
} else {
|
||||||
ZorkContainer tempContainer;
|
ZorkContainer tempContainer;
|
||||||
if (game.getCurrentRoom().container.contains(what)) {
|
if (game.getCurrentRoom().container.contains(what)) {
|
||||||
tempContainer = (ZorkContainer) game.get("container", what);
|
tempContainer = (ZorkContainer) game.get(CONTAINER, what);
|
||||||
tempContainer.open();
|
tempContainer.open();
|
||||||
if (tempContainer.item.isEmpty()) {
|
if (tempContainer.item.isEmpty()) {
|
||||||
System.out.println(what + " is empty");
|
System.out.println(what + " is empty");
|
||||||
|
|
|
@ -6,7 +6,9 @@ import com.github.dtschust.zork.types.ZorkContainer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PutAction extends Action {
|
import static com.github.dtschust.zork.Zork.Type.CONTAINER;
|
||||||
|
|
||||||
|
public class PutAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("put");
|
return arguments.get(0).equals("put");
|
||||||
|
@ -23,7 +25,7 @@ public class PutAction extends Action {
|
||||||
final String destination = arguments.get(3);
|
final String destination = arguments.get(3);
|
||||||
|
|
||||||
if (game.getCurrentRoom().container.contains(destination)) {
|
if (game.getCurrentRoom().container.contains(destination)) {
|
||||||
ZorkContainer tempContainer = (ZorkContainer) game.get("container", destination);
|
ZorkContainer tempContainer = (ZorkContainer) game.get(CONTAINER, destination);
|
||||||
if (tempContainer.isOpen() && game.inventory.contains(what)) {
|
if (tempContainer.isOpen() && game.inventory.contains(what)) {
|
||||||
tempContainer.item.add(what);
|
tempContainer.item.add(what);
|
||||||
game.inventory.remove(what);
|
game.inventory.remove(what);
|
||||||
|
|
|
@ -6,7 +6,9 @@ import com.github.dtschust.zork.types.ZorkItem;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ReadAction extends Action {
|
import static com.github.dtschust.zork.Zork.Type.ITEM;
|
||||||
|
|
||||||
|
public class ReadAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("read");
|
return arguments.get(0).equals("read");
|
||||||
|
@ -22,7 +24,7 @@ public class ReadAction extends Action {
|
||||||
final String what = arguments.get(1);
|
final String what = arguments.get(1);
|
||||||
|
|
||||||
if (game.inventory.contains(what)) {
|
if (game.inventory.contains(what)) {
|
||||||
ZorkItem tempItem = (ZorkItem) game.get("item", what);
|
ZorkItem tempItem = (ZorkItem) game.get(ITEM, what);
|
||||||
if (tempItem.writing != null && !tempItem.writing.isEmpty()) {
|
if (tempItem.writing != null && !tempItem.writing.isEmpty()) {
|
||||||
System.out.println(tempItem.writing);
|
System.out.println(tempItem.writing);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,7 +7,9 @@ import com.github.dtschust.zork.types.ZorkRoom;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TakeAction extends Action {
|
import static com.github.dtschust.zork.Zork.Type.*;
|
||||||
|
|
||||||
|
public class TakeAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("take");
|
return arguments.get(0).equals("take");
|
||||||
|
@ -26,16 +28,16 @@ public class TakeAction extends Action {
|
||||||
game.inventory.add(tempString);
|
game.inventory.add(tempString);
|
||||||
ZorkRoom tempRoom = (game.getCurrentRoom());
|
ZorkRoom tempRoom = (game.getCurrentRoom());
|
||||||
tempRoom.item.remove(tempString);
|
tempRoom.item.remove(tempString);
|
||||||
game.put("room", tempRoom);
|
game.put(ROOM, tempRoom);
|
||||||
System.out.println("Item " + tempString + " added to inventory.");
|
System.out.println("Item " + tempString + " added to inventory.");
|
||||||
} else {
|
} else {
|
||||||
/* Search all containers in the current room for the item! */
|
/* Search all containers in the current room for the item! */
|
||||||
for (String key : game.getCurrentRoom().container) {
|
for (String key : game.getCurrentRoom().container) {
|
||||||
ZorkContainer tempContainer = (ZorkContainer) game.get("container", key);
|
ZorkContainer tempContainer = (ZorkContainer) game.get(CONTAINER, key);
|
||||||
if (tempContainer != null && tempContainer.isOpen() && tempContainer.item.contains(tempString)) {
|
if (tempContainer != null && tempContainer.isOpen() && tempContainer.item.contains(tempString)) {
|
||||||
game.inventory.add(tempString);
|
game.inventory.add(tempString);
|
||||||
tempContainer.item.remove(tempString);
|
tempContainer.item.remove(tempString);
|
||||||
game.put("container", tempContainer);
|
game.put(CONTAINER, tempContainer);
|
||||||
System.out.println("Item " + tempString + " added to inventory.");
|
System.out.println("Item " + tempString + " added to inventory.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ import com.github.dtschust.zork.types.ZorkItem;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.ITEM;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn on an item
|
* Turn on an item
|
||||||
*/
|
*/
|
||||||
public class TurnOnAction extends Action {
|
public class TurnOnAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("turn") && arguments.get(1).equals("on");
|
return arguments.get(0).equals("turn") && arguments.get(1).equals("on");
|
||||||
|
@ -25,7 +27,7 @@ public class TurnOnAction extends Action {
|
||||||
final String what = arguments.get(2);
|
final String what = arguments.get(2);
|
||||||
|
|
||||||
if (game.inventory.contains(what)) {
|
if (game.inventory.contains(what)) {
|
||||||
ZorkItem tempItem = (ZorkItem) game.get("item", what);
|
ZorkItem tempItem = (ZorkItem) game.get(ITEM, what);
|
||||||
System.out.println("You activate the " + what + ".");
|
System.out.println("You activate the " + what + ".");
|
||||||
if (tempItem != null) {
|
if (tempItem != null) {
|
||||||
tempItem.printAndExecuteActions(game);
|
tempItem.printAndExecuteActions(game);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* The "Update" command figures out what type of item it is, and then change its status
|
* The "Update" command figures out what type of item it is, and then change its status
|
||||||
*/
|
*/
|
||||||
public class UpdateAction extends Action {
|
public class UpdateAction implements Action {
|
||||||
@Override
|
@Override
|
||||||
public boolean matchesInput(List<String> arguments) {
|
public boolean matchesInput(List<String> arguments) {
|
||||||
return arguments.get(0).equals("Update");
|
return arguments.get(0).equals("Update");
|
||||||
|
@ -26,7 +26,7 @@ public class UpdateAction extends Action {
|
||||||
final String object = arguments.get(1);
|
final String object = arguments.get(1);
|
||||||
final String newStatus = arguments.get(3);
|
final String newStatus = arguments.get(3);
|
||||||
|
|
||||||
ZorkMap<ZorkObject> collection = (ZorkMap<ZorkObject>) game.getListThroughLookup(object);
|
ZorkMap<ZorkObject> collection = game.getListThroughLookup(ZorkObject.class, object);
|
||||||
ZorkObject tempObject = collection.get(object);
|
ZorkObject tempObject = collection.get(object);
|
||||||
tempObject.updateStatus(newStatus);
|
tempObject.updateStatus(newStatus);
|
||||||
collection.put(tempObject);
|
collection.put(tempObject);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.github.dtschust.zork.types;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.Zork;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface HasSetOfCollectable {
|
||||||
|
|
||||||
|
default Set<String> getSetFromType(Zork.Type type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,14 @@
|
||||||
package com.github.dtschust.zork.types;
|
package com.github.dtschust.zork.types;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.Zork;
|
||||||
import com.github.dtschust.zork.ZorkTrigger;
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static com.github.dtschust.zork.Zork.Type.ITEM;
|
||||||
|
|
||||||
/* Container*/
|
/* Container*/
|
||||||
public class ZorkContainer extends ZorkObject {
|
public class ZorkContainer extends ZorkObject implements HasSetOfCollectable {
|
||||||
public final Set<String> item;
|
public final Set<String> item;
|
||||||
private final List<String> accepts;
|
private final List<String> accepts;
|
||||||
private boolean open;
|
private boolean open;
|
||||||
|
@ -46,4 +49,11 @@ public class ZorkContainer extends ZorkObject {
|
||||||
public void open() {
|
public void open() {
|
||||||
open = true;
|
open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getSetFromType(Zork.Type type) {
|
||||||
|
if (type.equals(ITEM))
|
||||||
|
return item;
|
||||||
|
throw new IllegalStateException("Unexpected value: " + type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package com.github.dtschust.zork.types;
|
package com.github.dtschust.zork.types;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class ZorkMap<T extends ZorkObject> extends HashMap<String, T> {
|
public class ZorkMap<T extends ZorkObject> extends HashMap<String, T> implements Iterable<T> {
|
||||||
|
|
||||||
public T put(T object) {
|
public T put(T object) {
|
||||||
return put(object.name, object);
|
return put(object.name, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return this.values().iterator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,4 +36,5 @@ public abstract class ZorkObject {
|
||||||
public boolean isStatusEqualTo(String status) {
|
public boolean isStatusEqualTo(String status) {
|
||||||
return this.status.equals(status);
|
return this.status.equals(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package com.github.dtschust.zork.types;
|
package com.github.dtschust.zork.types;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.Zork;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/* Room*/
|
/* Room*/
|
||||||
public class ZorkRoom extends ZorkObject {
|
public class ZorkRoom extends ZorkObject implements HasSetOfCollectable {
|
||||||
public final String type;
|
public final String type;
|
||||||
public final Map<String, String> border = new HashMap<>();
|
public final Map<String, String> border = new HashMap<>();
|
||||||
public final Set<String> container = new HashSet<>();
|
public final Set<String> container = new HashSet<>();
|
||||||
|
@ -17,4 +19,18 @@ public class ZorkRoom extends ZorkObject {
|
||||||
super(name, description);
|
super(name, description);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getSetFromType(Zork.Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case CONTAINER:
|
||||||
|
return container;
|
||||||
|
case CREATURE:
|
||||||
|
return creature;
|
||||||
|
case ITEM:
|
||||||
|
return item;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected value: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue