Kristian Knarvik 30ba612e56 Minor changes
Adds new method for containers for finding first item of a class
Adds health potions
Allows the user to use potions
2018-03-20 01:10:30 +01:00

448 lines
14 KiB
Java

package inf101.v18.rogue101.objects;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.shared.NPC;
import javafx.scene.input.KeyCode;
import java.util.ArrayList;
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;
// True if the player wants to drop something using the digit keys
private boolean picking = false;
private boolean writing = false;
private boolean exploringChest = false;
private String text = "";
private String name = "Player";
@Override
public boolean keyPressed(IGame game, KeyCode key) {
boolean turnConsumed = false;
if (writing) {
write(game, key);
return false;
}
if (key == KeyCode.LEFT || key == KeyCode.A) {
tryToMove(game, GridDirection.WEST);
turnConsumed = true;
} else if (key == KeyCode.RIGHT || key == KeyCode.D) {
tryToMove(game, GridDirection.EAST);
turnConsumed = true;
} else if (key == KeyCode.UP || key == KeyCode.W) {
tryToMove(game, GridDirection.NORTH);
turnConsumed = true;
} else if (key == KeyCode.DOWN || key == KeyCode.S) {
tryToMove(game, GridDirection.SOUTH);
turnConsumed = true;
} else if (key == KeyCode.E) {
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());
if (keyValue <= 9 && keyValue >= 0) {
if (keyValue == 0) {
keyValue = 10;
}
if (dropping) {
drop(game, keyValue - 1);
dropping = false;
turnConsumed = true;
} else if (picking) {
pickUp(game, keyValue - 1);
picking = false;
turnConsumed = true;
} else if (exploringChest) {
loot(game, keyValue - 1);
exploringChest = false;
turnConsumed = true;
}
}
} else if (key == KeyCode.N) {
game.displayMessage("Please enter your name: ");
writing = true;
} else if (key == KeyCode.R) {
IItem item = getItem(IRangedWeapon.class);
if (item == null) {
game.displayMessage("You do not have a ranged weapon.");
} else {
turnConsumed = rangedAttack(game, 3);
if (!turnConsumed) {
game.displayMessage("No enemies within range.");
}
}
} else if (key == KeyCode.F) {
IItem item = getItem(IMagicWeapon.class);
if (item == null) {
game.displayMessage("You do not have a magic weapon.");
} else {
turnConsumed = rangedAttack(game, 2);
if (!turnConsumed) {
game.displayMessage("No enemies within range.");
}
}
}
showStatus(game);
return turnConsumed;
}
/**
* Lets the user write his/her name.
*
* @param game An IGame object
* @param key The key pressed
*/
private void write(IGame game, KeyCode key) {
if (key == KeyCode.BACK_SPACE) {
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.impl_getChar(); //Deprecated, but kind of necessary
game.displayMessage("Please enter your name: " + text);
} else {
name = text.toLowerCase();
text = "";
game.displayMessage("Name set.");
writing = false;
}
}
/**
* Initializes the picking up of an item, and does what is needed.
*
* @param game An IGame object
* @return True if a turn was consumed. False otherwise
*/
private boolean pickUpInit(IGame game) {
List<IItem> items = game.getLocalItems();
if (items.size() < 1) {
game.displayMessage("There is nothing to pick up");
} else {
if (items.get(0) instanceof IStatic && items.get(0) instanceof IContainer) { //Static items are always the biggest (and hopefully the only) item on a tile.
openChest(game, items);
} else {
if (items.size() == 1) {
pickUp(game, 0);
return true;
} else {
game.displayMessage("Items on this tile:" + niceList(items, 5));
picking = true;
}
}
}
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();
game.displayMessage(container.getInteractMessage() + niceList(items, 10));
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++) {
msg.append(" [").append(i + 1).append("] ").append(firstCharToUpper(list.get(i).getName()));
}
return msg.toString();
}
/**
* Initialized the dropping of an item, and does what is needed.
*
* @param game An IGame object
* @return True if a turn was consumed. False otherwise
*/
private boolean dropInit(IGame game) {
if (equipped.size() == 1) {
drop(game, 0);
return true;
} else if (equipped.size() < 1) {
game.displayMessage("You have nothing to drop");
} else {
game.displayMessage("Items to drop:" + niceList(equipped.getContent(), equipped.getContent().size()));
dropping = true;
}
return false;
}
/**
* Picks up an item at index i.
*
* @param game An IGame object
* @param i The wanted index
*/
private void pickUp(IGame game, int i) {
if (!equipped.isFull()) {
List<IItem> items = game.getLocalItems();
if (items.size() >= i) {
IItem pickedUp = game.pickUp(items.get(i));
if (pickedUp != null) {
equipped.add(pickedUp);
game.displayMessage("Picked up " + pickedUp.getName());
} else {
game.displayMessage("The item could not be picked up.");
}
} else {
game.displayMessage("That item does not exist.");
}
} else {
game.displayMessage("Your inventory is full.");
}
}
/**
* Takes an item from a chest, and gives it to the player.
*/
private void loot(IGame game, int i) {
if (!equipped.isFull()) {
IContainer container = getStaticContainer(game);
if (container != null && i < container.getContent().size()) {
IItem loot = container.getContent().get(i);
equipped.add(loot);
container.remove(i);
game.displayMessage("Looted " + loot.getName());
}
} 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) {
return null;
}
IItem item = items.get(0);
if (item instanceof IStatic && item instanceof IContainer) {
return (IContainer) item;
} else {
return null;
}
}
/**
* Drops an item at index i.
*
* @param game An IGame object
* @param i The wanted index
*/
private void drop(IGame game, int i) {
if (!equipped.isEmpty() && equipped.size() > i) {
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);
return;
} else {
game.displayMessage(container.getName() + " is full.");
return;
}
}
if (game.drop(equipped.get(i))) {
equipped.remove(i);
game.displayMessage("Item dropped.");
} else {
game.displayMessage("The ground rejected the item.");
}
} else {
game.displayMessage("You have nothing to drop.");
}
}
/**
* Updates the status bar with the Player's current stats.
*
* @param game An IGame object
*/
private void showStatus(IGame game) {
List<String> items = new ArrayList<>();
for (IItem item : equipped.getContent()) {
String name = item.getName();
if (name != null) {
items.add(firstCharToUpper(name));
}
}
//TODO: Add item bonuses to visible stats.
game.formatStatus("HP: %d/%d ATK: %d DEF: %s DMG: %s INV: %s", hp, getMaxHealth(), getAttack(), getDefence(), getDamage(), String.join(",", items));
}
/**
* Changes the first character in a string to uppercase.
*
* @param input The input string
* @return The input string with the first character uppercased
*/
private String firstCharToUpper(String input) {
if (input.length() < 1) {
return input;
} else {
return Character.toUpperCase(input.charAt(0)) + input.substring(1);
}
}
/**
* 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)) {
game.move(dir);
} else if (loc.canGo(dir) && game.getMap().hasActors(loc.go(dir))) {
NPC.playSound("audio/Sword Swing-SoundBible.com-639083727.wav");
game.attack(dir, game.getMap().getActors(loc.go(dir)).get(0));
} else {
game.displayMessage("Umm, it is not possible to move in that direction.");
}
}
@Override
public int getAttack() {
return attack;
}
@Override
public int getDamage() {
return 20;
}
@Override
public IItem getItem(Class<?> type) {
for (IItem item : equipped.getContent()) {
if (type.isInstance(item)) {
return item;
}
}
return null;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return defence;
}
@Override
public int getMaxHealth() {
return 1000;
}
@Override
public String getName() {
return firstCharToUpper(name);
}
@Override
public int getSize() {
return 10;
}
@Override
public String getPrintSymbol() {
return "\u001b[96m" + "@" + "\u001b[0m";
}
@Override
public String getSymbol() {
return "@";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
showStatus(game);
if (hp < 1) {
game.displayStatus("Game Over");
}
return amount;
}
@Override
public int getVision() {
//TODO: Increase vision based on equipped items
return 3;
}
private boolean rangedAttack(IGame game, int range) {
List<ILocation> neighbours = game.getVisible();
for (ILocation neighbour : neighbours) {
if (game.getMap().hasActors(neighbour)) {
ILocation current = game.getLocation();
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) {
GridDirection dir = dirs.get(0); //Only ever has one item
game.rangedAttack(dir, actor);
return true;
}
}
}
return false;
}
}