Small fixes and improvements
A boss will drop all its items A girl will choose a name from a list (not finalized) Carrots will no longer spawn randomly Lets a message overflow to the next line Added a new method for dropping an item at any location (for Boss.java) Improves backpacks Improves symbols for existing items Added an interact-message to all containers, and removed it from IStatic Makes it possible to get any of the 1-10 items in a chest Makes an item dropped on the same tile as a static container enter the container Code improvements Game end screen
This commit is contained in:
parent
bec5f90efd
commit
27e4259f00
@ -1,17 +1,26 @@
|
||||
package inf101.v18.rogue101.enemies;
|
||||
|
||||
import inf101.v18.grid.ILocation;
|
||||
import inf101.v18.rogue101.game.IGame;
|
||||
import inf101.v18.rogue101.items.Backpack;
|
||||
import inf101.v18.rogue101.items.Sword;
|
||||
import inf101.v18.rogue101.objects.IItem;
|
||||
import inf101.v18.rogue101.objects.INonPlayer;
|
||||
import inf101.v18.rogue101.shared.NPC;
|
||||
|
||||
public class Boss implements INonPlayer {
|
||||
private int hp = getMaxHealth();
|
||||
Backpack backpack = new Backpack();
|
||||
private Backpack backpack = new Backpack();
|
||||
private ILocation loc;
|
||||
|
||||
public Boss() {
|
||||
backpack.add(new Sword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doTurn(IGame game) {
|
||||
|
||||
loc = game.getLocation();
|
||||
NPC.tryAttack(game, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,7 +70,7 @@ public class Boss implements INonPlayer {
|
||||
|
||||
@Override
|
||||
public String getPrintSymbol() {
|
||||
return "\uD83D\uDE08";
|
||||
return "\u001b[91m" + "\uD83D\uDE08" + "\u001b[0m";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,10 +78,25 @@ public class Boss implements INonPlayer {
|
||||
return "B";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVision() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int handleDamage(IGame game, IItem source, int amount) {
|
||||
//TODO: Drop item on death.
|
||||
hp -= amount;
|
||||
if (hp < 0 && backpack.size() > 0) {
|
||||
boolean dropped = false;
|
||||
for (IItem item : backpack.getContent()) {
|
||||
if (game.dropAt(loc, item)) {
|
||||
dropped = true;
|
||||
}
|
||||
}
|
||||
if (dropped) {
|
||||
game.displayMessage(getName() + " dropped something");
|
||||
}
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public class Girl implements INonPlayer {
|
||||
private Backpack backpack = new Backpack();
|
||||
private static final Random random = new Random();
|
||||
private List<Class<?>> validItems;
|
||||
private static final String[] namelist = {"Milk", "Salad"};
|
||||
|
||||
public Girl() {
|
||||
setStats();
|
||||
@ -100,8 +101,7 @@ public class Girl implements INonPlayer {
|
||||
}
|
||||
|
||||
private String randomName() {
|
||||
//TODO: Choose from a list of names, or generate name.
|
||||
return "Girl";
|
||||
return namelist[random.nextInt(namelist.length)] + "-chan";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,16 +141,16 @@ public class Girl implements INonPlayer {
|
||||
private boolean attack(IGame game) {
|
||||
switch (occupation) {
|
||||
case KNIGHT:
|
||||
if (NPC.tryAttack(game)) {
|
||||
if (NPC.tryAttack(game, 1)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MAGE:
|
||||
if (NPC.tryAttackRanged(game, 2)) {
|
||||
if (NPC.tryAttack(game, 2)) {
|
||||
return true;
|
||||
}
|
||||
case BOWSMAN:
|
||||
if (NPC.tryAttackRanged(game, 4)) {
|
||||
if (NPC.tryAttack(game, 4)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -183,15 +183,6 @@ public class Girl implements INonPlayer {
|
||||
return attack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a girl's occupation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Occupation getOccupation() {
|
||||
return occupation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttack() {
|
||||
return attack;
|
||||
|
@ -30,7 +30,7 @@ public class Rabbit implements INonPlayer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NPC.tryAttack(game)) {
|
||||
if (NPC.tryAttack(game, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ 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"};
|
||||
String[] info = {"Controls:", "WASD or arrow keys for movement", "E to pick up an item", "Q to drop an item", "1-0 to choose an item (10=0)", "N to change name", "ENTER to confirm", "R to use a ranged attack", "F to use a magic attack"};
|
||||
for (int i = 0; i < info.length; i++) {
|
||||
this.printer.printAt(map.getWidth() + 2, 1 + i, info[i]);
|
||||
}
|
||||
@ -181,6 +181,7 @@ public class Game implements IGame {
|
||||
if (!map.has(loc, target)) {
|
||||
throw new IllegalMoveException("Target isn't there!");
|
||||
}
|
||||
//TODO: Detect the weapon used
|
||||
IWeapon weapon = (IWeapon) currentActor.getItem(IWeapon.class);
|
||||
if (weapon != null) {
|
||||
NPC.playSound(weapon.getSound());
|
||||
@ -189,7 +190,9 @@ public class Game implements IGame {
|
||||
}
|
||||
if (getAttack() >= getDefence(target)) {
|
||||
int actualDamage = target.handleDamage(this, target, getDamage(target));
|
||||
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
|
||||
if (currentActor != null) {
|
||||
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());
|
||||
}
|
||||
@ -243,18 +246,21 @@ public class Game implements IGame {
|
||||
beginTurn();
|
||||
}
|
||||
|
||||
if (random.nextInt(100) < 20) {
|
||||
/*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());
|
||||
}
|
||||
}
|
||||
}*/ //We don't want this in the actual game.
|
||||
|
||||
// process actors one by one; for the IPlayer, we return and wait for keypresses
|
||||
// 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)
|
||||
if (numPlayers == 0) {
|
||||
kill();
|
||||
}
|
||||
while (!actors.isEmpty()) {
|
||||
// get the next player or non-player in the queue
|
||||
currentActor = actors.remove(0);
|
||||
@ -275,11 +281,11 @@ public class Game implements IGame {
|
||||
} else if (currentActor instanceof IPlayer) {
|
||||
if (currentActor.isDestroyed()) {
|
||||
// a dead human player gets removed from the game
|
||||
// TODO: you might want to be more clever here
|
||||
displayMessage("YOU DIE!!!");
|
||||
//This never actually triggers, because of map.clean();
|
||||
/*displayMessage("YOU DIE!!!");
|
||||
map.remove(currentLocation, currentActor);
|
||||
currentActor = null;
|
||||
currentLocation = null;
|
||||
currentLocation = null;*/
|
||||
} else {
|
||||
// For the human player, we need to wait for input, so we just return.
|
||||
// Further keypresses will cause keyPressed() to be called, and once the human
|
||||
@ -300,6 +306,37 @@ public class Game implements IGame {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is dead. It needs to be properly killed off.
|
||||
*/
|
||||
public void kill() {
|
||||
map.remove(currentLocation, currentActor);
|
||||
currentActor = null;
|
||||
currentLocation = null;
|
||||
actors = new ArrayList<>();
|
||||
loadMap("gameover.txt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a map with the desired name
|
||||
*
|
||||
* @param mapName Name of map, including extension.
|
||||
*/
|
||||
private void loadMap(String mapName) {
|
||||
IGrid<String> inputGrid = MapReader.readFile("maps/" + mapName);
|
||||
if (inputGrid == null) {
|
||||
System.err.println("Map not found – falling back to builtin map");
|
||||
inputGrid = MapReader.readString(Main.BUILTIN_MAP);
|
||||
}
|
||||
this.map = new GameMap(inputGrid.getArea());
|
||||
for (ILocation loc : inputGrid.locations()) {
|
||||
IItem item = createItem(inputGrid.get(loc));
|
||||
if (item != null) {
|
||||
map.add(loc, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the map and collect all the actors.
|
||||
*/
|
||||
@ -357,6 +394,8 @@ public class Game implements IGame {
|
||||
itemFactories.put("S", Sword::new);
|
||||
itemFactories.put("c", Chest::new);
|
||||
itemFactories.put("B", Boss::new);
|
||||
itemFactories.put("s", Staff::new);
|
||||
itemFactories.put("b", Bow::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -392,13 +431,37 @@ public class Game implements IGame {
|
||||
printer.clearLine(Main.LINE_MSG1);
|
||||
printer.clearLine(Main.LINE_MSG2);
|
||||
printer.clearLine(Main.LINE_MSG3);
|
||||
|
||||
int maxLen = 80; //The maximum length of a message to not overflow.
|
||||
boolean secondLineWritten = false;
|
||||
boolean thirdLineWritten = false;
|
||||
|
||||
if (lastMessages.size() > 0) {
|
||||
printer.printAt(1, Main.LINE_MSG1, lastMessages.get(0));
|
||||
String message = lastMessages.get(0);
|
||||
if (message.length() > 2 * maxLen) {
|
||||
printer.printAt(1, Main.LINE_MSG1, message.substring(0, maxLen));
|
||||
printer.printAt(1, Main.LINE_MSG2, message.substring(maxLen, 2 * maxLen));
|
||||
printer.printAt(1, Main.LINE_MSG3, message.substring(2 * maxLen));
|
||||
secondLineWritten = thirdLineWritten = true;
|
||||
} else if (message.length() > maxLen) {
|
||||
printer.printAt(1, Main.LINE_MSG1, message.substring(0, maxLen));
|
||||
printer.printAt(1, Main.LINE_MSG2, message.substring(maxLen));
|
||||
secondLineWritten = true;
|
||||
} else {
|
||||
printer.printAt(1, Main.LINE_MSG1, message);
|
||||
}
|
||||
}
|
||||
if (lastMessages.size() > 1) {
|
||||
printer.printAt(1, Main.LINE_MSG2, lastMessages.get(1));
|
||||
if (lastMessages.size() > 1 && !secondLineWritten) {
|
||||
String message = lastMessages.get(1);
|
||||
if (message.length() > maxLen) {
|
||||
printer.printAt(1, Main.LINE_MSG2, message.substring(0, maxLen));
|
||||
printer.printAt(1, Main.LINE_MSG3, message.substring(maxLen));
|
||||
thirdLineWritten = true;
|
||||
} else {
|
||||
printer.printAt(1, Main.LINE_MSG2, message);
|
||||
}
|
||||
}
|
||||
if (lastMessages.size() > 2) {
|
||||
if (lastMessages.size() > 2 && !thirdLineWritten) {
|
||||
printer.printAt(1, Main.LINE_MSG3, lastMessages.get(2));
|
||||
}
|
||||
System.out.println("Message: «" + s + "»");
|
||||
@ -412,9 +475,11 @@ public class Game implements IGame {
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
//map.draw(painter, printer);
|
||||
GameMap aMap = (GameMap) map;
|
||||
aMap.drawVisible(painter, printer, this);
|
||||
if (numPlayers == 0) {
|
||||
map.draw(painter, printer);
|
||||
} else {
|
||||
((GameMap) map).drawVisible(painter, printer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -426,6 +491,15 @@ public class Game implements IGame {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dropAt(ILocation loc, IItem item) {
|
||||
if (item != null) {
|
||||
map.add(loc, item);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void formatDebug(String s, Object... args) {
|
||||
displayDebug(String.format(s, args));
|
||||
@ -624,43 +698,51 @@ public class Game implements IGame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridDirection locationDirection(ILocation start, ILocation target) {
|
||||
public List<GridDirection> locationDirection(ILocation start, ILocation target) {
|
||||
int targetX = target.getX(), targetY = target.getY();
|
||||
int startX = start.getX(), startY = start.getY();
|
||||
GridDirection dir = GridDirection.CENTER;
|
||||
List<GridDirection> dirs = new ArrayList<>();
|
||||
if (targetX > startX && targetY > startY) {
|
||||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||||
dir = GridDirection.SOUTH;
|
||||
dirs.add(GridDirection.SOUTH);
|
||||
dirs.add(GridDirection.EAST);
|
||||
} else {
|
||||
dir = GridDirection.EAST;
|
||||
dirs.add(GridDirection.EAST);
|
||||
dirs.add(GridDirection.SOUTH);
|
||||
}
|
||||
} else if (targetX > startX && targetY < startY) {
|
||||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||||
dir = GridDirection.NORTH;
|
||||
dirs.add(GridDirection.NORTH);
|
||||
dirs.add(GridDirection.EAST);
|
||||
} else {
|
||||
dir = GridDirection.EAST;
|
||||
dirs.add(GridDirection.EAST);
|
||||
dirs.add(GridDirection.NORTH);
|
||||
}
|
||||
} else if (targetX < startX && targetY > startY) {
|
||||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||||
dir = GridDirection.SOUTH;
|
||||
dirs.add(GridDirection.SOUTH);
|
||||
dirs.add(GridDirection.WEST);
|
||||
} else {
|
||||
dir = GridDirection.WEST;
|
||||
dirs.add(GridDirection.WEST);
|
||||
dirs.add(GridDirection.SOUTH);
|
||||
}
|
||||
} else if (targetX < startX && targetY < startY) {
|
||||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||||
dir = GridDirection.NORTH;
|
||||
dirs.add(GridDirection.NORTH);
|
||||
dirs.add(GridDirection.WEST);
|
||||
} else {
|
||||
dir = GridDirection.WEST;
|
||||
dirs.add(GridDirection.WEST);
|
||||
dirs.add(GridDirection.NORTH);
|
||||
}
|
||||
} else if (targetX > startX) {
|
||||
dir = GridDirection.EAST;
|
||||
dirs.add(GridDirection.EAST);
|
||||
} else if (targetX < startX) {
|
||||
dir = GridDirection.WEST;
|
||||
dirs.add(GridDirection.WEST);
|
||||
} else if (targetY > startY) {
|
||||
dir = GridDirection.SOUTH;
|
||||
dirs.add(GridDirection.SOUTH);
|
||||
} else if (targetY < startY) {
|
||||
dir = GridDirection.NORTH;
|
||||
dirs.add(GridDirection.NORTH);
|
||||
}
|
||||
return dir;
|
||||
return dirs;
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +165,15 @@ public interface IGame {
|
||||
*/
|
||||
boolean drop(IItem item);
|
||||
|
||||
/**
|
||||
* Does the same as drop, but for a specified location.
|
||||
*
|
||||
* @param loc The location to drop the location
|
||||
* @param item The item to drop
|
||||
* @return True if the item was dropped. False otherwise
|
||||
*/
|
||||
boolean dropAt(ILocation loc, IItem item);
|
||||
|
||||
/**
|
||||
* Clear the unused graphics area (you can fill it with whatever you want!)
|
||||
*/
|
||||
@ -319,12 +328,12 @@ public interface IGame {
|
||||
Random getRandom();
|
||||
|
||||
/**
|
||||
* Gets the best direction to go from current to neighbour.
|
||||
* Gets a list of the best directions 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);
|
||||
List<GridDirection> locationDirection(ILocation current, ILocation neighbour);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import inf101.v18.rogue101.game.IGame;
|
||||
import inf101.v18.rogue101.objects.IItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Backpack implements IContainer {
|
||||
@ -14,7 +15,7 @@ public class Backpack implements IContainer {
|
||||
/**
|
||||
* The maximum amount of items allowed in a single backpack.
|
||||
*/
|
||||
private final int MAX_SIZE = 50;
|
||||
private final int MAX_SIZE = 5;
|
||||
|
||||
@Override
|
||||
public int getCurrentHealth() {
|
||||
@ -54,14 +55,10 @@ public class Backpack implements IContainer {
|
||||
/**
|
||||
* Retrieves the current size of the Backpack.
|
||||
*
|
||||
* @return
|
||||
* @return The size
|
||||
*/
|
||||
public int size() {
|
||||
int totalSize = 0;
|
||||
for (IItem item : content) {
|
||||
totalSize += item.getSize();
|
||||
}
|
||||
return totalSize;
|
||||
return content.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +74,7 @@ public class Backpack implements IContainer {
|
||||
* Tries to add an item to the Backpack
|
||||
*
|
||||
* @param item The item to add
|
||||
* @return
|
||||
* @return True if the item was added. False if the backpack is full
|
||||
*/
|
||||
public boolean add(IItem item) {
|
||||
if (size() < MAX_SIZE) {
|
||||
@ -107,13 +104,23 @@ public class Backpack implements IContainer {
|
||||
return content.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addItem(IItem item) {
|
||||
if (content.size() < MAX_SIZE) {
|
||||
content.add(item);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content List for direct manipulation.
|
||||
*
|
||||
* @return A list of T
|
||||
*/
|
||||
public List<IItem> getContent() {
|
||||
return content;
|
||||
return Collections.unmodifiableList(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,9 +40,14 @@ public class Bow implements IRangedWeapon {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrintSymbol() {
|
||||
return "\uD83C\uDFF9";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "B";
|
||||
return "b";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,32 +4,48 @@ import inf101.v18.rogue101.game.IGame;
|
||||
import inf101.v18.rogue101.objects.IItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Chest implements IContainer, IStatic {
|
||||
private List<IItem> container;
|
||||
private int MAX_SIZE = 10;
|
||||
|
||||
public Chest() {
|
||||
this.container = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Chest(int lvl) {
|
||||
this.container = new ArrayList<>();
|
||||
fill(lvl);
|
||||
}
|
||||
|
||||
public Chest(List<IItem> items) {
|
||||
this.container = items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly fills chest with random items based on dungeon level.
|
||||
*
|
||||
* @param lvl The current dungeon level
|
||||
*/
|
||||
private void fill (int lvl) {
|
||||
//TODO: Implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public IItem get(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List getContent() {
|
||||
return container;
|
||||
public List<IItem> getContent() {
|
||||
return Collections.unmodifiableList(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFull() {
|
||||
return false;
|
||||
return container.size() >= MAX_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -47,18 +63,13 @@ public class Chest implements IContainer, IStatic {
|
||||
return "Chest";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInteractMessage() {
|
||||
return "Items in " + getName() + ": ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
public String getPrintSymbol() {
|
||||
return "\uD83D\uDDC3";
|
||||
return "\u001b[94m" + "\uD83D\uDDC3" + "\u001b[0m";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,4 +81,14 @@ public class Chest implements IContainer, IStatic {
|
||||
public int handleDamage(IGame game, IItem source, int amount) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addItem(IItem item) {
|
||||
if (container.size() < MAX_SIZE) {
|
||||
container.add(item);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,14 @@ public interface IContainer extends IItem {
|
||||
*/
|
||||
IItem get(int i);
|
||||
|
||||
/**
|
||||
* Adds an item to a container.
|
||||
*
|
||||
* @praram item The item to add to the container
|
||||
* @return True if the container was not full
|
||||
*/
|
||||
boolean addItem(IItem item);
|
||||
|
||||
/**
|
||||
* Gets a list with everything inside a container.
|
||||
*
|
||||
@ -27,4 +35,13 @@ public interface IContainer extends IItem {
|
||||
* @return True if it has no space left
|
||||
*/
|
||||
boolean isFull();
|
||||
|
||||
/**
|
||||
* Returns the message to show the user upon interacting with the container.
|
||||
*
|
||||
* @return A message
|
||||
*/
|
||||
default String getInteractMessage() {
|
||||
return "Items in " + getName() + ": ";
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,4 @@ public interface IStatic extends IItem {
|
||||
default int getSize() {
|
||||
return 10000;
|
||||
}
|
||||
|
||||
/**
|
||||
* A message to display when an interaction with the player happens.
|
||||
*
|
||||
* @return A message
|
||||
*/
|
||||
String getInteractMessage();
|
||||
}
|
||||
|
@ -40,6 +40,11 @@ public class Staff implements IMagicWeapon {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrintSymbol() {
|
||||
return "⚚";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "s";
|
||||
|
@ -40,6 +40,11 @@ public class Sword implements IMeleeWeapon {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrintSymbol() {
|
||||
return "⚔";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "S";
|
||||
|
@ -119,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);
|
||||
@ -132,7 +132,7 @@ public class GameMap implements IGameMap {
|
||||
if (!dontPrint) {
|
||||
sym = list.get(0).getPrintSymbol();
|
||||
}
|
||||
}
|
||||
}
|
||||
printer.printAt(loc.getX() + 1, loc.getY() + 1, sym);
|
||||
}
|
||||
} finally {
|
||||
@ -144,13 +144,13 @@ public class GameMap implements IGameMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws only the tiles visible to the player.
|
||||
* Writes a black void unto the whole map.
|
||||
* Properly draws only the tiles visible to the player.
|
||||
*
|
||||
* @param painter
|
||||
* @param printer
|
||||
* @param game
|
||||
* @param painter A painter
|
||||
* @param printer A printer
|
||||
*/
|
||||
public void drawVisible(ITurtle painter, Printer printer, IGame game) {
|
||||
public void drawVisible(ITurtle painter, Printer printer) {
|
||||
Iterable<ILocation> cells;
|
||||
if (Main.MAP_DRAW_ONLY_DIRTY_CELLS) {
|
||||
if (dirtyLocs.isEmpty())
|
||||
@ -176,11 +176,15 @@ public class GameMap implements IGameMap {
|
||||
ILocation playerPos = null;
|
||||
for (ILocation loc : cells) {
|
||||
printer.printAt(loc.getX() + 1, loc.getY() + 1, " ");
|
||||
//We need to get the player and its location from somewhere.
|
||||
if (this.hasActors(loc) && this.getActors(loc).get(0) instanceof IPlayer) {
|
||||
player = (IPlayer) this.getActors(loc).get(0);
|
||||
playerPos = loc;
|
||||
}
|
||||
}
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
List<ILocation> positions = getVisible(getNeighbourhood(playerPos,player.getVision()), playerPos);
|
||||
positions.add(playerPos);
|
||||
for (ILocation loc : positions) {
|
||||
@ -351,24 +355,20 @@ public class GameMap implements IGameMap {
|
||||
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));
|
||||
for (int x = startX - i + 1; x < startX + i; x++) { //Top and bottom
|
||||
if (grid.isValid(x, startY - i)) {
|
||||
neighbours.add(getLocation(x, startY - i));
|
||||
}
|
||||
if (grid.isValid(x, bottomY)) {
|
||||
neighbours.add(getLocation(x, bottomY));
|
||||
if (grid.isValid(x, startY + i)) {
|
||||
neighbours.add(getLocation(x, startY + i));
|
||||
}
|
||||
}
|
||||
for (int y = topY; y <= bottomY; y++) {
|
||||
if (grid.isValid(leftX, y)) {
|
||||
neighbours.add(getLocation(leftX, y));
|
||||
for (int y = startY - i; y <= startY + i; y++) { //Sides
|
||||
if (grid.isValid(startX - i, y)) {
|
||||
neighbours.add(getLocation(startX - i, y));
|
||||
}
|
||||
if (grid.isValid(rightX, y)) {
|
||||
neighbours.add(getLocation(rightX, y));
|
||||
if (grid.isValid(startX + i, y)) {
|
||||
neighbours.add(getLocation(startX + i, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
src/inf101/v18/rogue101/map/maps/gameover.txt
Normal file
21
src/inf101/v18/rogue101/map/maps/gameover.txt
Normal file
@ -0,0 +1,21 @@
|
||||
40 20
|
||||
########################################
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #### #### ## ## #### #
|
||||
# # # # # # # # # #
|
||||
# # ## #### # # # #### #
|
||||
# # # # # # # # #
|
||||
# #### # # # # #### #
|
||||
# #
|
||||
# #
|
||||
# #### # # #### #### #
|
||||
# # # # # # # # #
|
||||
# # # # # #### #### #
|
||||
# # # # # # # # #
|
||||
# #### ## #### # # #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
########################################
|
@ -7,8 +7,8 @@
|
||||
# #
|
||||
############### # #
|
||||
# # #
|
||||
# S # #
|
||||
# @ ######## #########
|
||||
# S s # #
|
||||
# b @ ######## #########
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
|
@ -2,6 +2,7 @@ package inf101.v18.rogue101.objects;
|
||||
|
||||
import inf101.v18.grid.GridDirection;
|
||||
import inf101.v18.grid.ILocation;
|
||||
import inf101.v18.rogue101.game.Game;
|
||||
import inf101.v18.rogue101.game.IGame;
|
||||
import inf101.v18.rogue101.items.*;
|
||||
import inf101.v18.rogue101.shared.NPC;
|
||||
@ -47,7 +48,10 @@ public class Player implements IPlayer {
|
||||
} 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 (keyValue <= 9 && keyValue >= 0) {
|
||||
if (keyValue == 0) {
|
||||
keyValue = 10;
|
||||
}
|
||||
if (dropping) {
|
||||
drop(game, keyValue - 1);
|
||||
dropping = false;
|
||||
@ -70,14 +74,20 @@ public class Player implements IPlayer {
|
||||
if (item == null) {
|
||||
game.displayMessage("You do not have a ranged weapon.");
|
||||
} else {
|
||||
turnConsumed = NPC.tryAttackRanged(game, 3);
|
||||
turnConsumed = rangedAttack(game, 3);
|
||||
if (!turnConsumed) {
|
||||
game.displayMessage("No enemies within range.");
|
||||
}
|
||||
}
|
||||
} else if (key == KeyCode.F) {
|
||||
IItem item = getItem(IMagicWeapon.class);
|
||||
if (item == null) {
|
||||
game.displayMessage("You do not have a magic weapon.");
|
||||
} else {
|
||||
turnConsumed = NPC.tryAttackRanged(game, 2);
|
||||
turnConsumed = rangedAttack(game, 2);
|
||||
if (!turnConsumed) {
|
||||
game.displayMessage("No enemies within range.");
|
||||
}
|
||||
}
|
||||
}
|
||||
showStatus(game);
|
||||
@ -136,17 +146,20 @@ public class Player implements IPlayer {
|
||||
}
|
||||
|
||||
private void openChest(IGame game, List<IItem> items) {
|
||||
IStatic item = (IStatic)items.get(0);
|
||||
StringBuilder msg = new StringBuilder(item.getInteractMessage());
|
||||
IContainer container = (IContainer) item;
|
||||
IContainer container = (IContainer) items.get(0);
|
||||
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());
|
||||
game.displayMessage(container.getInteractMessage() + niceList(items, 10));
|
||||
exploringChest = true;
|
||||
}
|
||||
|
||||
private String niceList(List<IItem> list, int max) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
for (int i = 0; i < Math.min(list.size(), max); i++) {
|
||||
msg.append(" [").append(i + 1).append("] ").append(firstCharToUpper(list.get(i).getName()));
|
||||
}
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialized the dropping of an item, and does what is needed.
|
||||
*
|
||||
@ -154,10 +167,10 @@ public class Player implements IPlayer {
|
||||
* @return True if a turn was consumed. False otherwise
|
||||
*/
|
||||
private boolean dropInit(IGame game) {
|
||||
if (equipped.getContent().size() == 1) {
|
||||
if (equipped.size() == 1) {
|
||||
drop(game, 0);
|
||||
return true;
|
||||
} else if (equipped.getContent().size() < 1) {
|
||||
} else if (equipped.size() < 1) {
|
||||
game.displayMessage("You have nothing to drop");
|
||||
} else {
|
||||
StringBuilder msg = new StringBuilder("Items to drop:");
|
||||
@ -200,10 +213,8 @@ public class Player implements IPlayer {
|
||||
*/
|
||||
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;
|
||||
IContainer container = getStaticContainer(game);
|
||||
if (container != null && i < container.getContent().size()) {
|
||||
IItem loot = container.getContent().get(i);
|
||||
equipped.add(loot);
|
||||
container.getContent().remove(i);
|
||||
@ -216,6 +227,19 @@ public class Player implements IPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
private IContainer getStaticContainer(IGame game) {
|
||||
List<IItem> items = game.getLocalItems();
|
||||
if (items.size() < 1) {
|
||||
return null;
|
||||
}
|
||||
IItem item = items.get(0);
|
||||
if (item instanceof IStatic && item instanceof IContainer) {
|
||||
return (IContainer) item;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an item at index i.
|
||||
*
|
||||
@ -224,6 +248,17 @@ public class Player implements IPlayer {
|
||||
*/
|
||||
private void drop(IGame game, int i) {
|
||||
if (!equipped.isEmpty() && equipped.size() > i) {
|
||||
IContainer container = getStaticContainer(game);
|
||||
if (container != null) {
|
||||
if (container.addItem(equipped.get(i))) {
|
||||
equipped.remove(i);
|
||||
game.displayMessage("Item stored in container.");
|
||||
return;
|
||||
} else {
|
||||
game.displayMessage(container.getName() + " is full.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (game.drop(equipped.get(i))) {
|
||||
equipped.remove(i);
|
||||
game.displayMessage("Item dropped.");
|
||||
@ -348,4 +383,22 @@ public class Player implements IPlayer {
|
||||
//TODO: Increase vision based on equipped items
|
||||
return 3;
|
||||
}
|
||||
|
||||
private boolean rangedAttack(IGame game, int range) {
|
||||
List<ILocation> neighbours = game.getVisible();
|
||||
for (ILocation neighbour : neighbours) {
|
||||
if (game.getMap().hasActors(neighbour)) {
|
||||
ILocation current = game.getLocation();
|
||||
List<GridDirection> dirs = game.locationDirection(current, neighbour);
|
||||
IActor actor = game.getMap().getActors(neighbour).get(0); //We assume there is only one actor.
|
||||
if (actor instanceof INonPlayer && current.gridDistanceTo(neighbour) <= range) {
|
||||
for (GridDirection dir : dirs) {
|
||||
game.rangedAttack(dir, actor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@ 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.map.IGameMap;
|
||||
import inf101.v18.rogue101.map.IMapView;
|
||||
import inf101.v18.rogue101.objects.IActor;
|
||||
import inf101.v18.rogue101.objects.IItem;
|
||||
import inf101.v18.rogue101.states.Occupation;
|
||||
import inf101.v18.rogue101.objects.INonPlayer;
|
||||
import inf101.v18.rogue101.objects.IPlayer;
|
||||
import javafx.scene.media.Media;
|
||||
import javafx.scene.media.MediaPlayer;
|
||||
|
||||
@ -18,52 +20,40 @@ import java.util.List;
|
||||
*/
|
||||
public class NPC {
|
||||
|
||||
/**
|
||||
* Makes a NPC attack anything it can, or move closer to any enemy in its line of sight.
|
||||
*
|
||||
* @param game An IGame object
|
||||
* @return True if the NPC made a move. False otherwise
|
||||
*/
|
||||
public static boolean tryAttack(IGame game) {
|
||||
List<ILocation> neighbours = game.getVisible();
|
||||
for (ILocation neighbour : neighbours) {
|
||||
if (game.getMap().hasActors(neighbour)) {
|
||||
ILocation current = game.getLocation();
|
||||
GridDirection dir = game.locationDirection(current, neighbour);
|
||||
IActor actor = game.getMap().getActors(neighbour).get(0); //We assume there is only one actor.
|
||||
if (current.gridDistanceTo(neighbour) <= 1 && current.canGo(dir) && game.getMap().has(current.go(dir), actor) && !actor.isDestroyed()) {
|
||||
game.attack(dir, actor);
|
||||
return true;
|
||||
} else if (game.canGo(dir)) {
|
||||
game.move(dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private NPC() {}
|
||||
|
||||
/**
|
||||
* Performs a ranged attack on the closest actor (if any) observable by the current actor.
|
||||
* Performs a ranged attack on the closest actor (if any) observable by the current actor if range is > 1.
|
||||
* Performs a melee attack on an actor (if any) on a neighbouring tile if range == 1.
|
||||
* Moves closer to an enemy if an attack is not possible.
|
||||
*
|
||||
* @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());
|
||||
public static boolean tryAttack(IGame game, int range) {
|
||||
List<ILocation> neighbours = game.getVisible();
|
||||
for (ILocation neighbour : neighbours) {
|
||||
if (game.getMap().hasActors(neighbour)) {
|
||||
IMapView map = game.getMap();
|
||||
if (map.hasActors(neighbour) && map.getActors(neighbour).get(0) instanceof IPlayer) {
|
||||
ILocation current = game.getLocation();
|
||||
GridDirection dir = game.locationDirection(current, neighbour);
|
||||
List<GridDirection> dirs = 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;
|
||||
for (GridDirection dir : dirs) {
|
||||
if (range == 1 && current.gridDistanceTo(neighbour) <= 1 && current.canGo(dir) && game.getMap().has(current.go(dir), actor)) {
|
||||
game.attack(dir, actor);
|
||||
return true;
|
||||
}
|
||||
if (range > 1 && current.gridDistanceTo(neighbour) <= range) {
|
||||
game.rangedAttack(dir, actor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (GridDirection dir : dirs) {
|
||||
if (game.canGo(dir)) {
|
||||
game.move(dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,22 +70,20 @@ public class NPC {
|
||||
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;
|
||||
ILocation current = game.getLocation();
|
||||
List<GridDirection> dirs = game.locationDirection(current, neighbour);
|
||||
for (GridDirection dir : dirs) {
|
||||
if (game.canGo(dir)) {
|
||||
game.move(dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasItem) {
|
||||
ILocation current = game.getLocation();
|
||||
GridDirection dir = game.locationDirection(current, neighbour);
|
||||
if (game.canGo(dir)) {
|
||||
game.move(dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -130,10 +118,12 @@ public class NPC {
|
||||
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;
|
||||
List<GridDirection> dirs = reverseDir(game.locationDirection(current, neighbour));
|
||||
for (GridDirection dir : dirs) {
|
||||
if (game.canGo(dir)) {
|
||||
game.move(dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,21 +131,25 @@ public class NPC {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the main four directions.
|
||||
* Reverses a list of directions.
|
||||
*
|
||||
* @param dir A direction
|
||||
* @return An opposite direction
|
||||
* @param dirs A list directions
|
||||
* @return A list of the opposite directions
|
||||
*/
|
||||
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;
|
||||
private static List<GridDirection> reverseDir(List<GridDirection> dirs) {
|
||||
for (int i = 0; i < dirs.size(); i++) {
|
||||
GridDirection dir = dirs.get(i);
|
||||
if (dir == GridDirection.SOUTH) {
|
||||
dirs.set(i, GridDirection.NORTH);
|
||||
} else if (dir == GridDirection.NORTH) {
|
||||
dirs.set(i, GridDirection.SOUTH);
|
||||
} else if (dir == GridDirection.WEST) {
|
||||
dirs.set(i, GridDirection.EAST);
|
||||
} else {
|
||||
dirs.set(i, GridDirection.WEST);
|
||||
}
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user