2018-02-28 23:51:40 +01:00
|
|
|
|
package inf101.v18.rogue101.game;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Random;
|
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
|
|
|
|
|
import inf101.v18.gfx.Screen;
|
|
|
|
|
import inf101.v18.gfx.gfxmode.ITurtle;
|
|
|
|
|
import inf101.v18.gfx.gfxmode.TurtlePainter;
|
|
|
|
|
import inf101.v18.gfx.textmode.Printer;
|
|
|
|
|
import inf101.v18.grid.GridDirection;
|
|
|
|
|
import inf101.v18.grid.IGrid;
|
|
|
|
|
import inf101.v18.grid.ILocation;
|
|
|
|
|
import inf101.v18.rogue101.Main;
|
2018-03-09 23:18:00 +01:00
|
|
|
|
import inf101.v18.rogue101.enemies.Girl;
|
2018-02-28 23:51:40 +01:00
|
|
|
|
import inf101.v18.rogue101.examples.Carrot;
|
2018-03-07 21:40:06 +01:00
|
|
|
|
import inf101.v18.rogue101.items.Manga;
|
2018-02-28 23:51:40 +01:00
|
|
|
|
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;
|
2018-03-07 21:40:06 +01:00
|
|
|
|
import inf101.v18.rogue101.objects.*;
|
2018-02-28 23:51:40 +01:00
|
|
|
|
import javafx.scene.canvas.GraphicsContext;
|
|
|
|
|
import javafx.scene.input.KeyCode;
|
|
|
|
|
import javafx.scene.paint.Color;
|
|
|
|
|
|
|
|
|
|
public class Game implements IGame {
|
|
|
|
|
/**
|
|
|
|
|
* All the IActors that have things left to do this turn
|
|
|
|
|
*/
|
|
|
|
|
private List<IActor> actors = Collections.synchronizedList(new ArrayList<>());
|
|
|
|
|
/**
|
|
|
|
|
* For fancy solution to factory problem
|
|
|
|
|
*/
|
|
|
|
|
private Map<String, Supplier<IItem>> itemFactories = new HashMap<>();
|
|
|
|
|
/**
|
|
|
|
|
* Useful random generator
|
|
|
|
|
*/
|
|
|
|
|
private Random random = new Random();
|
|
|
|
|
/**
|
|
|
|
|
* The game map. {@link IGameMap} gives us a few more details than
|
|
|
|
|
* {@link IMapView} (write access to item lists); the game needs this but
|
|
|
|
|
* individual items don't.
|
|
|
|
|
*/
|
|
|
|
|
private IGameMap map;
|
|
|
|
|
private IActor currentActor;
|
|
|
|
|
private ILocation currentLocation;
|
|
|
|
|
private int movePoints = 0;
|
|
|
|
|
private final ITurtle painter;
|
|
|
|
|
private final Printer printer;
|
|
|
|
|
private int numPlayers = 0;
|
|
|
|
|
|
|
|
|
|
public Game(Screen screen, ITurtle painter, Printer printer) {
|
|
|
|
|
this.painter = painter;
|
|
|
|
|
this.printer = printer;
|
|
|
|
|
|
2018-03-07 21:40:06 +01:00
|
|
|
|
addFactory();
|
2018-02-28 23:51:40 +01:00
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// generated
|
|
|
|
|
//
|
|
|
|
|
// 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");
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Game(String mapString) {
|
2018-03-08 00:57:53 +01:00
|
|
|
|
printer = new Printer(1280, 720);
|
2018-03-07 22:34:18 +01:00
|
|
|
|
painter = new TurtlePainter(1280, 720);
|
2018-03-07 21:40:06 +01:00
|
|
|
|
|
|
|
|
|
addFactory();
|
|
|
|
|
|
2018-02-28 23:51:40 +01:00
|
|
|
|
IGrid<String> inputGrid = MapReader.readString(mapString);
|
|
|
|
|
this.map = new GameMap(inputGrid.getArea());
|
|
|
|
|
for (ILocation loc : inputGrid.locations()) {
|
|
|
|
|
IItem item = createItem(inputGrid.get(loc));
|
|
|
|
|
if (item != null) {
|
|
|
|
|
map.add(loc, item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void addItem(IItem item) {
|
|
|
|
|
map.add(currentLocation, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void addItem(String sym) {
|
|
|
|
|
IItem item = createItem(sym);
|
|
|
|
|
if (item != null)
|
|
|
|
|
map.add(currentLocation, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ILocation attack(GridDirection dir, IItem target) {
|
2018-03-08 23:38:59 +01:00
|
|
|
|
ILocation loc = currentLocation.go(dir);
|
|
|
|
|
if (!map.has(loc, target)) {
|
2018-02-28 23:51:40 +01:00
|
|
|
|
throw new IllegalMoveException("Target isn't there!");
|
2018-03-08 23:38:59 +01:00
|
|
|
|
}
|
|
|
|
|
int damage = currentActor.getAttack() + random.nextInt(20) + 1;
|
|
|
|
|
if (damage >= target.getDefence() + 10) {
|
|
|
|
|
int actualDamage = target.handleDamage(this, target, damage);
|
|
|
|
|
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
|
|
|
|
|
} else {
|
|
|
|
|
displayMessage("The attack missed.");
|
|
|
|
|
}
|
2018-02-28 23:51:40 +01:00
|
|
|
|
|
|
|
|
|
map.clean(loc);
|
|
|
|
|
|
|
|
|
|
if (target.isDestroyed()) {
|
|
|
|
|
return move(dir);
|
|
|
|
|
} else {
|
|
|
|
|
movePoints--;
|
|
|
|
|
return currentLocation;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Begin a new game turn, or continue to the previous turn
|
|
|
|
|
*
|
|
|
|
|
* @return True if the game should wait for more user input
|
|
|
|
|
*/
|
|
|
|
|
public boolean doTurn() {
|
|
|
|
|
do {
|
|
|
|
|
if (actors.isEmpty()) {
|
|
|
|
|
// System.err.println("new turn!");
|
|
|
|
|
|
|
|
|
|
// no one in the queue, we're starting a new turn!
|
|
|
|
|
// first collect all the actors:
|
|
|
|
|
beginTurn();
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-01 21:13:25 +01:00
|
|
|
|
if (random.nextInt(100) < 20) {
|
2018-03-07 21:40:06 +01:00
|
|
|
|
ILocation loc = map.getLocation(random.nextInt(map.getWidth()), random.nextInt(map.getHeight()));
|
2018-03-01 21:13:25 +01:00
|
|
|
|
if (!map.hasActors(loc) && !map.hasItems(loc) && !map.hasWall(loc)) {
|
|
|
|
|
map.add(loc, new Carrot());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-28 23:51:40 +01:00
|
|
|
|
// process actors one by one; for the IPlayer, we return and wait for keypresses
|
|
|
|
|
// Possible TODO: 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)
|
|
|
|
|
while (!actors.isEmpty()) {
|
|
|
|
|
// get the next player or non-player in the queue
|
|
|
|
|
currentActor = actors.remove(0);
|
|
|
|
|
if (currentActor.isDestroyed()) // skip if it's dead
|
|
|
|
|
continue;
|
|
|
|
|
currentLocation = map.getLocation(currentActor);
|
|
|
|
|
if (currentLocation == null) {
|
|
|
|
|
displayDebug("doTurn(): Whoops! Actor has disappeared from the map: " + currentActor);
|
|
|
|
|
}
|
|
|
|
|
movePoints = 1; // everyone gets to do one thing
|
|
|
|
|
|
|
|
|
|
if (currentActor instanceof INonPlayer) {
|
|
|
|
|
// computer-controlled players do their stuff right away
|
|
|
|
|
((INonPlayer) currentActor).doTurn(this);
|
|
|
|
|
// remove any dead items from current location
|
|
|
|
|
map.clean(currentLocation);
|
|
|
|
|
} 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!!!");
|
|
|
|
|
map.remove(currentLocation, currentActor);
|
|
|
|
|
currentActor = 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
|
|
|
|
|
// makes a move, it'll lose its movement point and doTurn() will be called again
|
|
|
|
|
//
|
|
|
|
|
// NOTE: currentActor and currentLocation are set to the IPlayer (above),
|
|
|
|
|
// so the game remembers who the player is whenever new keypresses occur. This
|
|
|
|
|
// is also how e.g., getLocalItems() work – the game always keeps track of
|
|
|
|
|
// whose turn it is.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
displayDebug("doTurn(): Hmm, this is a very strange actor: " + currentActor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (numPlayers > 0); // we can safely repeat if we have players, since we'll return (and break out of
|
|
|
|
|
// the loop) once we hit the player
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Go through the map and collect all the actors.
|
|
|
|
|
*/
|
|
|
|
|
private void beginTurn() {
|
|
|
|
|
numPlayers = 0;
|
|
|
|
|
// this extra fancy iteration over each map location runs *in parallel* on
|
|
|
|
|
// multicore systems!
|
|
|
|
|
// that makes some things more tricky, hence the "synchronized" block and
|
|
|
|
|
// "Collections.synchronizedList()" in the initialization of "actors".
|
|
|
|
|
// NOTE: If you want to modify this yourself, it might be a good idea to replace
|
|
|
|
|
// "parallelStream()" by "stream()", because weird things can happen when many
|
|
|
|
|
// things happen
|
|
|
|
|
// at the same time! (or do INF214 or DAT103 to learn about locks and threading)
|
|
|
|
|
map.getArea().parallelStream().forEach((loc) -> { // will do this for each location in map
|
|
|
|
|
List<IItem> list = map.getAllModifiable(loc); // all items at loc
|
|
|
|
|
Iterator<IItem> li = list.iterator(); // manual iterator lets us remove() items
|
|
|
|
|
while (li.hasNext()) { // this is what "for(IItem item : list)" looks like on the inside
|
|
|
|
|
IItem item = li.next();
|
|
|
|
|
if (item.getCurrentHealth() < 0) {
|
|
|
|
|
// normally, we expect these things to be removed when they are destroyed, so
|
|
|
|
|
// this shouldn't happen
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
formatDebug("beginTurn(): found and removed leftover destroyed item %s '%s' at %s%n",
|
|
|
|
|
item.getName(), item.getSymbol(), loc);
|
|
|
|
|
}
|
|
|
|
|
li.remove();
|
|
|
|
|
map.remove(loc, item); // need to do this too, to update item map
|
|
|
|
|
} else if (item instanceof IPlayer) {
|
|
|
|
|
actors.add(0, (IActor) item); // we let the human player go first
|
|
|
|
|
synchronized (this) {
|
|
|
|
|
numPlayers++;
|
|
|
|
|
}
|
|
|
|
|
} else if (item instanceof IActor) {
|
|
|
|
|
actors.add((IActor) item); // add other actors to the end of the list
|
2018-03-01 21:13:25 +01:00
|
|
|
|
} else if (item instanceof Carrot) {
|
|
|
|
|
((Carrot) item).doTurn();
|
2018-02-28 23:51:40 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean canGo(GridDirection dir) {
|
|
|
|
|
return map.canGo(currentLocation, dir);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-07 21:40:06 +01:00
|
|
|
|
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);
|
2018-03-09 23:18:00 +01:00
|
|
|
|
itemFactories.put("G", Girl::new);
|
2018-03-07 21:40:06 +01:00
|
|
|
|
itemFactories.put(".", Dust::new);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-28 23:51:40 +01:00
|
|
|
|
@Override
|
|
|
|
|
public IItem createItem(String sym) {
|
|
|
|
|
switch (sym) {
|
|
|
|
|
case " ":
|
|
|
|
|
return null;
|
|
|
|
|
default:
|
|
|
|
|
// alternative/advanced method
|
|
|
|
|
Supplier<IItem> factory = itemFactories.get(sym);
|
|
|
|
|
if (factory != null) {
|
|
|
|
|
return factory.get();
|
|
|
|
|
} else {
|
|
|
|
|
System.err.println("createItem: Don't know how to create a '" + sym + "'");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void displayDebug(String s) {
|
|
|
|
|
printer.clearLine(Main.LINE_DEBUG);
|
|
|
|
|
printer.printAt(1, Main.LINE_DEBUG, s, Color.DARKRED);
|
|
|
|
|
System.err.println(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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
|
|
|
|
|
printer.clearLine(Main.LINE_MSG1);
|
|
|
|
|
printer.printAt(1, Main.LINE_MSG1, s);
|
|
|
|
|
System.out.println("Message: «" + s + "»");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void displayStatus(String s) {
|
|
|
|
|
printer.clearLine(Main.LINE_STATUS);
|
|
|
|
|
printer.printAt(1, Main.LINE_STATUS, s);
|
|
|
|
|
System.out.println("Status: «" + s + "»");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void draw() {
|
|
|
|
|
map.draw(painter, printer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean drop(IItem item) {
|
|
|
|
|
if (item != null) {
|
|
|
|
|
map.add(currentLocation, item);
|
|
|
|
|
return true;
|
|
|
|
|
} else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void formatDebug(String s, Object... args) {
|
|
|
|
|
displayDebug(String.format(s, args));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void formatMessage(String s, Object... args) {
|
|
|
|
|
displayMessage(String.format(s, args));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void formatStatus(String s, Object... args) {
|
|
|
|
|
displayStatus(String.format(s, args));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getHeight() {
|
|
|
|
|
return map.getHeight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<IItem> getLocalItems() {
|
|
|
|
|
return map.getItems(currentLocation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ILocation getLocation() {
|
|
|
|
|
return currentLocation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ILocation getLocation(GridDirection dir) {
|
|
|
|
|
if (currentLocation.canGo(dir))
|
|
|
|
|
return currentLocation.go(dir);
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the game map. {@link IGameMap} gives us a few more details than
|
|
|
|
|
* {@link IMapView} (write access to item lists); the game needs this but
|
|
|
|
|
* individual items don't.
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public IMapView getMap() {
|
|
|
|
|
return map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<GridDirection> getPossibleMoves() {
|
2018-03-01 21:13:25 +01:00
|
|
|
|
List<GridDirection> moves = new ArrayList<>();
|
|
|
|
|
for (GridDirection dir : GridDirection.FOUR_DIRECTIONS) {
|
|
|
|
|
if (canGo(dir)) {
|
|
|
|
|
moves.add(dir);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return moves;
|
2018-02-28 23:51:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public List<ILocation> getVisible() {
|
2018-03-09 21:29:56 +01:00
|
|
|
|
List<ILocation> neighbours = map.getNeighbourhood(currentLocation, currentActor.getVision());
|
|
|
|
|
List<ILocation> valid = new ArrayList<>();
|
|
|
|
|
for (ILocation neighbour : neighbours) {
|
|
|
|
|
boolean blocked = false;
|
|
|
|
|
for (ILocation tile : currentLocation.gridLineTo(neighbour)) {
|
|
|
|
|
if (map.hasWall(tile)) {
|
|
|
|
|
blocked = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!blocked) {
|
|
|
|
|
valid.add(neighbour);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return valid;
|
2018-02-28 23:51:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int getWidth() {
|
|
|
|
|
return map.getWidth();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void 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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ILocation move(GridDirection dir) {
|
|
|
|
|
if (movePoints < 1)
|
|
|
|
|
throw new IllegalMoveException("You're out of moves!");
|
|
|
|
|
ILocation newLoc = map.go(currentLocation, dir);
|
|
|
|
|
map.remove(currentLocation, currentActor);
|
|
|
|
|
map.add(newLoc, currentActor);
|
|
|
|
|
currentLocation = newLoc;
|
|
|
|
|
movePoints--;
|
|
|
|
|
return currentLocation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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;
|
|
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ILocation rangedAttack(GridDirection dir, IItem target) {
|
2018-03-09 23:18:00 +01:00
|
|
|
|
ILocation loc = currentLocation;
|
|
|
|
|
int damage = (currentActor.getAttack() + random.nextInt(20) + 1)
|
|
|
|
|
/ loc.gridDistanceTo(map.getLocation(target)); //Close attacks will take more damage.
|
|
|
|
|
if (damage >= target.getDefence() + 10) {
|
|
|
|
|
int actualDamage = target.handleDamage(this, target, damage);
|
|
|
|
|
formatMessage("%s hits %s for %d damage", currentActor.getName(), target.getName(), actualDamage);
|
|
|
|
|
} else {
|
|
|
|
|
displayMessage("The attack missed.");
|
|
|
|
|
}
|
|
|
|
|
map.clean(loc);
|
|
|
|
|
if (target.isDestroyed() && map.has(currentLocation.go(dir), target)) {
|
|
|
|
|
return move(dir);
|
|
|
|
|
} else {
|
|
|
|
|
return currentLocation;
|
|
|
|
|
}
|
2018-02-28 23:51:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public ITurtle getPainter() {
|
|
|
|
|
return painter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Printer getPrinter() {
|
|
|
|
|
return printer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int[] getFreeTextAreaBounds() {
|
|
|
|
|
int[] area = new int[4];
|
|
|
|
|
area[0] = getWidth() + 1;
|
|
|
|
|
area[1] = 1;
|
|
|
|
|
area[2] = printer.getLineWidth();
|
|
|
|
|
area[3] = printer.getPageHeight() - 5;
|
|
|
|
|
return area;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void clearFreeTextArea() {
|
|
|
|
|
printer.clearRegion(getWidth() + 1, 1, printer.getLineWidth() - getWidth(), printer.getPageHeight() - 5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void clearFreeGraphicsArea() {
|
|
|
|
|
painter.as(GraphicsContext.class).clearRect(getWidth() * printer.getCharWidth(), 0,
|
|
|
|
|
painter.getWidth() - getWidth() * printer.getCharWidth(),
|
|
|
|
|
(printer.getPageHeight() - 5) * printer.getCharHeight());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public double[] getFreeGraphicsAreaBounds() {
|
|
|
|
|
double[] area = new double[4];
|
|
|
|
|
area[0] = getWidth() * printer.getCharWidth();
|
|
|
|
|
area[1] = 0;
|
|
|
|
|
area[2] = painter.getWidth();
|
|
|
|
|
area[3] = getHeight() * printer.getCharHeight();
|
|
|
|
|
return area;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public IActor getActor() {
|
|
|
|
|
return currentActor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ILocation setCurrent(IActor actor) {
|
|
|
|
|
currentLocation = map.getLocation(actor);
|
|
|
|
|
if (currentLocation != null) {
|
|
|
|
|
currentActor = actor;
|
|
|
|
|
movePoints = 1;
|
|
|
|
|
}
|
|
|
|
|
return currentLocation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IActor setCurrent(ILocation loc) {
|
|
|
|
|
List<IActor> list = map.getActors(loc);
|
|
|
|
|
if (!list.isEmpty()) {
|
|
|
|
|
currentActor = list.get(0);
|
|
|
|
|
currentLocation = loc;
|
|
|
|
|
movePoints = 1;
|
|
|
|
|
}
|
|
|
|
|
return currentActor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IActor setCurrent(int x, int y) {
|
|
|
|
|
return setCurrent(map.getLocation(x, y));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Random getRandom() {
|
|
|
|
|
return random;
|
|
|
|
|
}
|
2018-03-08 23:38:59 +01:00
|
|
|
|
|
|
|
|
|
@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;
|
|
|
|
|
}
|
2018-02-28 23:51:40 +01:00
|
|
|
|
}
|