Minor changes

Adds new method for containers for finding first item of a class
Adds health potions
Allows the user to use potions
This commit is contained in:
2018-03-20 01:10:30 +01:00
parent 5ac42d8fcb
commit 30ba612e56
18 changed files with 231 additions and 90 deletions

View File

@ -11,7 +11,7 @@ public class AppInfo {
/**
* Your name.
*/
public static final String APP_DEVELOPER = "INF101-student";
public static final String APP_DEVELOPER = "kkn015";
/**
* A short description.
*/
@ -19,8 +19,8 @@ public class AppInfo {
/**
* List of extra credits (e.g. for media sources)
*/
public static final List<String> APP_EXTRA_CREDITS = Arrays.asList(//
/* "Graphics by Foo Bar" */
public static final List<String> APP_EXTRA_CREDITS = Arrays.asList(
"Sounds by Mike Koenig, Stephan Schutze and Mark DiAngelo"
);
/**
* Help text. Could be used for an in-game help page, perhaps.

View File

@ -10,7 +10,7 @@ import inf101.v18.rogue101.shared.NPC;
public class Boss implements INonPlayer {
private int hp = getMaxHealth();
private Backpack backpack = new Backpack();
private final Backpack backpack = new Backpack();
private ILocation loc;
public Boss() {

View File

@ -26,10 +26,10 @@ public class Girl implements INonPlayer {
private int attack;
private int defence;
private int visibility;
private Backpack backpack = new Backpack();
private final Backpack backpack = new Backpack();
private static final Random random = new Random();
private List<Class<?>> validItems;
private static final String[] namelist = {"Milk", "Salad"};
private static final String[] namelist = {"Milk", "Salad", "Mikan", "Rima", "Suki", "Miki", "Hinata", "Haruna", "Asuna"};
public Girl() {
setStats();
@ -100,6 +100,11 @@ public class Girl implements INonPlayer {
validItems.add(IBuffItem.class);
}
/**
* Picks a "random" name
*
* @return A random name
*/
private String randomName() {
return namelist[random.nextInt(namelist.length)] + "-chan";
}

View File

@ -1,4 +1,4 @@
package inf101.v18.rogue101.items;
package inf101.v18.rogue101.examples;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
@ -6,22 +6,6 @@ import inf101.v18.rogue101.objects.IItem;
public class Manga implements IItem {
private int hp = getMaxHealth();
/*@Override
public boolean draw(ITurtle painter, double w, double h) {
painter.save();
painter.jump(-10);
painter.setInk(Color.BLACK);
painter.draw(getSize());
painter.turn(90);
painter.draw(getSize()/2);
painter.turn(90);
painter.draw(getSize());
painter.turn(90);
painter.draw(getSize()/2);
painter.restore();
return true;
}*/
@Override
public int getCurrentHealth() {
return hp;

View File

@ -14,7 +14,7 @@ import inf101.v18.rogue101.shared.NPC;
public class Rabbit implements INonPlayer {
private int food = 0;
private int hp = getMaxHealth();
private static List<Class<?>> validItems = new ArrayList<>();
private static final List<Class<?>> validItems = new ArrayList<>();
static {
validItems.add(Carrot.class);
}

View File

@ -20,6 +20,7 @@ import inf101.v18.rogue101.Main;
import inf101.v18.rogue101.enemies.Boss;
import inf101.v18.rogue101.enemies.Girl;
import inf101.v18.rogue101.examples.Carrot;
import inf101.v18.rogue101.examples.Manga;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.examples.Rabbit;
import inf101.v18.rogue101.map.GameMap;
@ -91,9 +92,9 @@ public class Game implements IGame {
}
// Prints some helpful information.
String[] info = {"Controls:", "WASD or arrow keys for movement", "E to pick up an item", "Q to drop an item", "1-0 to choose an item (10=0)", "N to change name", "ENTER to confirm", "R to use a ranged attack", "F to use a magic attack"};
String[] info = {"Controls:", "WASD or arrow keys for movement", "E to pick up an item", "Q to drop an item", "1-0 to choose an item (10=0)", "N to change name", "ENTER to confirm", "R to use a ranged attack", "F to use a magic attack", "C to use a potion"};
for (int i = 0; i < info.length; i++) {
this.printer.printAt(map.getWidth() + 2, 1 + i, info[i]);
this.printer.printAt(Main.COLUMN_MAP_END + 2, 1 + i, info[i]);
}
}
@ -132,10 +133,6 @@ public class Game implements IGame {
*/
private int getAttack() {
int damage = currentActor.getAttack() + random.nextInt(20) + 1;
IWeapon weapon = (IWeapon)currentActor.getItem(IWeapon.class);
if (weapon != null) {
damage += weapon.getWeaponDamage();
}
IBuffItem buff = (IBuffItem)currentActor.getItem(IBuffItem.class);
if (buff != null) {
damage += buff.getBuffDamage();
@ -167,6 +164,10 @@ public class Game implements IGame {
*/
private int getDamage(IItem target) {
int damage = currentActor.getDamage();
IWeapon weapon = (IWeapon)currentActor.getItem(IWeapon.class);
if (weapon != null) {
damage += weapon.getWeaponDamage();
}
IActor actor = (IActor) target;
IBuffItem item = (IBuffItem) actor.getItem(IBuffItem.class);
if (item != null) {
@ -181,7 +182,7 @@ public class Game implements IGame {
if (!map.has(loc, target)) {
throw new IllegalMoveException("Target isn't there!");
}
//TODO: Detect the weapon used
//TODO: Detect the weapon used, and use its sound
IWeapon weapon = (IWeapon) currentActor.getItem(IWeapon.class);
if (weapon != null) {
NPC.playSound(weapon.getSound());
@ -279,14 +280,7 @@ public class Game implements IGame {
map.clean(currentLocation);
return false;
} else if (currentActor instanceof IPlayer) {
if (currentActor.isDestroyed()) {
// a dead human player gets removed from the game
//This never actually triggers, because of map.clean();
/*displayMessage("YOU DIE!!!");
map.remove(currentLocation, currentActor);
currentActor = null;
currentLocation = null;*/
} else {
if (!currentActor.isDestroyed()) {
// For the human player, we need to wait for input, so we just return.
// Further keypresses will cause keyPressed() to be called, and once the human
// makes a move, it'll lose its movement point and doTurn() will be called again
@ -315,6 +309,7 @@ public class Game implements IGame {
currentLocation = null;
actors = new ArrayList<>();
loadMap("gameover.txt");
NPC.playSound("audio/Dying-SoundBible.com-1255481835.wav");
}
/**
@ -396,6 +391,7 @@ public class Game implements IGame {
itemFactories.put("B", Boss::new);
itemFactories.put("s", Staff::new);
itemFactories.put("b", Bow::new);
itemFactories.put("H", HealthPotion::new);
}
@Override

View File

@ -11,7 +11,7 @@ public class Backpack implements IContainer {
/**
* A list containing everything in the backpack.
*/
private List<IItem> content = new ArrayList<>();
private final List<IItem> content = new ArrayList<>();
/**
* The maximum amount of items allowed in a single backpack.
*/
@ -85,15 +85,20 @@ public class Backpack implements IContainer {
}
}
/**
* Removes an element at index i
*
* @param i The index of an element
*/
@Override
public void remove(int i) {
content.remove(i);
}
/**
* Removes item from the backpack.
*
* @param item The item to remove
*/
public void remove(IItem item) {
content.remove(item);
}
/**
* Gets a T at index i,
*
@ -127,4 +132,14 @@ public class Backpack implements IContainer {
public boolean isFull() {
return size() >= MAX_SIZE;
}
@Override
public IItem getFirst(Class<?> clazz) {
for (IItem item : content) {
if (clazz.isInstance(item)) {
return item;
}
}
return null;
}
}

View File

@ -8,8 +8,8 @@ import java.util.Collections;
import java.util.List;
public class Chest implements IContainer, IStatic {
private List<IItem> container;
private int MAX_SIZE = 10;
private final List<IItem> container;
private final int MAX_SIZE = 10;
public Chest() {
this.container = new ArrayList<>();
@ -48,6 +48,16 @@ public class Chest implements IContainer, IStatic {
return container.size() >= MAX_SIZE;
}
@Override
public IItem getFirst(Class<?> clazz) {
for (IItem item : container) {
if (clazz.isInstance(item)) {
return item;
}
}
return null;
}
@Override
public int getCurrentHealth() {
return 0;
@ -91,4 +101,9 @@ public class Chest implements IContainer, IStatic {
return false;
}
}
@Override
public void remove(int i) {
container.remove(i);
}
}

View File

@ -0,0 +1,56 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
public class HealthPotion implements IConsumable {
@Override
public int hpIncrease() {
return 100;
}
@Override
public int attackIncrease() {
return 0;
}
@Override
public int defenceIncrease() {
return 0;
}
@Override
public int getCurrentHealth() {
return 0;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 0;
}
@Override
public String getName() {
return "Healing potion";
}
@Override
public int getSize() {
return 0;
}
@Override
public String getSymbol() {
return "H";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
return 0;
}
}

View File

@ -3,7 +3,7 @@ package inf101.v18.rogue101.items;
import inf101.v18.rogue101.objects.IItem;
public interface IConsumable extends IItem {
void hpIncrease();
void attackIncrease();
void defenceIncrease();
int hpIncrease();
int attackIncrease();
int defenceIncrease();
}

View File

@ -9,39 +9,54 @@ public interface IContainer extends IItem {
* Retrieves an item from a container in index i
*
* @param i The index of an element
* @return An IItem
* @throws IndexOutOfBoundsException If the index is out of range.
* @return An IItem
* @throws IndexOutOfBoundsException If the index is out of range.
*/
IItem get(int i);
/**
* Adds an item to a container.
*
* @praram item The item to add to the container
* @return True if the container was not full
* @praram item The item to add to the container
*/
boolean addItem(IItem item);
/**
* Gets a list with everything inside a container.
*
* @return A list of Objects extending IItem
* @return A list of Objects extending IItem
*/
List<IItem> getContent();
/**
* Checks if we can add anything at all to the container.
*
* @return True if it has no space left
* @return True if it has no space left
*/
boolean isFull();
/**
* Returns the message to show the user upon interacting with the container.
*
* @return A message
* @return A message
*/
default String getInteractMessage() {
return "Items in " + getName() + ": ";
}
}
/**
* Gets the first item from the container of a specified type.
*
* @param clazz The class type to accept
* @return An IItem or null
*/
IItem getFirst(Class<?> clazz);
/**
* Removes an element at index i from the container.
*
* @param i The index of an element
*/
void remove(int i);
}

View File

@ -11,9 +11,10 @@ import inf101.v18.grid.IMultiGrid;
import inf101.v18.grid.MultiGrid;
import inf101.v18.rogue101.Main;
import inf101.v18.rogue101.examples.Carrot;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.game.IllegalMoveException;
import inf101.v18.rogue101.items.Manga;
import inf101.v18.rogue101.items.Chest;
import inf101.v18.rogue101.examples.Manga;
import inf101.v18.rogue101.items.Sword;
import inf101.v18.rogue101.objects.IActor;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.IPlayer;
@ -73,6 +74,12 @@ public class GameMap implements IGameMap {
if (random.nextInt(100) < 1) {
this.add(loc, new Manga());
}
if (random.nextInt(100) < 5) {
this.add(loc, new Chest());
}
if (random.nextInt(100) < 5) {
this.add(loc, new Sword());
}
}
}

View File

@ -8,9 +8,9 @@
############### # #
# # #
# S s # #
# b @ ######## #########
# # #
# b H @ ######## #########
# # #
# H # #
# # #
# G #
# G #

View File

@ -2,7 +2,6 @@ package inf101.v18.rogue101.objects;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.Game;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.shared.NPC;
@ -12,6 +11,8 @@ import java.util.List;
public class Player implements IPlayer {
private int hp = getMaxHealth();
private int attack = 50;
private int defence = 30;
private final Backpack equipped = new Backpack();
// True if the player wants to pick up something using the digit keys
private boolean dropping = false;
@ -45,6 +46,8 @@ public class Player implements IPlayer {
turnConsumed = pickUpInit(game);
} else if (key == KeyCode.Q) {
turnConsumed = dropInit(game);
} else if (key == KeyCode.C) {
turnConsumed = useConsumable(game);
} else if (key.isDigitKey()) {
//Takes care of all digit keys, but we only use the first 5 for picking up and dropping.
int keyValue = Integer.parseInt(key.getName());
@ -102,10 +105,12 @@ public class Player implements IPlayer {
*/
private void write(IGame game, KeyCode key) {
if (key == KeyCode.BACK_SPACE) {
text = text.substring(0, text.length() - 1);
game.displayMessage("Please enter your name: " + text);
if (text.length() > 0) {
text = text.substring(0, text.length() - 1);
game.displayMessage("Please enter your name: " + text);
}
} else if (key != KeyCode.ENTER) {
text += key.toString();
text += key.impl_getChar(); //Deprecated, but kind of necessary
game.displayMessage("Please enter your name: " + text);
} else {
name = text.toLowerCase();
@ -133,11 +138,7 @@ public class Player implements IPlayer {
pickUp(game, 0);
return true;
} else {
StringBuilder msg = new StringBuilder("Items on this tile:");
for (int i = 0; i < Math.min(items.size(), 5); i++) {
msg.append(" [").append(i + 1).append("] ").append(firstCharToUpper(items.get(i).getName()));
}
game.displayMessage(msg.toString());
game.displayMessage("Items on this tile:" + niceList(items, 5));
picking = true;
}
}
@ -145,6 +146,36 @@ public class Player implements IPlayer {
return false;
}
/**
* Uses the first consumable from the player's inventory
*
* @param game An IGame object
* @return
*/
private boolean useConsumable(IGame game) {
IConsumable potion = (IConsumable) equipped.getFirst(IConsumable.class);
if (potion != null) {
hp += potion.hpIncrease();
if (hp > getMaxHealth()) {
hp = getMaxHealth();
}
attack += potion.attackIncrease();
defence += potion.defenceIncrease();
equipped.remove(potion);
game.displayMessage("Used " + potion.getName());
return true;
} else {
game.displayMessage("You don't have any potions.");
}
return false;
}
/**
* Lists the contents of a chest, an makes the player ready to loot.
*
* @param game An IGame object
* @param items A list of items on the current tile
*/
private void openChest(IGame game, List<IItem> items) {
IContainer container = (IContainer) items.get(0);
items = container.getContent();
@ -152,6 +183,13 @@ public class Player implements IPlayer {
exploringChest = true;
}
/**
* Prints a set number of item names from a list.
*
* @param list The list of items
* @param max The maximum number of items to print
* @return
*/
private String niceList(List<IItem> list, int max) {
StringBuilder msg = new StringBuilder();
for (int i = 0; i < Math.min(list.size(), max); i++) {
@ -173,11 +211,7 @@ public class Player implements IPlayer {
} else if (equipped.size() < 1) {
game.displayMessage("You have nothing to drop");
} else {
StringBuilder msg = new StringBuilder("Items to drop:");
for (int i = 0; i < equipped.getContent().size(); i++) {
msg.append(" [").append(i + 1).append("] ").append(firstCharToUpper(equipped.get(i).getName()));
}
game.displayMessage(msg.toString());
game.displayMessage("Items to drop:" + niceList(equipped.getContent(), equipped.getContent().size()));
dropping = true;
}
return false;
@ -217,16 +251,20 @@ public class Player implements IPlayer {
if (container != null && i < container.getContent().size()) {
IItem loot = container.getContent().get(i);
equipped.add(loot);
container.getContent().remove(i);
container.remove(i);
game.displayMessage("Looted " + loot.getName());
} else {
game.displayMessage("It seems you are trying to loot a chest, without a chest nearby.");
}
} else {
game.displayMessage("Your inventory is full.");
}
}
/**
* Gets any static containers on the current tile.
*
* @param game An IGame object
* @return A static container
*/
private IContainer getStaticContainer(IGame game) {
List<IItem> items = game.getLocalItems();
if (items.size() < 1) {
@ -251,8 +289,8 @@ public class Player implements IPlayer {
IContainer container = getStaticContainer(game);
if (container != null) {
if (container.addItem(equipped.get(i))) {
game.displayMessage(equipped.get(i).getName() + " stored in " + container.getName());
equipped.remove(i);
game.displayMessage("Item stored in container.");
return;
} else {
game.displayMessage(container.getName() + " is full.");
@ -301,6 +339,12 @@ public class Player implements IPlayer {
}
}
/**
* Lets the user move or attack if possible.
*
* @param game An IGame object
* @param dir The direction the player wants to go
*/
private void tryToMove(IGame game, GridDirection dir) {
ILocation loc = game.getLocation();
if (game.canGo(dir)) {
@ -315,7 +359,7 @@ public class Player implements IPlayer {
@Override
public int getAttack() {
return 50;
return attack;
}
@Override
@ -340,7 +384,7 @@ public class Player implements IPlayer {
@Override
public int getDefence() {
return 30;
return defence;
}
@Override
@ -392,10 +436,9 @@ public class Player implements IPlayer {
List<GridDirection> dirs = game.locationDirection(current, neighbour);
IActor actor = game.getMap().getActors(neighbour).get(0); //We assume there is only one actor.
if (actor instanceof INonPlayer && current.gridDistanceTo(neighbour) <= range) {
for (GridDirection dir : dirs) {
game.rangedAttack(dir, actor);
return true;
}
GridDirection dir = dirs.get(0); //Only ever has one item
game.rangedAttack(dir, actor);
return true;
}
}
}

View File

@ -51,6 +51,8 @@ public class GameMapTest {
@Test
public void testNeighboursOutOfBounds() {
//This is a bit overkill, but it's meant to distinguish between kind of working and always working.
GameMap gameMap = new GameMap(20, 20);
ILocation location = gameMap.getLocation(0, 0);

View File

@ -42,7 +42,7 @@ public class PlayerTest {
assertEquals(loc, game.getLocation());
player.keyPressed(game, KeyCode.DOWN);
assertEquals(loc, game.getLocation());
}*/ //TODO: Fix error when playing sound
}*/ //TODO: Fix error "Toolkit not initialized"
@Test
public void testItem() {