This commit is contained in:
Anya Helene Bagge
2018-03-17 22:22:11 +01:00
63 changed files with 2317 additions and 190 deletions

View File

@ -332,7 +332,8 @@ public class Screen {
private final Map<IPaintLayer, Canvas> layerCanvases = new IdentityHashMap<>();
private final Canvas background;
private final Group root;
private Paint bgColor = Color.CORNFLOWERBLUE;
//private Paint bgColor = Color.CORNFLOWERBLUE;
private Paint bgColor = Color.BLACK;
private int aspect = 0;
private double scaling = 0;
private double currentScale = 1.0;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -12,7 +12,7 @@ public class BlocksAndBoxes {
private List<Integer> order;
private PixelOrder(int a, int b, int c, int d) {
PixelOrder(int a, int b, int c, int d) {
order = Arrays.asList(a, b, c, d);
}
@ -44,7 +44,7 @@ public class BlocksAndBoxes {
public static final String BLOCK_REVERSE_BOTTOM_RIGHT = "";
public static final String BLOCK_FULL = "";
public static final String BLOCK_HALF = "";;
public static final String BLOCK_HALF = "";
public static String blockAddOne(String s, PixelOrder order) {
int i = BlocksAndBoxes.unicodeBlocksString.indexOf(s);

View File

@ -74,7 +74,7 @@ public class DemoPages {
printer.println(" 0123456789ABCDEF 0123456789ABCDEF");
for (int y = 0; y < 16; y++) {
printer.print(String.format(" %X", y));
int c = 0x00 + y * 0x010;
int c = y * 0x010;
for (int x = 0; x < 16; x++) {
printer.print(c >= 0x20 ? Character.toString((char) (c + x)) : " ");
}

View File

@ -75,7 +75,7 @@ public class Printer implements IPaintLayer {
return r;
}
Color DEFAULT_FILL = Color.BLACK;
Color DEFAULT_FILL = Color.WHITE;
Color DEFAULT_STROKE = Color.TRANSPARENT;
@ -91,7 +91,7 @@ public class Printer implements IPaintLayer {
// private int pageWidth = LINE_WIDTHS[resMode], pageHeight =
// PAGE_HEIGHTS[resMode];
private int leftMargin = 1, topMargin = 1;
private TextFont font = FONT_LMMONO;
private TextFont font = FONT_SYMBOLA;
private int videoAttrs = 0;
private String csiSeq = null;

View File

@ -305,8 +305,6 @@ public class TextFont {
* @param font
* Name of the font file. Will search for the file in the same folder
* as the TextFont class, as well as ".." and "../fonts".
* @param size
* Point size of the font.
* @param squareSize
* The width and height of a square defining the bounds of letters
* @param xTranslate
@ -350,10 +348,7 @@ public class TextFont {
* {@link #setGraphicsContext(GraphicsContext, double)} for extra on-the-fly
* horizontal scaling, e.g. to make half-width letters ("hires" mode).
*
*
* @param font
* Name of the font file. Will search for the file in the same folder
* as the TextFont class, as well as ".." and "../fonts".
*
* @param size
* Point size of the font.
* @param squareSize
@ -400,10 +395,7 @@ public class TextFont {
* {@link #setGraphicsContext(GraphicsContext, double)} for extra on-the-fly
* horizontal scaling, e.g. to make half-width letters ("hires" mode).
*
*
* @param font
* Name of the font file. Will search for the file in the same folder
* as the TextFont class, as well as ".." and "../fonts".
*
* @param size
* Point size of the font.
* @param squareSize
@ -739,13 +731,13 @@ public class TextFont {
case '0':
return -2 * thin;
case '2':
ctx.setLineDashes(new double[] { 14.75, 2.5 });
ctx.setLineDashes(14.75, 2.5);
break;
case '3':
ctx.setLineDashes(new double[] { 9, 2.5 });
ctx.setLineDashes(9, 2.5);
break;
case '4':
ctx.setLineDashes(new double[] { 6.125, 2.5 });
ctx.setLineDashes(6.125, 2.5);
break;
case '.':
return 0.0;

View File

@ -8,6 +8,8 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Objects;
public class TextFontAdjuster extends Application {
// private static final String FONT_NAME = "PetMe64.ttf";
// new TextFont(FONT_NAME, 22.2, TextModes.CHAR_BOX_SIZE, 0.0, 0.0, 1.0, 1.0);
@ -219,7 +221,7 @@ public class TextFontAdjuster extends Application {
return false;
});
screen.setKeyTypedHandler((KeyEvent event) -> {
if (event.getCharacter() != KeyEvent.CHAR_UNDEFINED) {
if (!Objects.equals(event.getCharacter(), KeyEvent.CHAR_UNDEFINED)) {
printer.print(event.getCharacter());
return true;
}

View File

@ -55,7 +55,7 @@ public enum TextMode {
private int vIndex;
private TextMode(int w, int h, int aspect) {
TextMode(int w, int h, int aspect) {
this.hIndex = w;
this.vIndex = h;
this.aspect = aspect;

View File

@ -32,7 +32,7 @@ public enum GridDirection {
private final int dy;
private final int mask;
private GridDirection(double degrees, int dx, int dy, int mask) {
GridDirection(double degrees, int dx, int dy, int mask) {
this.degrees = degrees;
this.dx = dx;
this.dy = dy;

View File

@ -52,8 +52,8 @@ public interface IGrid<T> extends Iterable<T> {
* // clear the grid
* grid.setAll(null);
* </pre>
*
* @param initialiser
*
* @param element
*/
void fill(T element);
@ -203,8 +203,6 @@ public interface IGrid<T> extends Iterable<T> {
* y must be greater than or equal to 0 and less than getHeight(). x must be
* greater than or equal to 0 and less than getWidth().
*
* @param pos
* The (x,y) position of the grid cell to get the contents of.
* @param element
* The contents the cell is to have.
* @throws IndexOutOfBoundsException

View File

@ -91,7 +91,7 @@ public interface ILocation extends IPosition {
* Find the grid cells between this location (exclusive) and another location
* (inclusive).
*
* This is will be a list of length {@link #gridDistanceTo(other)}, containing
* This is a list of length {@link #gridDistanceTo(other)}, containing
* the cells that a chess queen would visit when moving to <code>other</code>.
* <p>
* Computes the maximum of the horizontal and the vertical distance. For

View File

@ -23,8 +23,7 @@ public class MyGrid<T> implements IGrid<T> {
* new MyGrid(10, 10, ((x, y) -> String.format("(%d,%d)", x, y));
* </pre>
*
* @param width
* @param height
* @param area
* @param initialiser
* The initialiser function
*/
@ -43,8 +42,7 @@ public class MyGrid<T> implements IGrid<T> {
/**
* Construct a grid with the given dimensions.
*
* @param width
* @param height
* @param area
* @param initElement
* What the cells should initially hold (possibly null)
*/
@ -95,9 +93,7 @@ public class MyGrid<T> implements IGrid<T> {
@Override
public IGrid<T> copy() {
MyGrid<T> newGrid = new MyGrid<>(getWidth(), getHeight(), (l) -> get(l));
return newGrid;
return new MyGrid<>(getWidth(), getHeight(), (l) -> get(l));
}
@Override

View File

@ -80,10 +80,7 @@ public class RectArea implements IArea {
if (x != other.getX()) {
return false;
}
if (y != other.getY()) {
return false;
}
return true;
return y == other.getY();
}
@Override
@ -304,10 +301,7 @@ public class RectArea implements IArea {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("RectArea [width=").append(width).append(", height=").append(height).append(", hWrap=")
.append(hWrap).append(", vWrap=").append(vWrap).append("]");
return builder.toString();
return "RectArea [width=" + width + ", height=" + height + ", hWrap=" + hWrap + ", vWrap=" + vWrap + "]";
}
@Override

View File

@ -12,7 +12,7 @@ import static org.junit.Assert.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.junit.Test;
public class AreaRetting {
private static final int N = 10000;

View File

@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import org.junit.Test;
public class GridRetting {
private static final int N = 10000;

View File

@ -20,7 +20,7 @@ import javafx.util.Duration;
public class Main extends Application {
// you might want to tune these options
public static final boolean MAIN_USE_BACKGROUND_GRID = true;
public static final boolean MAIN_USE_BACKGROUND_GRID = false;
public static final boolean MAP_AUTO_SCALE_ITEM_DRAW = true;
public static final boolean MAP_DRAW_ONLY_DIRTY_CELLS = false;
public static final TextMode MAIN_TEXT_MODE = TextMode.MODE_80X25;
@ -88,7 +88,7 @@ public class Main extends Application {
printer.setTextMode(MAIN_TEXT_MODE, true);
// Font with emojis need separate download
// printer.setFont(Printer.FONT_SYMBOLA);
printer.setFont(Printer.FONT_SYMBOLA);
if (grid)
printer.drawCharCells();
@ -123,18 +123,21 @@ public class Main extends Application {
printer.redrawTextPage();
return true;
}
} else if (code == KeyCode.ENTER) {
/*} else if (code == KeyCode.ENTER) {
try {
doTurn();
} catch (Exception e) {
printer.printAt(1, 25, "Exception: " + e.getMessage(), Color.RED);
e.printStackTrace();
}
return true;
return true;*/ // This interferes with other code
} else {
try {
game.keyPressed(code);
doTurn();
if (game.keyPressed(code)) {
game.draw();
} else {
doTurn();
}
} catch (Exception e) {
e.printStackTrace();
try {

View File

@ -0,0 +1,78 @@
package inf101.v18.rogue101.enemies;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.items.Backpack;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.INonPlayer;
public class Boss implements INonPlayer {
private int hp = getMaxHealth();
Backpack backpack = new Backpack();
@Override
public void doTurn(IGame game) {
}
@Override
public int getAttack() {
return 50;
}
@Override
public int getDamage() {
return 15;
}
@Override
public IItem getItem(Class<?> type) {
for (IItem item : backpack.getContent()) {
if (type.isInstance(item)) {
return item;
}
}
return null;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return 50;
}
@Override
public int getMaxHealth() {
return 500;
}
@Override
public String getName() {
return "Lucifer";
}
@Override
public int getSize() {
return 50;
}
@Override
public String getPrintSymbol() {
return "\uD83D\uDE08";
}
@Override
public String getSymbol() {
return "B";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
//TODO: Drop item on death.
hp -= amount;
return amount;
}
}

View File

@ -0,0 +1,260 @@
package inf101.v18.rogue101.enemies;
import inf101.v18.grid.GridDirection;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.items.*;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.INonPlayer;
import inf101.v18.rogue101.shared.NPC;
import inf101.v18.rogue101.states.Age;
import inf101.v18.rogue101.states.Occupation;
import inf101.v18.rogue101.states.Personality;
import java.nio.channels.ClosedSelectorException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class Girl implements INonPlayer {
private final String name = randomName();
private final Age age = Age.getRandom();
private final Personality personality = Personality.getRandom();
private final Occupation occupation = Occupation.getRandom();
private int maxhp;
private int hp;
private int attack;
private int defence;
private int visibility;
private Backpack backpack = new Backpack();
private static final Random random = new Random();
private List<Class<?>> validItems;
public Girl() {
setStats();
setValidItems();
}
//TODO: Game balance
private void setStats() {
switch (age) {
case TODDLER:
maxhp = 100;
attack = 10;
defence = 50;
visibility = 1;
break;
case CHILD:
maxhp = 150;
attack = 20;
defence = 40;
visibility = 2;
break;
case TEEN:
maxhp = 200;
attack = 25;
defence = 30;
visibility = 3;
break;
case ADULT:
maxhp = 250;
attack = 30;
defence = 20;
visibility = 4;
break;
case ELDER:
maxhp = 200;
attack = 15;
defence = 35;
visibility = 2;
break;
}
if (occupation == Occupation.KNIGHT) {
attack += 10; //Knights are quite powerful.
}
if (occupation == Occupation.MAGE) {
attack += 5; // Mages have lesser range than bowsmen, but more damage.
}
maxhp += (int)(random.nextGaussian() * 10);
hp = maxhp;
attack += (int)(random.nextGaussian() * 5);
defence += (int)(random.nextGaussian() * 5);
}
/**
* Specified which items the current Girl should be able to pick up.
*/
private void setValidItems() {
validItems = new ArrayList<>();
switch (occupation) {
case BOWSMAN:
validItems.add(IRangedWeapon.class);
break;
case MAGE:
validItems.add(IMagicWeapon.class);
break;
case KNIGHT:
validItems.add(IMeleeWeapon.class);
}
validItems.add(IBuffItem.class);
}
private String randomName() {
//TODO: Choose from a list of names, or generate name.
return "Girl";
}
@Override
public void doTurn(IGame game) {
if (!backpack.isFull()) {
IItem item = NPC.pickUp(game, validItems);
if (item != null) {
backpack.add(item);
return;
}
if (NPC.trackItem(game, validItems)) {
return;
}
}
if (personality == Personality.AFRAID && NPC.flee(game)) {
return;
}
if (willAttack(game) && attack(game)) {
return;
}
List<GridDirection> possibleMoves = game.getPossibleMoves();
if (!possibleMoves.isEmpty()) {
Collections.shuffle(possibleMoves);
game.move(possibleMoves.get(0));
}
}
/**
* Tries to attack with an attack specified by the Girl's occupation
*
* @param game An IGame object
* @return True if an attack occurred. False otherwise
*/
private boolean attack(IGame game) {
switch (occupation) {
case KNIGHT:
if (NPC.tryAttack(game)) {
return true;
}
break;
case MAGE:
if (NPC.tryAttackRanged(game, 2)) {
return true;
}
case BOWSMAN:
if (NPC.tryAttackRanged(game, 4)) {
return true;
}
}
return false;
}
/**
* Checks if the current girl will try to attack.
*
* @param game An IGame object
* @return True if the girl will attack. False otherwise
*/
private boolean willAttack(IGame game) {
boolean attack = false;
switch (personality) {
case CALM:
if (hp < maxhp) {
attack = true;
}
break;
case AFRAID:
if (game.getPossibleMoves().isEmpty()) {
attack = true;
}
break;
case AGRESSIVE:
attack = true;
break;
}
return attack;
}
/**
* Retrieves a girl's occupation.
*
* @return
*/
public Occupation getOccupation() {
return occupation;
}
@Override
public int getAttack() {
return attack;
}
@Override
public int getDamage() {
return 5;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return defence;
}
@Override
public int getMaxHealth() {
return maxhp;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return 30;
}
@Override
public String getPrintSymbol() {
return "\u001b[95m" + "\uD83D\uDEB6" + "\u001b[0m";
}
@Override
public String getSymbol() {
return "G";
}
@Override
public int getVision() {
return visibility;
}
@Override
public IItem getItem(Class<?> type) {
for (IItem item : backpack.getContent()) {
if (type.isInstance(item)) {
return item;
}
}
return null;
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
return amount;
}
}

View File

@ -8,7 +8,7 @@ import javafx.scene.paint.Color;
public class Carrot implements IItem {
private int hp = getMaxHealth();
public void doTurn(IGame game) {
public void doTurn() {
hp = Math.min(hp + 1, getMaxHealth());
}
@ -50,7 +50,7 @@ public class Carrot implements IItem {
@Override
public int getMaxHealth() {
return 10;
return 23;
}
@Override
@ -74,7 +74,7 @@ public class Carrot implements IItem {
if (hp < 0) {
// we're all eaten!
hp = -1;
hp = 0;
}
return amount;
}

View File

@ -9,39 +9,49 @@ import inf101.v18.grid.GridDirection;
import inf101.v18.rogue101.game.IGame;
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;
private int hp = getMaxHealth();
private static List<Class<?>> validItems = new ArrayList<>();
static {
validItems.add(Carrot.class);
}
@Override
public void doTurn(IGame game) {
if (food == 0)
if (food == 0) {
hp--;
else
} else {
food--;
if (hp < 1)
}
if (hp < 1) {
return;
}
if (NPC.tryAttack(game)) {
return;
}
for (IItem item : game.getLocalItems()) {
if (item instanceof Carrot) {
System.out.println("found carrot!");
int eaten = item.handleDamage(game, this, 5);
int eaten = item.handleDamage(game, this, getDamage());
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;
}
}
}
// TODO: prøv forskjellige varianter her
List<GridDirection> possibleMoves = new ArrayList<>();
for (GridDirection dir : GridDirection.FOUR_DIRECTIONS) {
if (game.canGo(dir))
possibleMoves.add(dir);
if (NPC.trackItem(game, validItems)) {
return;
}
List<GridDirection> possibleMoves = game.getPossibleMoves();
if (!possibleMoves.isEmpty()) {
Collections.shuffle(possibleMoves);
game.move(possibleMoves.get(0));
@ -55,7 +65,7 @@ public class Rabbit implements INonPlayer {
@Override
public int getAttack() {
return 1000;
return 10;
}
@Override
@ -65,22 +75,22 @@ public class Rabbit implements INonPlayer {
@Override
public int getDamage() {
return 1000;
return 5;
}
@Override
public int getDefence() {
return 1000;
}
@Override
public int getMaxHealth() {
return 10;
}
@Override
public int getMaxHealth() {
return 30;
}
@Override
public String getName() {
return "rabbit";
return "Rabbit";
}
@Override
@ -88,6 +98,16 @@ public class Rabbit implements INonPlayer {
return 4;
}
@Override
public int getVision() {
return 4;
}
@Override
public IItem getItem(Class<?> type) {
return null;
}
@Override
public String getSymbol() {
return hp > 0 ? "R" : "¤";
@ -98,5 +118,4 @@ public class Rabbit implements INonPlayer {
hp -= amount;
return amount;
}
}

View File

@ -17,18 +17,17 @@ import inf101.v18.grid.GridDirection;
import inf101.v18.grid.IGrid;
import inf101.v18.grid.ILocation;
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.items.*;
import inf101.v18.rogue101.examples.Rabbit;
import inf101.v18.rogue101.map.GameMap;
import inf101.v18.rogue101.map.IGameMap;
import inf101.v18.rogue101.map.IMapView;
import inf101.v18.rogue101.map.MapReader;
import inf101.v18.rogue101.objects.Dust;
import inf101.v18.rogue101.objects.IActor;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.INonPlayer;
import inf101.v18.rogue101.objects.IPlayer;
import inf101.v18.rogue101.objects.Wall;
import inf101.v18.rogue101.objects.*;
import inf101.v18.rogue101.shared.NPC;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
@ -46,6 +45,12 @@ public class Game implements IGame {
* Useful random generator
*/
private Random random = new Random();
/**
* Saves the last three messages
*/
private List<String> lastMessages = new ArrayList<>();
/**
* The game map. {@link IGameMap} gives us a few more details than
* {@link IMapView} (write access to item lists); the game needs this but
@ -63,9 +68,7 @@ public class Game implements IGame {
this.painter = painter;
this.printer = printer;
// TODO: (*very* optional) for advanced factory technique, use
// something like "itemFactories.put("R", () -> new Rabbit());"
// must be done *before* you read the map
addFactory();
// NOTE: in a more realistic situation, we will have multiple levels (one map
// per level), and (at least for a Roguelike game) the levels should be
@ -74,7 +77,7 @@ public class Game implements IGame {
// inputGrid will be filled with single-character strings indicating what (if
// anything)
// should be placed at that map square
IGrid<String> inputGrid = MapReader.readFile("maps/level1.txt");
IGrid<String> inputGrid = MapReader.readFile("maps/testmap.txt");
if (inputGrid == null) {
System.err.println("Map not found falling back to builtin map");
inputGrid = MapReader.readString(Main.BUILTIN_MAP);
@ -87,11 +90,19 @@ 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-5 to choose an item", "N to change name", "ENTER to confirm", "R to use a ranged attack", "F to use a magic attack"};
for (int i = 0; i < info.length; i++) {
this.printer.printAt(map.getWidth() + 2, 1 + i, info[i]);
}
}
public Game(String mapString) {
printer = new Printer(1280, 720);
painter = new TurtlePainter(1280, 720);
addFactory();
IGrid<String> inputGrid = MapReader.readString(mapString);
this.map = new GameMap(inputGrid.getArea());
for (ILocation loc : inputGrid.locations()) {
@ -114,14 +125,74 @@ public class Game implements IGame {
map.add(currentLocation, item);
}
/**
* Calculates the attack of an IActor based on equipped items.
*
* @return The attack
*/
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();
}
return damage;
}
/**
* Gets the defence of the current target.
*
* @param target The target to evaluate
* @return The defence of the target
*/
private int getDefence(IItem target) {
int defence = target.getDefence() + 10;
IActor actor = (IActor) target;
IBuffItem item = (IBuffItem) actor.getItem(IBuffItem.class);
if (item != null) {
defence += item.getBuffDefence();
}
return defence;
}
/**
* Gets the damage done against the current target.
*
* @param target The target to evaluate.
* @return The damage done to the target.
*/
private int getDamage(IItem target) {
int damage = currentActor.getDamage();
IActor actor = (IActor) target;
IBuffItem item = (IBuffItem) actor.getItem(IBuffItem.class);
if (item != null) {
damage -= item.getBuffDamageReduction();
}
return damage;
}
@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
}
IWeapon weapon = (IWeapon) currentActor.getItem(IWeapon.class);
if (weapon != null) {
NPC.playSound(weapon.getSound());
} else {
NPC.playSound("audio/Realistic_Punch-Mark_DiAngelo-1609462330.wav");
}
if (getAttack() >= getDefence(target)) {
int actualDamage = target.handleDamage(this, target, getDamage(target));
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
} else {
formatMessage("%s tried to hit %s, but missed", currentActor.getName(), target.getName());
}
map.clean(loc);
@ -133,6 +204,30 @@ public class Game implements IGame {
}
}
@Override
public ILocation rangedAttack(GridDirection dir, IItem target) {
ILocation loc = currentLocation;
IWeapon weapon = (IWeapon) currentActor.getItem(IWeapon.class);
if (weapon != null) {
NPC.playSound(weapon.getSound());
} else {
NPC.playSound("audio/Snow Ball Throw And Splat-SoundBible.com-992042947.wav");
}
if (getAttack() >= getDefence(target)) {
int damage = getDamage(target) / loc.gridDistanceTo(map.getLocation(target));
int actualDamage = target.handleDamage(this, target, damage);
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
} else {
formatMessage("%s tried to hit %s, but missed", currentActor.getName(), target.getName());
}
map.clean(map.getLocation(target));
if (target.isDestroyed() && map.has(currentLocation.go(dir), target)) {
return move(dir);
} else {
return currentLocation;
}
}
/**
* Begin a new game turn, or continue to the previous turn
*
@ -148,8 +243,15 @@ public class Game implements IGame {
beginTurn();
}
if (random.nextInt(100) < 20) {
ILocation loc = map.getLocation(random.nextInt(map.getWidth()), random.nextInt(map.getHeight()));
if (!map.hasActors(loc) && !map.hasItems(loc) && !map.hasWall(loc)) {
map.add(loc, new Carrot());
}
}
// process actors one by one; for the IPlayer, we return and wait for keypresses
// Possible TODO: for INonPlayer, we could also return early (returning
// Possible for INonPlayer, we could also return early (returning
// *false*), and then insert a little timer delay between each non-player move
// (the timer
// is already set up in Main)
@ -169,6 +271,7 @@ public class Game implements IGame {
((INonPlayer) currentActor).doTurn(this);
// remove any dead items from current location
map.clean(currentLocation);
return false;
} else if (currentActor instanceof IPlayer) {
if (currentActor.isDestroyed()) {
// a dead human player gets removed from the game
@ -231,6 +334,8 @@ public class Game implements IGame {
}
} else if (item instanceof IActor) {
actors.add((IActor) item); // add other actors to the end of the list
} else if (item instanceof Carrot) {
((Carrot) item).doTurn();
}
}
});
@ -241,20 +346,22 @@ public class Game implements IGame {
return map.canGo(currentLocation, dir);
}
private void addFactory() {
itemFactories.put("#", Wall::new);
itemFactories.put("@", Player::new);
itemFactories.put("C", Carrot::new);
itemFactories.put("R", Rabbit::new);
itemFactories.put("M", Manga::new);
itemFactories.put("G", Girl::new);
itemFactories.put(".", Dust::new);
itemFactories.put("S", Sword::new);
itemFactories.put("c", Chest::new);
itemFactories.put("B", Boss::new);
}
@Override
public IItem createItem(String sym) {
switch (sym) {
case "#":
return new Wall();
case ".":
// TODO: add Dust
return null;
case "R":
return new Rabbit();
case "C":
return new Carrot();
case "@":
// TODO: add Player
case " ":
return null;
default:
@ -278,11 +385,22 @@ public class Game implements IGame {
@Override
public void displayMessage(String s) {
// it should be safe to print to lines Main.LINE_MSG1, Main.LINE_MSG2,
// Main.LINE_MSG3
// TODO: you can save the last three lines, and display/scroll them
if (lastMessages.size() >= 3) {
lastMessages.remove(2);
}
lastMessages.add(0, s);
printer.clearLine(Main.LINE_MSG1);
printer.printAt(1, Main.LINE_MSG1, s);
printer.clearLine(Main.LINE_MSG2);
printer.clearLine(Main.LINE_MSG3);
if (lastMessages.size() > 0) {
printer.printAt(1, Main.LINE_MSG1, lastMessages.get(0));
}
if (lastMessages.size() > 1) {
printer.printAt(1, Main.LINE_MSG2, lastMessages.get(1));
}
if (lastMessages.size() > 2) {
printer.printAt(1, Main.LINE_MSG3, lastMessages.get(2));
}
System.out.println("Message: «" + s + "»");
}
@ -294,7 +412,9 @@ public class Game implements IGame {
}
public void draw() {
map.draw(painter, printer);
//map.draw(painter, printer);
GameMap aMap = (GameMap) map;
aMap.drawVisible(painter, printer, this);
}
@Override
@ -356,15 +476,29 @@ public class Game implements IGame {
@Override
public List<GridDirection> getPossibleMoves() {
// TODO
throw new UnsupportedOperationException();
List<GridDirection> moves = new ArrayList<>();
for (GridDirection dir : GridDirection.FOUR_DIRECTIONS) {
if (canGo(dir)) {
moves.add(dir);
}
}
return moves;
}
@Override
public List<ILocation> getVisible() {
// TODO: maybe replace 3 by some sort of visibility range obtained from
// currentActor?
return map.getNeighbourhood(currentLocation, 3);
List<ILocation> neighbours = map.getNeighbourhood(currentLocation, currentActor.getVision());
List<ILocation> invalid = new ArrayList<>();
for (ILocation neighbour : neighbours) {
for (ILocation tile : currentLocation.gridLineTo(neighbour)) {
if (map.hasWall(tile)) {
invalid.add(neighbour);
break;
}
}
}
neighbours.removeAll(invalid);
return neighbours;
}
@Override
@ -372,14 +506,13 @@ public class Game implements IGame {
return map.getWidth();
}
public void keyPressed(KeyCode code) {
public boolean keyPressed(KeyCode code) {
// only an IPlayer/human can handle keypresses, and only if it's the human's
// turn
if (currentActor instanceof IPlayer) {
((IPlayer) currentActor).keyPressed(this, code); // do your thing
if (movePoints <= 0)
doTurn(); // proceed with turn if we're out of moves
return !((IPlayer) currentActor).keyPressed(this, code);
}
return true;
}
@Override
@ -396,22 +529,25 @@ public class Game implements IGame {
@Override
public IItem pickUp(IItem item) {
if (item != null && map.has(currentLocation, item)) {
// TODO: bruk getAttack()/getDefence() til å avgjøre om man får til å plukke opp
// tingen
// evt.: en IActor kan bare plukkes opp hvis den har få/ingen helsepoeng igjen
map.remove(currentLocation, item);
return item;
if (item != null && map.has(currentLocation, item) && !(item instanceof IStatic)) {
if (item instanceof IActor) {
if (item.getCurrentHealth() / item.getMaxHealth() < 3) {
map.remove(currentLocation, item);
return item;
} else {
return null;
}
} else if (currentActor.getAttack() > item.getDefence()) {
map.remove(currentLocation, item);
return item;
} else {
return null;
}
} else {
return null;
}
}
@Override
public ILocation rangedAttack(GridDirection dir, IItem target) {
return currentLocation;
}
@Override
public ITurtle getPainter() {
return painter;
@ -486,4 +622,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

@ -187,7 +187,7 @@ public interface IGame {
double[] getFreeGraphicsAreaBounds();
/**
* Get the bounds of the free texxt area.
* Get the bounds of the free text area.
* <p>
* You can fill this with whatever you want, using {@link #getPrinter()} and
* {@link #clearFreeTextArea()}.
@ -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

@ -0,0 +1,123 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
import java.util.ArrayList;
import java.util.List;
public class Backpack implements IContainer {
/**
* A list containing everything in the backpack.
*/
private List<IItem> content = new ArrayList<>();
/**
* The maximum amount of items allowed in a single backpack.
*/
private final int MAX_SIZE = 50;
@Override
public int getCurrentHealth() {
return 0;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 0;
}
@Override
public String getName() {
return "Backpack";
}
@Override
public int getSize() {
return 20;
}
@Override
public String getSymbol() {
return "B";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
return 0;
}
/**
* Retrieves the current size of the Backpack.
*
* @return
*/
public int size() {
int totalSize = 0;
for (IItem item : content) {
totalSize += item.getSize();
}
return totalSize;
}
/**
* Checks if the Backpack is empty
*
* @return True if empty. False otherwise
*/
public boolean isEmpty() {
return content.isEmpty();
}
/**
* Tries to add an item to the Backpack
*
* @param item The item to add
* @return
*/
public boolean add(IItem item) {
if (size() < MAX_SIZE) {
content.add(item);
return true;
} else {
return false;
}
}
/**
* Removes an element at index i
*
* @param i The index of an element
*/
public void remove(int i) {
content.remove(i);
}
/**
* Gets a T at index i,
*
* @param i The index of an element
* @return An object of type T
*/
public IItem get(int i) {
return content.get(i);
}
/**
* Gets the content List for direct manipulation.
*
* @return A list of T
*/
public List<IItem> getContent() {
return content;
}
@Override
public boolean isFull() {
return size() >= MAX_SIZE;
}
}

View File

@ -0,0 +1,52 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
import java.util.Random;
public class Bow implements IRangedWeapon {
private static final Random random = new Random();
private final int damage = 3 + random.nextInt(20);
private final int hp = getMaxHealth();
@Override
public int getWeaponDamage() {
return damage;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 110;
}
@Override
public String getName() {
return "Unknown bow";
}
@Override
public int getSize() {
return 2;
}
@Override
public String getSymbol() {
return "B";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
return 0;
}
}

View File

@ -0,0 +1,73 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
import java.util.ArrayList;
import java.util.List;
public class Chest implements IContainer, IStatic {
private List<IItem> container;
public Chest() {
this.container = new ArrayList<>();
}
public Chest(List<IItem> items) {
this.container = items;
}
@Override
public IItem get(int i) {
return null;
}
@Override
public List getContent() {
return container;
}
@Override
public boolean isFull() {
return false;
}
@Override
public int getCurrentHealth() {
return 0;
}
@Override
public int getMaxHealth() {
return 0;
}
@Override
public String getName() {
return "Chest";
}
@Override
public String getInteractMessage() {
return "Items in " + getName() + ": ";
}
@Override
public int getSize() {
return 10000;
}
public String getPrintSymbol() {
return "\uD83D\uDDC3";
}
@Override
public String getSymbol() {
return "C";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
return 0;
}
}

View File

@ -0,0 +1,33 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.objects.IItem;
public interface IBuffItem extends IItem {
/**
* Retrieve damage increase done by the buff item.
*
* @return An int, May be 0
*/
int getBuffDamage();
/**
* Retrieve defence increase done by the buff item.
*
* @return An int, May be 0
*/
int getBuffDefence();
/**
* Retrieve defence increase done by the buff item.
*
* @return An int, May be 0
*/
int getBuffDamageReduction();
/**
* Retrieve visibility increase done by the buff item.
*
* @return An int, May be 0
*/
int getBuffVisibility();
}

View File

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

View File

@ -0,0 +1,30 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.objects.IItem;
import java.util.List;
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.
*/
IItem get(int i);
/**
* Gets a list with everything inside a container.
*
* @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
*/
boolean isFull();
}

View File

@ -0,0 +1,12 @@
package inf101.v18.rogue101.items;
/**
* An interface to extinguish different weapons for specific NPC.
*/
public interface IMagicWeapon extends IWeapon {
@Override
default String getSound() {
return "audio/Bow_Fire_Arrow-Stephan_Schutze-2133929391.wav";
}
}

View File

@ -0,0 +1,12 @@
package inf101.v18.rogue101.items;
/**
* An interface to extinguish different weapons for specific NPC.
*/
public interface IMeleeWeapon extends IWeapon {
@Override
default String getSound() {
return "audio/Sword Swing-SoundBible.com-639083727.wav";
}
}

View File

@ -0,0 +1,12 @@
package inf101.v18.rogue101.items;
/**
* An interface to extinguish different weapons for specific NPC.
*/
public interface IRangedWeapon extends IWeapon {
@Override
default String getSound() {
return "audio/Bow_Fire_Arrow-Stephan_Schutze-2133929391.wav";
}
}

View File

@ -0,0 +1,22 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.objects.IItem;
public interface IStatic extends IItem {
@Override
default int getDefence() {
return 0;
}
@Override
default int getSize() {
return 10000;
}
/**
* A message to display when an interaction with the player happens.
*
* @return A message
*/
String getInteractMessage();
}

View File

@ -0,0 +1,19 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.objects.IItem;
public interface IWeapon extends IItem {
/**
* Retrieves the damage points of a weapon.
*
* @return
*/
int getWeaponDamage();
/**
* Retrieves the string path of the sound to play upon an attack.
*
* @return A string path
*/
String getSound();
}

View File

@ -0,0 +1,65 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
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;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 1;
}
@Override
public String getName() {
return "Manga";
}
@Override
public int getSize() {
return 5;
}
@Override
public String getPrintSymbol() {
return "🕮";
}
@Override
public String getSymbol() {
return "M";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
return amount;
}
}

View File

@ -0,0 +1,63 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
public class Shield implements IBuffItem {
private final int hp = getMaxHealth();
@Override
public int getBuffDamage() {
return 0;
}
@Override
public int getBuffDefence() {
return 10;
}
@Override
public int getBuffDamageReduction() {
return 5;
}
@Override
public int getBuffVisibility() {
return 0;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 150;
}
@Override
public String getName() {
return "Unknown shield";
}
@Override
public int getSize() {
return 2;
}
@Override
public String getSymbol() {
return "";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
return 0;
}
}

View File

@ -0,0 +1,53 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
import java.util.Random;
public class Staff implements IMagicWeapon {
private static final Random random = new Random();
private final int damage = 5 + random.nextInt(25);
private int hp = getMaxHealth();
@Override
public int getWeaponDamage() {
return damage;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 90;
}
@Override
public String getName() {
return "Unknown staff";
}
@Override
public int getSize() {
return 0;
}
@Override
public String getSymbol() {
return "s";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
return amount;
}
}

View File

@ -0,0 +1,53 @@
package inf101.v18.rogue101.items;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IItem;
import java.util.Random;
public class Sword implements IMeleeWeapon {
private static final Random random = new Random();
private final int damage = 5 + random.nextInt(25);
private int hp = getMaxHealth();
@Override
public int getWeaponDamage() {
return damage;
}
@Override
public int getCurrentHealth() {
return hp;
}
@Override
public int getDefence() {
return 0;
}
@Override
public int getMaxHealth() {
return 100;
}
@Override
public String getName() {
return "Unknown sword";
}
@Override
public int getSize() {
return 2;
}
@Override
public String getSymbol() {
return "S";
}
@Override
public int handleDamage(IGame game, IItem source, int amount) {
hp -= amount;
return amount;
}
}

View File

@ -1,12 +1,6 @@
package inf101.v18.rogue101.map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import inf101.v18.gfx.gfxmode.ITurtle;
import inf101.v18.gfx.textmode.Printer;
@ -16,9 +10,13 @@ import inf101.v18.grid.ILocation;
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.objects.IActor;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.IPlayer;
import inf101.v18.rogue101.objects.Wall;
import javafx.scene.canvas.GraphicsContext;
@ -48,6 +46,7 @@ public class GameMap implements IGameMap {
@Override
public void add(ILocation loc, IItem item) {
// keep track of location of all items
items.put(item, loc);
// also keep track of whether we need to redraw this cell
@ -55,8 +54,26 @@ public class GameMap implements IGameMap {
// do the actual adding
List<IItem> list = grid.get(loc);
for (int i = 0; i < list.size(); i++) {
if (item.compareTo(list.get(i)) >= 0) {
list.add(i, item);
return;
}
}
list.add(item);
// TODO: should be sorted!
}
public void addRandomItems(ILocation loc) {
if (!this.hasActors(loc) && !this.hasWall(loc)) {
Random random = new Random();
if (random.nextInt(100) < 20) {
this.add(loc, new Carrot());
}
if (random.nextInt(100) < 1) {
this.add(loc, new Manga());
}
}
}
@Override
@ -102,7 +119,7 @@ public class GameMap implements IGameMap {
try {
for (ILocation loc : cells) {
List<IItem> list = grid.get(loc);
String sym = " ";
String sym = ".";
if (!list.isEmpty()) {
if (Main.MAP_DRAW_ONLY_DIRTY_CELLS) {
ctx.clearRect(loc.getX() * w, loc.getY() * h, w, h);
@ -126,6 +143,88 @@ public class GameMap implements IGameMap {
dirtyLocs.clear();
}
/**
* Draws only the tiles visible to the player.
*
* @param painter
* @param printer
* @param game
*/
public void drawVisible(ITurtle painter, Printer printer, IGame game) {
Iterable<ILocation> cells;
if (Main.MAP_DRAW_ONLY_DIRTY_CELLS) {
if (dirtyLocs.isEmpty())
return;
else
cells = dirtyLocs;
} else {
cells = grid.locations();
painter.as(GraphicsContext.class).clearRect(0, 0, getWidth() * printer.getCharWidth(),
getHeight() * printer.getCharHeight());
printer.clearRegion(1, 1, getWidth(), getHeight());
}
GraphicsContext ctx = painter.as(GraphicsContext.class);
double h = printer.getCharHeight();
double w = printer.getCharWidth();
if (Main.MAP_AUTO_SCALE_ITEM_DRAW) {
ctx.save();
ctx.scale(w / h, 1.0);
w = h;
}
try {
IPlayer player = null;
ILocation playerPos = null;
for (ILocation loc : cells) {
printer.printAt(loc.getX() + 1, loc.getY() + 1, " ");
if (this.hasActors(loc) && this.getActors(loc).get(0) instanceof IPlayer) {
player = (IPlayer) this.getActors(loc).get(0);
playerPos = loc;
}
}
List<ILocation> positions = getVisible(getNeighbourhood(playerPos,player.getVision()), playerPos);
positions.add(playerPos);
for (ILocation loc : positions) {
List<IItem> list = grid.get(loc);
String sym = ".";
if (!list.isEmpty()) {
if (Main.MAP_DRAW_ONLY_DIRTY_CELLS) {
ctx.clearRect(loc.getX() * w, loc.getY() * h, w, h);
// ctx.fillRect(loc.getX() * w, loc.getY() * h, w, h);
}
painter.save();
painter.jumpTo((loc.getX() + 0.5) * w, (loc.getY() + 0.5) * h);
boolean dontPrint = list.get(0).draw(painter, w, h);
painter.restore();
if (!dontPrint) {
sym = list.get(0).getPrintSymbol();
}
}
printer.printAt(loc.getX() + 1, loc.getY() + 1, sym);
}
} finally {
if (Main.MAP_AUTO_SCALE_ITEM_DRAW) {
ctx.restore();
}
}
dirtyLocs.clear();
}
private List<ILocation> getVisible(List<ILocation> neighbours, ILocation loc) {
List<ILocation> invalid = new ArrayList<>();
for (ILocation neighbour : neighbours) {
if (!hasWall(neighbour)) {
for (ILocation tile : loc.gridLineTo(neighbour)) {
if (hasWall(tile)) {
invalid.add(neighbour);
break;
}
}
}
}
neighbours.removeAll(invalid);
return neighbours;
}
@Override
public List<IActor> getActors(ILocation loc) {
List<IActor> items = new ArrayList<>();
@ -241,14 +340,72 @@ 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!
// TODO: implement this!
throw new UnsupportedOperationException();
@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!
}
List<ILocation> neighbours = new ArrayList<>();
int startX = loc.getX();
int startY = loc.getY();
for (int i = 1; i <= dist; i++) {
int leftX = startX - i;
int rightX = startX + i;
int topY = startY - i;
int bottomY = startY + i;
for (int x = leftX + 1; x < rightX; x++) {
if (grid.isValid(x, topY)) {
neighbours.add(getLocation(x, topY));
}
if (grid.isValid(x, bottomY)) {
neighbours.add(getLocation(x, bottomY));
}
}
for (int y = topY; y <= bottomY; y++) {
if (grid.isValid(leftX, y)) {
neighbours.add(getLocation(leftX, y));
}
if (grid.isValid(rightX, y)) {
neighbours.add(getLocation(rightX, y));
}
}
}
return neighbours;
}
/**
* 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

@ -9,7 +9,7 @@
#. ...R..C. ..R.R..........C.RC....... #
#..C.....R..... ........RR R..R.....R..#
#...R..R.R..............R .R..R........#
#.R.....R........RRR.......R.. .C....R.#
#.R.....R...M....RRR.......R.. .C....R.#
#.C.. ..R. .....R.RC..C....R...R..C. .#
#. R..............R R..R........C.....R#
#........###############################

View File

@ -0,0 +1,21 @@
40 20
########################################
# #
# #
# #
# G G #
# #
############### # #
# # #
# S # #
# @ ######## #########
# # #
# # #
# # #
# G #
# G #
# c #
# ####
# # B#
# G #
########################################

View File

@ -21,4 +21,20 @@ 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;
}
/**
* Gets an item of the specified type, if the actor has the item.
*
* @return An IItem or null
*/
IItem getItem(Class<?> type);
}

View File

@ -20,6 +20,7 @@ public interface IPlayer extends IActor {
*
* @param game
* Game, for interacting with the world
* @return True if the player has done anything consuming a turn. False otherwise
*/
void keyPressed(IGame game, KeyCode key);
boolean keyPressed(IGame game, KeyCode key);
}

View File

@ -0,0 +1,351 @@
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 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.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 <= 5 && keyValue > 0) {
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 = NPC.tryAttackRanged(game, 3);
}
} else if (key == KeyCode.F) {
IItem item = getItem(IMagicWeapon.class);
if (item == null) {
game.displayMessage("You do not have a magic weapon.");
} else {
turnConsumed = NPC.tryAttackRanged(game, 2);
}
}
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) {
text = text.substring(0, text.length() - 1);
game.displayMessage("Please enter your name: " + text);
} else if (key != KeyCode.ENTER) {
text += key.toString();
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 {
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());
picking = true;
}
}
}
return false;
}
private void openChest(IGame game, List<IItem> items) {
IStatic item = (IStatic)items.get(0);
StringBuilder msg = new StringBuilder(item.getInteractMessage());
IContainer container = (IContainer) item;
items = container.getContent();
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());
exploringChest = true;
}
/**
* 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.getContent().size() == 1) {
drop(game, 0);
return true;
} else if (equipped.getContent().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());
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()) {
List<IItem> items = game.getLocalItems();
IItem item = items.get(0);
if (item instanceof IStatic && item instanceof IContainer) {
IContainer container = (IContainer) item;
IItem loot = container.getContent().get(i);
equipped.add(loot);
container.getContent().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.");
}
}
/**
* 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) {
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);
}
}
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 50;
}
@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 30;
}
@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;
}
}

View File

@ -0,0 +1,171 @@
package inf101.v18.rogue101.shared;
import inf101.v18.grid.GridDirection;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.enemies.Girl;
import inf101.v18.rogue101.game.IGame;
import inf101.v18.rogue101.objects.IActor;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.states.Occupation;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import java.io.File;
import java.util.List;
/**
* A holder class for methods used by IActors.
*/
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;
}
/**
* Performs a ranged attack on the closest actor (if any) observable by the current actor.
*
* @param game An IGame object
* @param range The range of the equipped weapon
* @return True if an attack or a move towards an enemy was successful. False otherwise
*/
public static boolean tryAttackRanged(IGame game, int range) {
// Ranged attacks don't care about walls.
List<ILocation> neighbours = game.getMap().getNeighbourhood(game.getLocation(), game.getActor().getVision());
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) <= range && !actor.isDestroyed()) {
game.rangedAttack(dir, actor);
return true;
} else if (game.canGo(dir)) {
game.move(dir);
return true;
}
}
}
return false;
}
/**
* Tries to find and reach an item of a specified class.
*
* @param game An IGame object
* @param validItems A list of classes the NPC should look for
* @return True if an appropriate item was found in the range of the current actor. False otherwise.
*/
public static boolean trackItem(IGame game, List<Class<?>> validItems) {
List<ILocation> neighbours = game.getVisible();
for (ILocation neighbour : neighbours) {
boolean hasItem = false;
for (IItem item : game.getMap().getAll(neighbour)) {
for (Class<?> validItem : validItems) {
if (validItem.isInstance(item)) {
hasItem = true;
}
}
}
if (hasItem) {
ILocation current = game.getLocation();
GridDirection dir = game.locationDirection(current, neighbour);
if (game.canGo(dir)) {
game.move(dir);
return true;
}
}
}
return false;
}
/**
* Picks up an item of one of the specified class types.
*
* @param game An IGame object
* @param validItems A list of classes the NPC should accept
* @return An item if it was successfully picked up. Null otherwise
*/
public static IItem pickUp(IGame game, List<Class<?>> validItems) {
for (IItem item : game.getLocalItems()) {
for (Class<?> validItem : validItems) {
if (validItem.isInstance(item)) {
return game.pickUp(item);
}
}
}
return null;
}
/**
* Makes an IActor go to the opposite direction of the closest enemy.
* Will return false if there are no visible enemies.
*
* @param game An IGame object
* @return True if the IActor fled. False otherwise
*/
public static boolean flee(IGame game) {
List<ILocation> neighbours = game.getVisible();
for (ILocation neighbour : neighbours) {
if (game.getMap().hasActors(neighbour)) {
ILocation current = game.getLocation();
GridDirection dir = reverseDir(game.locationDirection(current, neighbour));
if (game.canGo(dir)) {
game.move(dir);
return true;
}
}
}
return false;
}
/**
* Reverses the main four directions.
*
* @param dir A direction
* @return An opposite direction
*/
private static GridDirection reverseDir(GridDirection dir) {
if (dir == GridDirection.SOUTH) {
return GridDirection.NORTH;
} else if (dir == GridDirection.NORTH) {
return GridDirection.SOUTH;
} else if (dir == GridDirection.WEST) {
return GridDirection.EAST;
} else {
return GridDirection.WEST;
}
}
/**
* Plays a sound.
*
* @param filename The String path of the audio file to play
*/
public static void playSound(String filename) {
Media sound = new Media(new File(filename).toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(sound);
mediaPlayer.play();
}
}

View File

@ -0,0 +1,16 @@
package inf101.v18.rogue101.states;
import java.util.Random;
/**
* An enum to distinguish different enemies of the same type.
*/
public enum Age {
TODDLER, CHILD, TEEN, ADULT, ELDER;
private static final Random random = new Random();
public static Age getRandom() {
return values()[random.nextInt(values().length)];
}
}

View File

@ -0,0 +1,16 @@
package inf101.v18.rogue101.states;
import java.util.Random;
/**
* An enum to distinguish different enemies of the same type.
*/
public enum Occupation {
KNIGHT, MAGE, BOWSMAN;
private static final Random random = new Random();
public static Occupation getRandom() {
return values()[random.nextInt(values().length)];
}
}

View File

@ -0,0 +1,16 @@
package inf101.v18.rogue101.states;
import java.util.Random;
/**
* An enum to distinguish different enemies of the same type.
*/
public enum Personality {
AGRESSIVE, CALM, AFRAID;
private static final Random random = new Random();
public static Personality getRandom() {
return values()[random.nextInt(values().length)];
}
}

View File

@ -2,19 +2,111 @@ package inf101.v18.rogue101.tests;
import static org.junit.Assert.*;
import org.junit.jupiter.api.Test;
import inf101.v18.rogue101.objects.IItem;
import org.junit.Test;
import inf101.v18.grid.ILocation;
import inf101.v18.rogue101.map.GameMap;
class GameMapTest {
import java.util.List;
public class GameMapTest {
@Test
void testSortedAdd() {
public void testSortedAdd() {
GameMap gameMap = new GameMap(20, 20);
ILocation location = gameMap.getLocation(10, 10);
// TODO:
fail("Not yet implemented");
for (int i = 0; i < 30; i++) {
gameMap.addRandomItems(location);
}
List<IItem> items = gameMap.getAll(location);
for (int i = 0; i < items.size() - 1; i++) {
assertTrue(items.get(i).compareTo(items.get(i + 1)) >= 0);
}
}
@Test
public void testGetNeighbours() {
GameMap gameMap = new GameMap(20, 20);
ILocation location = gameMap.getLocation(10, 10);
List<ILocation> neighbours = gameMap.getNeighbourhood(location, 1);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 1);
}
assertEquals(8, neighbours.size());
neighbours = gameMap.getNeighbourhood(location, 2);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 2);
}
assertEquals(24, neighbours.size());
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(48, neighbours.size());
}
@Test
public void testNeighboursOutOfBounds() {
GameMap gameMap = new GameMap(20, 20);
ILocation location = gameMap.getLocation(0, 0);
List<ILocation> neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(15, neighbours.size());
location = gameMap.getLocation(19, 0);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(15, neighbours.size());
location = gameMap.getLocation(19, 19);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(15, neighbours.size());
location = gameMap.getLocation(0, 19);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(15, neighbours.size());
location = gameMap.getLocation(0, 10);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(27, neighbours.size());
location = gameMap.getLocation(19, 10);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(27, neighbours.size());
location = gameMap.getLocation(10, 0);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(27, neighbours.size());
location = gameMap.getLocation(10, 19);
neighbours = gameMap.getNeighbourhood(location, 3);
for (ILocation neighbour : neighbours) {
assertTrue(location.gridDistanceTo(neighbour) <= 3);
}
assertEquals(27, neighbours.size());
}
}

View File

@ -2,41 +2,97 @@ package inf101.v18.rogue101.tests;
import static org.junit.Assert.*;
import org.junit.jupiter.api.Test;
import org.junit.Test;
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.map.GameMap;
import inf101.v18.rogue101.objects.IItem;
import inf101.v18.rogue101.objects.IPlayer;
import javafx.scene.input.KeyCode;
class PlayerTest {
public static String TEST_MAP = "40 5\n" //
+ "########################################\n" //
+ "#...... ..C.R ......R.R......... ..R...#\n" //
+ "#.R@R...... ..........RC..R...... ... .#\n" //
+ "#... ..R........R......R. R........R.RR#\n" //
+ "########################################\n" //
;
public class PlayerTest {
@Test
void testPlayer1() {
// new game with our test map
Game game = new Game(TEST_MAP);
// pick (3,2) as the "current" position; this is where the player is on the
// test map, so it'll set up the player and return it
IPlayer player = (IPlayer) game.setCurrent(3, 2);
// find players location
public void testOutOfBounds() {
String NO_WALLS_MAP = "1 1\n"
+ "@\n";
Game game = new Game(NO_WALLS_MAP);
IPlayer player = (IPlayer) game.setCurrent(0, 0);
ILocation loc = game.getLocation();
// press "UP" key
player.keyPressed(game, KeyCode.UP);
// see that we moved north
assertEquals(loc.go(GridDirection.NORTH), game.getLocation());
player.keyPressed(game, KeyCode.LEFT);
assertEquals(loc, game.getLocation());
}
/*@Test
public void testActor() {
String RABBIT_MAP = "5 5\n"
+ "#####\n"
+ "#RRR#\n"
+ "#R@R#\n"
+ "#RRR#\n"
+ "#####\n";
Game game = new Game(RABBIT_MAP);
IPlayer player = (IPlayer) game.setCurrent(2, 2);
ILocation loc = game.getLocation();
player.keyPressed(game, KeyCode.LEFT);
assertEquals(loc, game.getLocation());
player.keyPressed(game, KeyCode.RIGHT);
assertEquals(loc, game.getLocation());
player.keyPressed(game, KeyCode.UP);
assertEquals(loc, game.getLocation());
player.keyPressed(game, KeyCode.DOWN);
assertEquals(loc, game.getLocation());
}*/ //TODO: Fix error when playing sound
@Test
public void testItem() {
String CARROT_MAP = "5 5\n"
+ "#####\n"
+ "#CCC#\n"
+ "#C@C#\n"
+ "#CCC#\n"
+ "#####\n";
Game game = new Game(CARROT_MAP);
IPlayer player = (IPlayer) game.setCurrent(2, 2);
ILocation loc = game.getLocation();
player.keyPressed(game, KeyCode.RIGHT);
assertEquals(loc.go(GridDirection.EAST), game.getLocation());
game.doTurn();
player.keyPressed(game, KeyCode.LEFT);
game.doTurn();
player.keyPressed(game, KeyCode.LEFT);
assertEquals(loc.go(GridDirection.WEST), game.getLocation());
}
@Test
public void testWallsAndKeys() { //This probably seems a little overkill, but we need to check all keys.
String EMPTY_MAP = "5 5\n"
+ "#####\n"
+ "# #\n"
+ "# @ #\n"
+ "# #\n"
+ "#####\n";
Game game = new Game(EMPTY_MAP);
IPlayer player = (IPlayer) game.setCurrent(2, 2);
ILocation loc = game.getLocation();
player.keyPressed(game, KeyCode.UP);
game.doTurn();
player.keyPressed(game, KeyCode.UP);
game.doTurn();
assertEquals(loc.go(GridDirection.NORTH), game.getLocation());
player.keyPressed(game, KeyCode.DOWN);
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();
assertEquals(loc.go(GridDirection.SOUTH), game.getLocation());
player.keyPressed(game, KeyCode.UP);
game.doTurn();
player.keyPressed(game, KeyCode.LEFT);
assertEquals(loc.go(GridDirection.WEST), game.getLocation());
}
}

View File

@ -82,8 +82,6 @@ public class DoubleGenerator extends AbstractGenerator<Double> {
public Double generate(Random rng) {
double d = rng.nextDouble();
double r = minValue + (d * diff);
return r;
return minValue + (d * diff);
}
}

View File

@ -73,8 +73,6 @@ public class GridGenerator<T> extends AbstractGenerator<IGrid<T>> {
int w = widthGenerator.generate(r);
int h = heightGenerator.generate(r);
IGrid<T> grid = new MyGrid<>(w, h, (l) -> elementGenerator.generate(r));
return grid;
return new MyGrid<>(w, h, (l) -> elementGenerator.generate(r));
}
}