Progress saving

This commit is contained in:
Kristian Knarvik 2018-03-08 23:38:59 +01:00
parent 2e50452a22
commit 0b143037de
10 changed files with 288 additions and 131 deletions

View File

@ -24,7 +24,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, [ ] delvis ferdig
* Del B: [ ] helt ferdig, [x] delvis ferdig
* Del C: [ ] helt ferdig, [ ] delvis ferdig
* [ ] hele semesteroppgaven er ferdig og klar til retting!
@ -58,7 +58,18 @@ g)
# Del B
## Svar på spørsmål
* ...
a)
* Nabo-celler blir behandlet en del annerledes, siden det skal være mulig å finne celler en viss avstand fra et punkt.
Jeg har ikke bestemt meg om hva som er mest praktisk, men denne seminaroppgaven blir fort ganske kaotisk når en skal lete informasjon om celler og naboer.
b)
* De fleste spill-trekkene går igjennom game for å enkelt kunne utføre turen til alle objekter.
Fordelene er at det er lett å få tak i all nødvendig informasjon, uten å måtte lage spesialiserte metoder, eller sende inn mange parametere.
Ulempene er at IItem objekter har mindre kontroll. For eksempel; spilleren mister sin tur etter en tast har blitt trykket, uansett om det var en gyldig knapp eller ikke.
Dette gjør det "umulig" å implementere menyer og brukervalg.
c)
* Game.addItem burde strengt tatt sjekket at en IActor ikke blir plassert på en IActor.
# Del C
## Oversikt over designvalg og hva du har gjort

View File

