Merge branch 'master' of https://retting.ii.uib.no/kkn015/inf101.v18.sem1 into kkn015
This commit is contained in:
@ -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;
|
||||
|
BIN
src/inf101/v18/gfx/fonts/Symbola.odt
Normal file
BIN
src/inf101/v18/gfx/fonts/Symbola.odt
Normal file
Binary file not shown.
BIN
src/inf101/v18/gfx/fonts/Symbola.pdf
Normal file
BIN
src/inf101/v18/gfx/fonts/Symbola.pdf
Normal file
Binary file not shown.
BIN
src/inf101/v18/gfx/fonts/Symbola.ttf
Normal file
BIN
src/inf101/v18/gfx/fonts/Symbola.ttf
Normal file
Binary file not shown.
BIN
src/inf101/v18/gfx/fonts/hintedSymbola.ttf
Normal file
BIN
src/inf101/v18/gfx/fonts/hintedSymbola.ttf
Normal file
Binary file not shown.
@ -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);
|
||||
|
@ -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)) : " ");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
78
src/inf101/v18/rogue101/enemies/Boss.java
Normal file
78
src/inf101/v18/rogue101/enemies/Boss.java
Normal 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;
|
||||
}
|
||||
}
|
260
src/inf101/v18/rogue101/enemies/Girl.java
Normal file
260
src/inf101/v18/rogue101/enemies/Girl.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
123
src/inf101/v18/rogue101/items/Backpack.java
Normal file
123
src/inf101/v18/rogue101/items/Backpack.java
Normal 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;
|
||||
}
|
||||
}
|
52
src/inf101/v18/rogue101/items/Bow.java
Normal file
52
src/inf101/v18/rogue101/items/Bow.java
Normal 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;
|
||||
}
|
||||
}
|
73
src/inf101/v18/rogue101/items/Chest.java
Normal file
73
src/inf101/v18/rogue101/items/Chest.java
Normal 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;
|
||||
}
|
||||
}
|
33
src/inf101/v18/rogue101/items/IBuffItem.java
Normal file
33
src/inf101/v18/rogue101/items/IBuffItem.java
Normal 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();
|
||||
}
|
9
src/inf101/v18/rogue101/items/IConsumable.java
Normal file
9
src/inf101/v18/rogue101/items/IConsumable.java
Normal 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();
|
||||
}
|
30
src/inf101/v18/rogue101/items/IContainer.java
Normal file
30
src/inf101/v18/rogue101/items/IContainer.java
Normal 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();
|
||||
}
|
12
src/inf101/v18/rogue101/items/IMagicWeapon.java
Normal file
12
src/inf101/v18/rogue101/items/IMagicWeapon.java
Normal 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";
|
||||
}
|
||||
}
|
12
src/inf101/v18/rogue101/items/IMeleeWeapon.java
Normal file
12
src/inf101/v18/rogue101/items/IMeleeWeapon.java
Normal 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";
|
||||
}
|
||||
}
|
12
src/inf101/v18/rogue101/items/IRangedWeapon.java
Normal file
12
src/inf101/v18/rogue101/items/IRangedWeapon.java
Normal 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";
|
||||
}
|
||||
}
|
22
src/inf101/v18/rogue101/items/IStatic.java
Normal file
22
src/inf101/v18/rogue101/items/IStatic.java
Normal 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();
|
||||
}
|
19
src/inf101/v18/rogue101/items/IWeapon.java
Normal file
19
src/inf101/v18/rogue101/items/IWeapon.java
Normal 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();
|
||||
}
|
65
src/inf101/v18/rogue101/items/Manga.java
Normal file
65
src/inf101/v18/rogue101/items/Manga.java
Normal 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;
|
||||
}
|
||||
}
|
63
src/inf101/v18/rogue101/items/Shield.java
Normal file
63
src/inf101/v18/rogue101/items/Shield.java
Normal 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;
|
||||
}
|
||||
}
|
53
src/inf101/v18/rogue101/items/Staff.java
Normal file
53
src/inf101/v18/rogue101/items/Staff.java
Normal 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;
|
||||
}
|
||||
}
|
53
src/inf101/v18/rogue101/items/Sword.java
Normal file
53
src/inf101/v18/rogue101/items/Sword.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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#
|
||||
#........###############################
|
||||
|
21
src/inf101/v18/rogue101/map/maps/testmap.txt
Normal file
21
src/inf101/v18/rogue101/map/maps/testmap.txt
Normal file
@ -0,0 +1,21 @@
|
||||
40 20
|
||||
########################################
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# G G #
|
||||
# #
|
||||
############### # #
|
||||
# # #
|
||||
# S # #
|
||||
# @ ######## #########
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# G #
|
||||
# G #
|
||||
# c #
|
||||
# ####
|
||||
# # B#
|
||||
# G #
|
||||
########################################
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
351
src/inf101/v18/rogue101/objects/Player.java
Normal file
351
src/inf101/v18/rogue101/objects/Player.java
Normal 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;
|
||||
}
|
||||
}
|
171
src/inf101/v18/rogue101/shared/NPC.java
Normal file
171
src/inf101/v18/rogue101/shared/NPC.java
Normal 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();
|
||||
}
|
||||
}
|
16
src/inf101/v18/rogue101/states/Age.java
Normal file
16
src/inf101/v18/rogue101/states/Age.java
Normal 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)];
|
||||
}
|
||||
}
|
16
src/inf101/v18/rogue101/states/Occupation.java
Normal file
16
src/inf101/v18/rogue101/states/Occupation.java
Normal 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)];
|
||||
}
|
||||
}
|
16
src/inf101/v18/rogue101/states/Personality.java
Normal file
16
src/inf101/v18/rogue101/states/Personality.java
Normal 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)];
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user