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:
Kristian Knarvik 2018-03-20 01:10:30 +01:00
parent 5ac42d8fcb
commit 30ba612e56
18 changed files with 231 additions and 90 deletions

View File

@ -25,7 +25,7 @@ Dette prosjektet inneholder [Semesteroppgave 1](SEM-1.md). Du kan også [lese op
# Fyll inn egne svar/beskrivelse/kommentarer til prosjektet under
* Levert av: *Kristian Knarvik* (*kkn015*)
* Del A: [x] helt ferdig, [ ] delvis ferdig
* Del B: [ ] helt ferdig, [x] delvis ferdig
* Del B: [x] helt ferdig, [x] delvis ferdig
* Del C: [ ] helt ferdig, [X] delvis ferdig
* [ ] hele semesteroppgaven er ferdig og klar til retting!
@ -69,15 +69,17 @@ Fordelene er at det er lett å få tak i all nødvendig informasjon, uten å må
Ulempene er at alle IActors og IItem blir veldig sterkt knyttet til IGame. De kan ikke brukes uten å spesifikt opprette et objekt som implementerer IGame først, som forhinder dem i å bli brukt i hvilket som helst annet prosjekt.
c)
* Game.addItem burde strengt tatt sjekket at en IActor ikke blir plassert på en IActor.
* Game.addItem burde strengt tatt sjekket at en IActor ikke blir plassert på en IActor. Det er ganske mye som ikke blir sjekket direkte, fordi en går ut ifra at brukeren vet hva den driver med. Dessverre er det ganske vanskelig å oppdage det sånn i forbifarten, med mindre det skaper en feilmelding.
d)
* Strukturen er definitivt mye mer kaotisk enn jeg først hadde trodd. Det er egentlig ganske vanskelig å finne det en er ute etter. I tillegg har jeg endret en del, som igjen endrer oppførselen til noen av de gamle klassene.
* Strukturen er definitivt mye mer kaotisk enn jeg først hadde trodd. Det er egentlig ganske vanskelig å finne det en er ute etter. I tillegg har jeg endret en del, som igjen endrer oppførselen til noen av de gamle klassene. Jeg har definitivt opplevd nye måter å gjøre ting på.
# Del C
## Oversikt over designvalg og hva du har gjort
* Oversikt over taster: WASD og piltaster kan brukes til bevegelse. Q for å legge ned ting. E for å plukke opp ting. 1-5 for å velge hvilke ting du ønsker å legge ned eller plukke opp.
* Oversikt over taster blir gitt til høyre for spillkartet.
* Main og Game har blitt endret til å tillate spilleren å selv velge når den har utført en tur. Dette har blitt gjort for å kunne ignorere uønskede tastetrykk, og for å tilby spilleren valg.
* Mye har blitt endret siden A og B, så noen ting kan ha blitt fjernet (automatiske gulrøtter har blitt kommentert ut).
* NPC inneholder hjelpemetoder som er felles for alle NPC, men oppfører seg svært ulikt basert på input.
### Tredjepartsfiler
* Bow Fire Arrow Sound (http://soundbible.com, Stephan Schutze, Noncommercial 3.0)
@ -85,4 +87,5 @@ d)
* Large Fireball Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Realistic Punch Sound (http://soundbible.com, Mark DiAngelo, Attribution 3.0)
* Snow Ball Throw And Splat Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)
* Dying Sound (http://soundbible.com, Mike Koenig, Attribution 3.0)

Binary file not shown.

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() {