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
749 lines
22 KiB
Java
749 lines
22 KiB
Java
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;
|
||
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.*;
|
||
import inf101.v18.rogue101.shared.NPC;
|
||
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();
|
||
|
||
/**
|
||
* 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
|
||
* 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;
|
||
|
||
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
|
||
// 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/testmap.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);
|
||
}
|
||
}
|
||
|
||
// 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-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]);
|
||
}
|
||
}
|
||
|
||
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()) {
|
||
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);
|
||
}
|
||
|
||
/**
|
||
* 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 = currentLocation.go(dir);
|
||
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());
|
||
} else {
|
||
NPC.playSound("audio/Realistic_Punch-Mark_DiAngelo-1609462330.wav");
|
||
}
|
||
if (getAttack() >= getDefence(target)) {
|
||
int actualDamage = target.handleDamage(this, target, getDamage(target));
|
||
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());
|
||
}
|
||
|
||
map.clean(loc);
|
||
|
||
if (target.isDestroyed()) {
|
||
return move(dir);
|
||
} else {
|
||
movePoints--;
|
||
return currentLocation;
|
||
}
|
||
}
|
||
|
||
@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
|
||
*
|
||
* @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();
|
||
}
|
||
|
||
/*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);
|
||
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);
|
||
return false;
|
||
} else if (currentActor instanceof IPlayer) {
|
||
if (currentActor.isDestroyed()) {
|
||
// a dead human player gets removed from the game
|
||
//This never actually triggers, because of map.clean();
|
||
/*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;
|
||
}
|
||
|
||
/**
|
||
* 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.
|
||
*/
|
||
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
|
||
} else if (item instanceof Carrot) {
|
||
((Carrot) item).doTurn();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
@Override
|
||
public boolean canGo(GridDirection dir) {
|
||
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);
|
||
itemFactories.put("s", Staff::new);
|
||
itemFactories.put("b", Bow::new);
|
||
}
|
||
|
||
@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) {
|
||
if (lastMessages.size() >= 3) {
|
||
lastMessages.remove(2);
|
||
}
|
||
lastMessages.add(0, s);
|
||
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) {
|
||
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 && !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 && !thirdLineWritten) {
|
||
printer.printAt(1, Main.LINE_MSG3, lastMessages.get(2));
|
||
}
|
||
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() {
|
||
if (numPlayers == 0) {
|
||
map.draw(painter, printer);
|
||
} else {
|
||
((GameMap) map).drawVisible(painter, printer);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public boolean drop(IItem item) {
|
||
if (item != null) {
|
||
map.add(currentLocation, item);
|
||
return true;
|
||
} else
|
||
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));
|
||
}
|
||
|
||
@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() {
|
||
List<GridDirection> moves = new ArrayList<>();
|
||
for (GridDirection dir : GridDirection.FOUR_DIRECTIONS) {
|
||
if (canGo(dir)) {
|
||
moves.add(dir);
|
||
}
|
||
}
|
||
return moves;
|
||
}
|
||
|
||
@Override
|
||
public List<ILocation> getVisible() {
|
||
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
|
||
public int getWidth() {
|
||
return map.getWidth();
|
||
}
|
||
|
||
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) {
|
||
return !((IPlayer) currentActor).keyPressed(this, code);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
@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) && !(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 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;
|
||
}
|
||
|
||
@Override
|
||
public List<GridDirection> locationDirection(ILocation start, ILocation target) {
|
||
int targetX = target.getX(), targetY = target.getY();
|
||
int startX = start.getX(), startY = start.getY();
|
||
List<GridDirection> dirs = new ArrayList<>();
|
||
if (targetX > startX && targetY > startY) {
|
||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||
dirs.add(GridDirection.SOUTH);
|
||
dirs.add(GridDirection.EAST);
|
||
} else {
|
||
dirs.add(GridDirection.EAST);
|
||
dirs.add(GridDirection.SOUTH);
|
||
}
|
||
} else if (targetX > startX && targetY < startY) {
|
||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||
dirs.add(GridDirection.NORTH);
|
||
dirs.add(GridDirection.EAST);
|
||
} else {
|
||
dirs.add(GridDirection.EAST);
|
||
dirs.add(GridDirection.NORTH);
|
||
}
|
||
} else if (targetX < startX && targetY > startY) {
|
||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||
dirs.add(GridDirection.SOUTH);
|
||
dirs.add(GridDirection.WEST);
|
||
} else {
|
||
dirs.add(GridDirection.WEST);
|
||
dirs.add(GridDirection.SOUTH);
|
||
}
|
||
} else if (targetX < startX && targetY < startY) {
|
||
if (Math.abs(targetX - startX) < Math.abs(targetY - startY)) {
|
||
dirs.add(GridDirection.NORTH);
|
||
dirs.add(GridDirection.WEST);
|
||
} else {
|
||
dirs.add(GridDirection.WEST);
|
||
dirs.add(GridDirection.NORTH);
|
||
}
|
||
} else if (targetX > startX) {
|
||
dirs.add(GridDirection.EAST);
|
||
} else if (targetX < startX) {
|
||
dirs.add(GridDirection.WEST);
|
||
} else if (targetY > startY) {
|
||
dirs.add(GridDirection.SOUTH);
|
||
} else if (targetY < startY) {
|
||
dirs.add(GridDirection.NORTH);
|
||
}
|
||
return dirs;
|
||
}
|
||
}
|