@ -7,8 +7,10 @@ import inf101.v18.gfx.gfxmode.ITurtle;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IActor;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.INonPlayer;
import inf101.v18.rogue101.shared.NPC;
public class Rabbit implements INonPlayer {
private int food = 0;
@ -25,6 +27,12 @@ public class Rabbit implements INonPlayer {
return;
}
//TODO: It seems the rabbit moves twice. We don't want that.
if (NPC.tryAttack(game)) {
return;
}
for (IItem item : game.getLocalItems()) {
if (item instanceof Carrot) {
System.out.println("found carrot!");
@ -32,8 +40,8 @@ public class Rabbit implements INonPlayer {
if (eaten > 0) {
System.out.println("ate carrot worth " + eaten + "!");
food += eaten;
game.displayMessage("You hear a faint crunching (" + getName() + " eats " + item.getArticle() + " "
+ item.getName() + ")");
//game.displayMessage("You hear a faint crunching (" + getName() + " eats " + item.getArticle() + " "
//+ item.getName() + ")");
return;
}
}
@ -78,7 +86,7 @@ public class Rabbit implements INonPlayer {
@Override
public int getDefence() {
return 1000;
return 10;
}
@Override
@ -88,7 +96,7 @@ public class Rabbit implements INonPlayer {
@Override
public String getName() {
return "rabbit";
return "Rabbit";
}
@Override
@ -96,6 +104,11 @@ public class Rabbit implements INonPlayer {
return 4;
}
@Override
public int getVision() {
return 5;
}
@Override
public String getSymbol() {
return hp > 0 ? "R" : "¤";

View File

@ -113,12 +113,17 @@ public class Game implements IGame {
@Override
public ILocation attack(GridDirection dir, IItem target) {
ILocation loc = map.go(currentLocation, dir);
if (map.has(loc, target))
ILocation loc = currentLocation.go(dir);
if (!map.has(loc, target)) {
throw new IllegalMoveException("Target isn't there!");
// TODO: implement attack
}
int damage = currentActor.getAttack() + random.nextInt(20) + 1;
if (damage >= target.getDefence() + 10) {
int actualDamage = target.handleDamage(this, target, damage);
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
} else {
displayMessage("The attack missed.");
}
map.clean(loc);
@ -371,9 +376,7 @@ public class Game implements IGame {
@Override
public List<ILocation> getVisible() {
// TODO: maybe replace 3 by some sort of visibility range obtained from
// currentActor?
return map.getNeighbourhood(currentLocation, 3);
return map.getNeighbourhood(currentLocation, currentActor.getVision());
}
@Override
@ -495,4 +498,45 @@ public class Game implements IGame {
public Random getRandom() {
return random;
}
@Override
public GridDirection locationDirection(ILocation start, ILocation target) {
int targetX = target.getX(), targetY = target.getY();
int startX = start.getX(), startY = start.getY();
GridDirection dir = GridDirection.CENTER;
if (targetX > startX && targetY > startY) {
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
dir = GridDirection.SOUTH;
} else {
dir = GridDirection.EAST;
}
} else if (targetX > startX && targetY < startY) {
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
dir = GridDirection.NORTH;
} else {
dir = GridDirection.EAST;
}
} else if (targetX < startX && targetY > startY) {
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
dir = GridDirection.SOUTH;
} else {
dir = GridDirection.WEST;
}
} else if (targetX < startX && targetY < startY) {
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
dir = GridDirection.NORTH;
} else {
dir = GridDirection.WEST;
}
} else if (targetX > startX) {
dir = GridDirection.EAST;
} else if (targetX < startX) {
dir = GridDirection.WEST;
} else if (targetY > startY) {
dir = GridDirection.SOUTH;
} else if (targetY < startY) {
dir = GridDirection.NORTH;
}
return dir;
}
}

View File

@ -317,4 +317,14 @@ public interface IGame {
* @return A random generator
*/
Random getRandom();
/**
* Gets the best direction to go from current to neighbour.
* If the target is positioned diagonally from the start, the direction requiring the most steps is chosen.
*
* @param current The location to go from
* @param neighbour The location to go to
* @return A direction
*/
GridDirection locationDirection(ILocation current, ILocation neighbour);
}

View File

@ -2,7 +2,6 @@ package inf101.v18.rogue101.map;
import java.util.*;
import com.sun.org.apache.bcel.internal.generic.ILOAD;
import inf101.v18.gfx.gfxmode.ITurtle;
import inf101.v18.gfx.textmode.Printer;
import inf101.v18.grid.GridDirection;
@ -257,100 +256,145 @@ public class GameMap implements IGameMap {
dirtyLocs.add(loc);
}
@Override
public List<ILocation> getNeighbourhood(ILocation loc, int dist) {
if (dist < 0 || loc == null) {
throw new IllegalArgumentException();
} else if (dist == 0){
return new ArrayList<>(); // empty!
}
@Override
public List<ILocation> getNeighbourhood(ILocation loc, int dist) {
if (dist < 0 || loc == null) {
throw new IllegalArgumentException();
} else if (dist == 0) {
return new ArrayList<>(); // empty!
}
//Goes in wider and wider squares, until we have reached dist.
List<ILocation> locations = new ArrayList<>();
for (int i = 1; i <= dist; i++) {
ILocation current = loc;
boolean collidedWest = false, collidedEast = false, collidedNorth = false, collidedSouth = false;
for (int j = 0; j < i; j++) {
if (current.canGo(GridDirection.WEST)) {
current = current.go(GridDirection.WEST);
} else {
collidedWest = true;
}
if (current.canGo(GridDirection.NORTH)) {
current = current.go(GridDirection.NORTH);
} else {
collidedNorth = true;
}
}
for (int j = 0; j < i * 2 + 1; j++) {
if ((collidedNorth && collidedWest) && j < i + 1) {
} else if (collidedNorth && j < i * 2) {
if (current.canGo(GridDirection.EAST)) {
current = current.go(GridDirection.EAST);
} else {
collidedEast = true;
break;
}
} else {
if (collidedWest && !collidedNorth && j < i * 2 + 1 - i || !collidedWest || collidedNorth) {
if (j > 0) {
if (current.canGo(GridDirection.EAST)) {
current = current.go(GridDirection.EAST);
} else {
collidedEast = true;
break;
}
}
locations.add(current);
}
}
}
for (int j = 0; j < i * 2; j++) {
if (collidedNorth && j < i) {
} else if (collidedEast && j < i + 1) {
if (current.canGo(GridDirection.SOUTH)) {
current = current.go(GridDirection.SOUTH);
} else {
collidedSouth = true;
break;
}
} else {
if (current.canGo(GridDirection.SOUTH)) {
current = current.go(GridDirection.SOUTH);
locations.add(current);
} else {
collidedSouth = true;
break;
}
}
}
for (int j = 0; j < i * 2; j++) {
if (collidedEast && j < i) {
} else if (collidedSouth && j < i) {
if (current.canGo(GridDirection.WEST)) {
current = current.go(GridDirection.WEST);
}
} else {
if (current.canGo(GridDirection.WEST)) {
current = current.go(GridDirection.WEST);
locations.add(current);
}
}
}
for (int j = 0; j < i * 2 - 1; j++) {
if (collidedSouth || collidedWest) {
} else {
if (current.canGo(GridDirection.NORTH)) {
current = current.go(GridDirection.NORTH);
locations.add(current);
}
}
}
}
return locations;
List<ILocation> locations = new ArrayList<>();
for (int i = 1; i <= dist; i++) {
ILocation current = loc;
boolean blockedNorth = false, blockedSouth = false, blockedWest = false, blockedEast = false;
for (int j = 0; j < i; j++) { //Checks if we reach out of bounds in the North or West direction.
if (current.canGo(GridDirection.NORTHWEST)) {
current = current.go(GridDirection.NORTHWEST);
} else if (current.canGo(GridDirection.WEST)) {
current = current.go(GridDirection.WEST);
blockedNorth = true;
} else if (current.canGo(GridDirection.NORTH)) {
current = current.go(GridDirection.NORTH);
blockedWest = true;
} else {
blockedNorth = true;
blockedWest = true;
break;
}
}
current = loc;
for (int j = 0; j < i; j++) { //Checks if we reach out of bounds in the South or East direction.
if (current.canGo(GridDirection.SOUTHEAST)) {
current = current.go(GridDirection.SOUTHEAST);
} else if (current.canGo(GridDirection.EAST)) {
current = current.go(GridDirection.EAST);
blockedSouth = true;
} else if (current.canGo(GridDirection.SOUTH)) {
current = current.go(GridDirection.SOUTH);
blockedEast = true;
} else {
blockedSouth = true;
blockedEast = true;
break;
}
}
if (!blockedNorth && !blockedEast && !blockedWest && !blockedSouth) { //If nothing is blocked, get the top and bottom.
current = goTo(loc, GridDirection.NORTHWEST, i);
addLoc(locations, current, GridDirection.EAST, i * 2 + 1);
current = goTo(loc, GridDirection.SOUTHWEST, i);
addLoc(locations, current, GridDirection.EAST, i * 2 + 1);
} else if (blockedNorth && !blockedEast && !blockedWest) { //If top is blocked, get bottom.
current = goTo(loc, GridDirection.EAST, i);
addLoc(locations, current, GridDirection.SOUTH, i + 1);
current = goTo(loc, GridDirection.WEST, i);
addLoc(locations, current, GridDirection.SOUTH, i + 1);
} else if (blockedSouth && !blockedEast && !blockedWest) { //If bottom is blocked, get top.
current = goTo(loc, GridDirection.EAST, i);
addLoc(locations, current, GridDirection.NORTH, i + 1);
current = goTo(loc, GridDirection.WEST, i);
addLoc(locations, current, GridDirection.NORTH, i + 1);
}
if (blockedNorth && !blockedEast) { //Get sides if we have collided with top or bottom.
current = goTo(loc, GridDirection.EAST, i);
addLoc(locations, current, GridDirection.SOUTH, i + 1);
} else if (blockedNorth && !blockedWest) {
current = goTo(loc, GridDirection.WEST, i);
addLoc(locations, current, GridDirection.SOUTH, i + 1);
} else if (blockedSouth && !blockedEast) {
current = goTo(loc, GridDirection.EAST, i);
addLoc(locations, current, GridDirection.NORTH, i + 1);
} else if (blockedSouth && !blockedWest) {
current = goTo(loc, GridDirection.WEST, i);
addLoc(locations, current, GridDirection.NORTH, i + 1);
}
if (!blockedNorth && !blockedSouth && !blockedWest) { //Get east sides.
current = goTo(loc, GridDirection.NORTHWEST, i);
current = current.go(GridDirection.SOUTH);
addLoc(locations, current, GridDirection.SOUTH, i * 2 - 1);
} else if (blockedNorth && blockedWest) {
current = goTo(loc, GridDirection.SOUTH, i);
addLoc(locations, current, GridDirection.EAST, i);
} else if (blockedSouth && blockedWest) {
current = goTo(loc, GridDirection.NORTH, i);
addLoc(locations, current, GridDirection.EAST, i);
} else if (blockedWest) {
current = goTo(loc, GridDirection.NORTH, i);
addLoc(locations, current, GridDirection.EAST, i + 1);
current = goTo(loc, GridDirection.SOUTH, i);
addLoc(locations, current, GridDirection.EAST, i + 1);
}
if (!blockedNorth && !blockedSouth && !blockedEast) { //Get west sides.
current = goTo(loc, GridDirection.NORTHEAST, i);
current = current.go(GridDirection.SOUTH);
addLoc(locations, current, GridDirection.SOUTH, i * 2 - 1);
} else if (blockedNorth && blockedEast) {
current = goTo(loc, GridDirection.SOUTH, i);
addLoc(locations, current, GridDirection.WEST, i);
} else if (blockedSouth && blockedEast) {
current = goTo(loc, GridDirection.NORTH, i);
addLoc(locations, current, GridDirection.WEST, i);
} else if (blockedEast) {
current = goTo(loc, GridDirection.NORTH, i);
addLoc(locations, current, GridDirection.WEST, i + 1);
current = goTo(loc, GridDirection.SOUTH, i);
addLoc(locations, current, GridDirection.WEST, i + 1);
}
}
return locations;
}
/**
* Goes in a direction, and saves all locations it passes to a list.
*
* @param locations Target list
* @param loc The location to start from
* @param dir Which direction to go
* @param max Maximum amount of steps
*/
private void addLoc(List<ILocation> locations, ILocation loc, GridDirection dir, int max) {
for (int j = 0; j < max; j++) {
locations.add(loc);
if (loc.canGo(dir)) {
loc = loc.go(dir);
} else {
return;
}
}
}
/**
* Goes a set amount of steps in a set direction, from a location, and returns the new location.
*
* @param loc Start location
* @param dir Direction to go
* @param max Maximum amount of steps
* @return The new location
*/
private ILocation goTo(ILocation loc, GridDirection dir, int max) {
for (int j = 0; j < max; j++) {
loc = loc.go(dir);
}
return loc;
}
}

View File

@ -21,4 +21,13 @@ public interface IActor extends IItem {
* the target)
*/
int getDamage();
/**
* How many tiles the IActor is able to see in each direction.
*
* @return Number of tiles
*/
default int getVision() {
return 1;
}
}

View File

@ -1,6 +1,7 @@
package inf101.v18.rogue101.objects;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.IGame;
import javafx.scene.input.KeyCode;
import java.util.ArrayList;
@ -46,7 +47,7 @@ public class Player implements IPlayer {
}
}
private void pickUpV2(IGame game) {
/*private void pickUpV2(IGame game) {
List<IItem> items = game.getLocalItems();
if (items.size() < 1) {
game.displayMessage("There is nothing to pick up.");
@ -62,7 +63,7 @@ public class Player implements IPlayer {
} else {
game.displayMessage("Your inventory is full.");
}
}
}*/
private void drop(IGame game) {
//TODO: Find a way to implement V2.
@ -88,8 +89,11 @@ public class Player implements IPlayer {
}
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))) {
game.attack(dir, game.getMap().getActors(loc.go(dir)).get(0));
} else {
game.displayMessage("Umm, it is not possible to move in that direction.");
}
@ -117,7 +121,7 @@ public class Player implements IPlayer {
@Override
public int getMaxHealth() {
return 100;
return 1000;
}
@Override
@ -138,6 +142,10 @@ public class Player implements IPlayer {
@Override
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
showStatus(game);
if (hp < 1) {
game.displayStatus("Game Over");
}
return amount;
}
}

View File

@ -0,0 +1,36 @@
package inf101.v18.rogue101.shared;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IActor;
import java.util.List;
public class NPC {
/**
* Makes a NPC attack anything it can, or move closer to any enemy in its line of sight.
*
* @param game An IGame object
* @return True if the NPC made a move. False otherwise
*/
public static boolean tryAttack(IGame game) {
List<ILocation> neighbours = game.getVisible();
for (ILocation neighbour : neighbours) {
if (game.getMap().hasActors(neighbour)) {
ILocation current = game.getLocation();
GridDirection dir = game.locationDirection(current, neighbour);
IActor actor = game.getMap().getActors(neighbour).get(0); //We assume there is only one actor.
if (current.gridDistanceTo(neighbour) <= 1 && current.canGo(dir) && game.getMap().has(current.go(dir), actor) && !actor.isDestroyed()) {
game.attack(dir, actor);
return true;
} else if (game.canGo(dir)) {
game.move(dir);
return true;
}
}
}
return false;
}
}

View File

@ -97,7 +97,6 @@ public class GameMapTest {
location = gameMap.getLocation(10, 0);
neighbours = gameMap.getNeighbourhood(location, 3);
System.out.println(neighbours);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
@ -105,7 +104,6 @@ public class GameMapTest {
location = gameMap.getLocation(10, 19);
neighbours = gameMap.getNeighbourhood(location, 3);
System.out.println(neighbours);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}

View File

@ -72,20 +72,10 @@ public class PlayerTest {
game.doTurn();
player.keyPressed(game, KeyCode.LEFT);
assertEquals(loc.go(GridDirection.WEST), game.getLocation());
game.doTurn();
player.keyPressed(game, KeyCode.UP);
game.doTurn();
player.keyPressed(game, KeyCode.RIGHT);
assertEquals(loc.go(GridDirection.NORTH), game.getLocation());
game.doTurn();
player.keyPressed(game, KeyCode.DOWN);
game.doTurn();
player.keyPressed(game, KeyCode.DOWN);
assertEquals(loc.go(GridDirection.SOUTH), game.getLocation());
}
@Test
public void testWallsAndKeys() {
public void testWallsAndKeys() { //This probably seems a little overkill, but we need to check all keys.
Game game = new Game(EMPTY_MAP);
IPlayer player = (IPlayer) game.setCurrent(2, 2);
ILocation loc = game.getLocation();
@ -98,21 +88,15 @@ public class PlayerTest {
game.doTurn();
player.keyPressed(game, KeyCode.RIGHT);
game.doTurn();
player.keyPressed(game, KeyCode.RIGHT);
game.doTurn();
assertEquals(loc.go(GridDirection.EAST), game.getLocation());
player.keyPressed(game, KeyCode.LEFT);
game.doTurn();
player.keyPressed(game, KeyCode.DOWN);
game.doTurn();
player.keyPressed(game, KeyCode.DOWN);
game.doTurn();
assertEquals(loc.go(GridDirection.SOUTH), game.getLocation());
player.keyPressed(game, KeyCode.UP);
game.doTurn();
player.keyPressed(game, KeyCode.LEFT);
game.doTurn();
player.keyPressed(game, KeyCode.LEFT);
assertEquals(loc.go(GridDirection.WEST), game.getLocation());
}
}