Compare commits
5 Commits
dc9dad4d9c
...
1f9e6f4e4a
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f9e6f4e4a | |||
| b9a43be3c6 | |||
| 9bb76ce682 | |||
| 712d58e8e3 | |||
| e8112f1cbb |
@ -65,8 +65,4 @@ public class CollisionChecker {
|
|||||||
|
|
||||||
return new Point(x, y);
|
return new Point(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTile(Point destination) {
|
|
||||||
map.removeTileImage(destination);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,6 @@ import java.awt.Point;
|
|||||||
public class BlinkyStrategy implements GhostStrategy {
|
public class BlinkyStrategy implements GhostStrategy {
|
||||||
@Override
|
@Override
|
||||||
public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) {
|
public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) {
|
||||||
return pacman.getTilePosition();
|
return pacman.getPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import java.awt.Point;
|
|||||||
public class ClydeStrategy implements GhostStrategy {
|
public class ClydeStrategy implements GhostStrategy {
|
||||||
@Override
|
@Override
|
||||||
public Point chooseTarget(Ghost clyde, PacMan pacman, GameMap map) {
|
public Point chooseTarget(Ghost clyde, PacMan pacman, GameMap map) {
|
||||||
Point pacTile = pacman.getTilePosition();
|
Point pacTile = pacman.getPosition();
|
||||||
Point clydeTile = clyde.getPosition(); // ghost’s current tile
|
Point clydeTile = clyde.getPosition(); // ghost’s current tile
|
||||||
|
|
||||||
double distance = pacTile.distance(clydeTile);
|
double distance = pacTile.distance(clydeTile);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ public class Ghost {
|
|||||||
|
|
||||||
private final GhostCollisionChecker collisionChecker;
|
private final GhostCollisionChecker collisionChecker;
|
||||||
private final GhostStrategy chaseStrategy;
|
private final GhostStrategy chaseStrategy;
|
||||||
|
private final Point startPos;
|
||||||
@Getter
|
@Getter
|
||||||
private Point position;
|
private Point position;
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ public class Ghost {
|
|||||||
position = new Point(
|
position = new Point(
|
||||||
13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X + (GameMap.MAP_TILESIZE / 2),
|
13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X + (GameMap.MAP_TILESIZE / 2),
|
||||||
4 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2) );
|
4 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2) );
|
||||||
|
startPos = position;
|
||||||
this.currentStrategy = chaseStrategy;
|
this.currentStrategy = chaseStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +75,40 @@ public class Ghost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updatePosition(PacMan pacman, GameMap map) {
|
private void updatePosition(PacMan pacman, GameMap map) {
|
||||||
|
// only move ghost on update interval - this is basically ghost speed;
|
||||||
if (movementTick >= GHOST_MOVEMENT_UPDATE_FREQUENCY) {
|
if (movementTick >= GHOST_MOVEMENT_UPDATE_FREQUENCY) {
|
||||||
|
chooseDirection(pacman, map);
|
||||||
|
|
||||||
|
Point newPosition = getNewPosition();
|
||||||
|
|
||||||
|
move(newPosition);
|
||||||
|
|
||||||
|
movementTick = 0;
|
||||||
|
} else movementTick++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a position and a direction - calculate the new position
|
||||||
|
*
|
||||||
|
* @return new position
|
||||||
|
*/
|
||||||
|
private Point getNewPosition() {
|
||||||
|
Point point = new Point(
|
||||||
|
position.x + direction.dx,
|
||||||
|
position.y + direction.dy);
|
||||||
|
//log.debug("Next position {}", point);
|
||||||
|
return point;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose a new direction when 'aligned' ie when in the exact middle of a tile
|
||||||
|
* else continue with the existing direction.
|
||||||
|
*
|
||||||
|
* @param pacman
|
||||||
|
* @param map
|
||||||
|
*/
|
||||||
|
private void chooseDirection(PacMan pacman, GameMap map) {
|
||||||
if (isAlligned(position)) {
|
if (isAlligned(position)) {
|
||||||
log.info("Evaluating possible directions");
|
log.info("Evaluating possible directions");
|
||||||
prevDirection = direction;
|
prevDirection = direction;
|
||||||
@ -83,19 +117,14 @@ public class Ghost {
|
|||||||
currentStrategy.chooseTarget(this, pacman, map));
|
currentStrategy.chooseTarget(this, pacman, map));
|
||||||
log.info("selecting direction {}", direction);
|
log.info("selecting direction {}", direction);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Point newPosition = new Point(
|
private void move(Point newPosition) {
|
||||||
position.x + direction.dx,
|
|
||||||
position.y + direction.dy);
|
|
||||||
log.debug("Next position {}", newPosition);
|
|
||||||
|
|
||||||
Point destination = collisionChecker.canMoveTo(direction, newPosition);
|
Point destination = collisionChecker.canMoveTo(direction, newPosition);
|
||||||
|
|
||||||
if (destination != null) {
|
if (destination != null) {
|
||||||
position = destination;
|
position = destination;
|
||||||
}
|
}
|
||||||
movementTick = 0;
|
|
||||||
} else movementTick++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Direction, Integer> prioritize(List<Direction> directions) {
|
private Map<Direction, Integer> prioritize(List<Direction> directions) {
|
||||||
@ -167,4 +196,11 @@ public class Ghost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFrightened() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPosition() {
|
||||||
|
position = startPos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ public class InkyStrategy implements GhostStrategy {
|
|||||||
public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) {
|
public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) {
|
||||||
// 1. Two tiles ahead of pacman
|
// 1. Two tiles ahead of pacman
|
||||||
Direction pacmanDir = pacman.getDirection();
|
Direction pacmanDir = pacman.getDirection();
|
||||||
Point pacmanPos = pacman.getTilePosition();
|
Point pacmanPos = pacman.getPosition();
|
||||||
Point ahead = switch (pacmanDir){
|
Point ahead = switch (pacmanDir){
|
||||||
case RIGHT -> new Point(pacmanPos.x + 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
case RIGHT -> new Point(pacmanPos.x + 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
||||||
case LEFT -> new Point(pacmanPos.x - 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
case LEFT -> new Point(pacmanPos.x - 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
||||||
|
|||||||
@ -22,6 +22,7 @@ public class PacMan {
|
|||||||
private static final int COLLISION_BOX_SIZE = 16;
|
private static final int COLLISION_BOX_SIZE = 16;
|
||||||
private static final int COLLISION_BOX_OFFSET = (PACMAN_SIZE - COLLISION_BOX_SIZE) / 2;
|
private static final int COLLISION_BOX_OFFSET = (PACMAN_SIZE - COLLISION_BOX_SIZE) / 2;
|
||||||
private final Game game;
|
private final Game game;
|
||||||
|
private final Point startPosition;
|
||||||
private int aniTick = 0;
|
private int aniTick = 0;
|
||||||
private int aniIndex = 0;
|
private int aniIndex = 0;
|
||||||
private static final int ANIMATION_UPDATE_FREQUENCY = 10;
|
private static final int ANIMATION_UPDATE_FREQUENCY = 10;
|
||||||
@ -29,6 +30,7 @@ public class PacMan {
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean moving;
|
private boolean moving;
|
||||||
private final BufferedImage[][] movmentImages = new BufferedImage[4][4];
|
private final BufferedImage[][] movmentImages = new BufferedImage[4][4];
|
||||||
|
@Getter
|
||||||
private Point position;
|
private Point position;
|
||||||
private static final BufferedImage COLLISION_BOX = MiscUtil.createOutlinedBox(COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, Color.yellow, 2);
|
private static final BufferedImage COLLISION_BOX = MiscUtil.createOutlinedBox(COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, Color.yellow, 2);
|
||||||
private final CollisionChecker collisionChecker;
|
private final CollisionChecker collisionChecker;
|
||||||
@ -39,9 +41,10 @@ public class PacMan {
|
|||||||
public PacMan(Game game, CollisionChecker collisionChecker) {
|
public PacMan(Game game, CollisionChecker collisionChecker) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.collisionChecker = collisionChecker;
|
this.collisionChecker = collisionChecker;
|
||||||
position = new Point(
|
this.position = new Point(
|
||||||
26 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X,
|
26 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X,
|
||||||
13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2));
|
13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2));
|
||||||
|
this.startPosition = this.position;
|
||||||
loadAnimation();
|
loadAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,8 +83,8 @@ public class PacMan {
|
|||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
updateAnimationTick();
|
updateAnimationTick();
|
||||||
if(direction == Direction.NONE) return;
|
//if(direction == Direction.NONE) return;
|
||||||
|
if(moving) {
|
||||||
Point newPosition = switch (direction) {
|
Point newPosition = switch (direction) {
|
||||||
case RIGHT -> new Point(position.x + speed, position.y);
|
case RIGHT -> new Point(position.x + speed, position.y);
|
||||||
case LEFT -> new Point(position.x - speed, position.y);
|
case LEFT -> new Point(position.x - speed, position.y);
|
||||||
@ -93,10 +96,10 @@ public class PacMan {
|
|||||||
Point destination = collisionChecker.getValidDestination(direction, newPosition, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE);
|
Point destination = collisionChecker.getValidDestination(direction, newPosition, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE);
|
||||||
|
|
||||||
if (destination != null) {
|
if (destination != null) {
|
||||||
collisionChecker.removeTile(destination);
|
|
||||||
position = destination;
|
position = destination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateAnimationTick() {
|
private void updateAnimationTick() {
|
||||||
if (moving) {
|
if (moving) {
|
||||||
@ -112,7 +115,19 @@ public class PacMan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point getTilePosition() {
|
public double distanceTo(Point point) {
|
||||||
return position;
|
return position.distance(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loseLife() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPosition() {
|
||||||
|
position = startPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image getLifeIcon() {
|
||||||
|
return movmentImages[0][1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ public class PinkyStrategy implements GhostStrategy{
|
|||||||
@Override
|
@Override
|
||||||
public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) {
|
public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) {
|
||||||
Direction pacmanDir = pacman.getDirection();
|
Direction pacmanDir = pacman.getDirection();
|
||||||
Point pacmanPos = pacman.getTilePosition();
|
Point pacmanPos = pacman.getPosition();
|
||||||
return switch (pacmanDir){
|
return switch (pacmanDir){
|
||||||
case RIGHT -> new Point(pacmanPos.x + 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
case RIGHT -> new Point(pacmanPos.x + 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
||||||
case LEFT -> new Point(pacmanPos.x - 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
case LEFT -> new Point(pacmanPos.x - 8 * GameMap.MAP_TILESIZE, pacmanPos.y);
|
||||||
|
|||||||
@ -16,41 +16,43 @@ public class GameMap {
|
|||||||
public static final int MAP_ROW_SIZE = 30;
|
public static final int MAP_ROW_SIZE = 30;
|
||||||
public static final int OFFSET_Y = 7 * MAP_TILESIZE; // 160px from top
|
public static final int OFFSET_Y = 7 * MAP_TILESIZE; // 160px from top
|
||||||
public static final int OFFSET_X = MAP_TILESIZE; // 16px from left
|
public static final int OFFSET_X = MAP_TILESIZE; // 16px from left
|
||||||
private final BufferedImage[][] images = new BufferedImage[13][19];
|
private final BufferedImage[][] mapSpriteBuffer;
|
||||||
private final MapTile[][] mapData;
|
private final MapTile[][] mapData;
|
||||||
|
private final BufferedImage[][] mapItemSpriteBuffer;
|
||||||
|
|
||||||
public GameMap() {
|
public GameMap() {
|
||||||
loadSprites();
|
this.mapSpriteBuffer = LoadSave.loadSprites("sprites/PacMan-custom-spritemap-0-3.png", 5, 11, MAP_TILESIZE);
|
||||||
mapData = loadMap("maps/map1.csv");
|
this.mapItemSpriteBuffer = LoadSave.loadSprites("sprites/PacManAssets-Items.png", 2, 8, MAP_TILESIZE);
|
||||||
|
this.mapData = loadMap("maps/map1.csv", MAP_ROW_SIZE, MAP_COL_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapTile[][] loadMap(String path) {
|
private MapTile[][] loadMap(String path, int mapRowSize, int mapColSize) {
|
||||||
MapTile[][] data = new MapTile[MAP_ROW_SIZE][MAP_COL_SIZE];
|
MapTile[][] data = new MapTile[mapRowSize][mapColSize];
|
||||||
|
|
||||||
try (InputStream is = getClass().getClassLoader().getResourceAsStream(path);
|
try (InputStream is = getClass().getClassLoader().getResourceAsStream(path);
|
||||||
BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
|
BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
while ((line = br.readLine()) != null && rowIndex < MAP_ROW_SIZE) {
|
while ((line = br.readLine()) != null && rowIndex < mapRowSize) {
|
||||||
String[] tokens = line.split(",");
|
String[] tokens = line.split(",");
|
||||||
if (tokens.length != MAP_COL_SIZE) {
|
if (tokens.length != mapColSize) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Invalid map format: row " + rowIndex + " has " + tokens.length +
|
"Invalid map format: row " + rowIndex + " has " + tokens.length +
|
||||||
" columns, expected " + MAP_COL_SIZE
|
" columns, expected " + mapColSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int col = 0; col < MAP_COL_SIZE; col++) {
|
for (int col = 0; col < mapColSize; col++) {
|
||||||
int value = Integer.parseInt(tokens[col].trim());
|
int value = Integer.parseInt(tokens[col].trim());
|
||||||
data[rowIndex][col] = new MapTile(getSprite(value), value);
|
data[rowIndex][col] = new MapTile(getSprite(value), value);
|
||||||
}
|
}
|
||||||
rowIndex++;
|
rowIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rowIndex != MAP_ROW_SIZE) {
|
if (rowIndex != mapRowSize) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Invalid map format: found " + rowIndex + " rows, expected " + MAP_ROW_SIZE
|
"Invalid map format: found " + rowIndex + " rows, expected " + mapRowSize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,76 +66,67 @@ public class GameMap {
|
|||||||
|
|
||||||
private BufferedImage getSprite(int value) {
|
private BufferedImage getSprite(int value) {
|
||||||
return switch (value){
|
return switch (value){
|
||||||
case 0 -> images[1][1];
|
case 0 -> mapSpriteBuffer[1][1];
|
||||||
case 1 -> images[0][0];
|
case 1 -> mapSpriteBuffer[0][0];
|
||||||
case 2 -> images[0][1];
|
case 2 -> mapSpriteBuffer[0][1];
|
||||||
case 3 -> images[0][2];
|
case 3 -> mapSpriteBuffer[0][2];
|
||||||
case 4 -> images[0][3];
|
case 4 -> mapSpriteBuffer[0][3];
|
||||||
case 5 -> images[0][4];
|
case 5 -> mapSpriteBuffer[0][4];
|
||||||
case 6 -> images[0][5];
|
case 6 -> mapSpriteBuffer[0][5];
|
||||||
case 7 -> images[0][6];
|
case 7 -> mapSpriteBuffer[0][6];
|
||||||
case 8 -> images[0][7];
|
case 8 -> mapSpriteBuffer[0][7];
|
||||||
case 9 -> images[0][8];
|
case 9 -> mapSpriteBuffer[0][8];
|
||||||
case 10 -> images[0][9];
|
case 10 -> mapSpriteBuffer[0][9];
|
||||||
case 11 -> images[0][10];
|
case 11 -> mapSpriteBuffer[0][10];
|
||||||
case 12 -> images[1][0];
|
case 12 -> mapSpriteBuffer[1][0];
|
||||||
case 13 -> images[1][1];
|
case 13 -> mapSpriteBuffer[1][1];
|
||||||
case 14 -> images[1][2];
|
case 14 -> mapSpriteBuffer[1][2];
|
||||||
case 15 -> images[1][3];
|
case 15 -> mapSpriteBuffer[1][3];
|
||||||
case 16 -> images[1][4];
|
case 16 -> mapSpriteBuffer[1][4];
|
||||||
case 17 -> images[1][5];
|
case 17 -> mapSpriteBuffer[1][5];
|
||||||
case 18 -> images[1][6];
|
case 18 -> mapSpriteBuffer[1][6];
|
||||||
case 19 -> images[1][7];
|
case 19 -> mapSpriteBuffer[1][7];
|
||||||
case 20 -> images[1][8];
|
case 20 -> mapSpriteBuffer[1][8];
|
||||||
case 21 -> images[1][9];
|
case 21 -> mapSpriteBuffer[1][9];
|
||||||
case 22 -> images[1][10];
|
case 22 -> mapSpriteBuffer[1][10];
|
||||||
case 23 -> images[2][0];
|
case 23 -> mapSpriteBuffer[2][0];
|
||||||
case 24 -> images[2][1];
|
case 24 -> mapSpriteBuffer[2][1];
|
||||||
case 25 -> images[2][2];
|
case 25 -> mapSpriteBuffer[2][2];
|
||||||
case 26 -> images[2][3];
|
case 26 -> mapSpriteBuffer[2][3];
|
||||||
case 27 -> images[2][4];
|
case 27 -> mapSpriteBuffer[2][4];
|
||||||
case 28 -> images[2][5];
|
case 28 -> mapSpriteBuffer[2][5];
|
||||||
case 29 -> images[2][6];
|
case 29 -> mapSpriteBuffer[2][6];
|
||||||
case 30 -> images[2][7];
|
case 30 -> mapSpriteBuffer[2][7];
|
||||||
case 31 -> images[2][8];
|
case 31 -> mapSpriteBuffer[2][8];
|
||||||
case 32 -> images[2][9];
|
case 32 -> mapSpriteBuffer[2][9];
|
||||||
case 33 -> images[2][10];
|
case 33 -> mapSpriteBuffer[2][10];
|
||||||
case 34 -> images[3][0];
|
case 34 -> mapSpriteBuffer[3][0];
|
||||||
case 35 -> images[3][1];
|
case 35 -> mapSpriteBuffer[3][1];
|
||||||
case 36 -> images[3][2];
|
case 36 -> mapSpriteBuffer[3][2];
|
||||||
case 37 -> images[3][3];
|
case 37 -> mapSpriteBuffer[3][3];
|
||||||
case 38 -> images[3][4];
|
case 38 -> mapSpriteBuffer[3][4];
|
||||||
case 39 -> images[3][5];
|
case 39 -> mapSpriteBuffer[3][5];
|
||||||
case 40 -> images[3][6];
|
case 40 -> mapSpriteBuffer[3][6];
|
||||||
case 41 -> images[3][7];
|
case 41 -> mapSpriteBuffer[3][7];
|
||||||
case 42 -> images[3][8];
|
case 42 -> mapSpriteBuffer[3][8];
|
||||||
case 43 -> images[3][9];
|
case 43 -> mapSpriteBuffer[3][9];
|
||||||
case 44 -> images[3][10];
|
case 44 -> mapSpriteBuffer[3][10];
|
||||||
case 45 -> images[4][0];
|
case 45 -> mapSpriteBuffer[4][0];
|
||||||
case 46 -> images[4][1];
|
case 46 -> mapSpriteBuffer[4][1];
|
||||||
case 47 -> images[4][2];
|
case 47 -> mapSpriteBuffer[4][2];
|
||||||
case 48 -> images[4][3];
|
case 48 -> mapSpriteBuffer[4][3];
|
||||||
case 49 -> images[4][4];
|
case 49 -> mapSpriteBuffer[4][4];
|
||||||
case 50 -> images[4][5];
|
case 50 -> mapSpriteBuffer[4][5];
|
||||||
case 51 -> images[4][6];
|
case 51 -> mapSpriteBuffer[4][6];
|
||||||
case 52 -> images[4][7];
|
case 52 -> mapSpriteBuffer[4][7];
|
||||||
case 53 -> images[4][8];
|
case 53 -> mapSpriteBuffer[4][8];
|
||||||
case 54 -> images[4][9];
|
case 54 -> mapSpriteBuffer[4][9];
|
||||||
case 55 -> images[4][10];
|
case 55 -> mapSpriteBuffer[4][10];
|
||||||
|
|
||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSprites() {
|
|
||||||
BufferedImage img = LoadSave.GetSpriteAtlas("sprites/PacMan-custom-spritemap-0-3.png");//473B78
|
|
||||||
for (int row = 0; row < 5; row++) {
|
|
||||||
for (int col = 0; col < 11; col++) {
|
|
||||||
images[row][col] = img.getSubimage(MAP_TILESIZE * col, MAP_TILESIZE * row, MAP_TILESIZE, MAP_TILESIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void draw(Graphics g) {
|
public void draw(Graphics g) {
|
||||||
for (int row = 0; row < mapData.length; row++) {
|
for (int row = 0; row < mapData.length; row++) {
|
||||||
for (int col = 0; col < mapData[row].length; col++) {
|
for (int col = 0; col < mapData[row].length; col++) {
|
||||||
@ -176,15 +169,30 @@ public class GameMap {
|
|||||||
if (col >= columns() || col < 0 ) return true;
|
if (col >= columns() || col < 0 ) return true;
|
||||||
MapTile mapTile = mapData[row][col];
|
MapTile mapTile = mapData[row][col];
|
||||||
boolean solid = mapTile.isSolid();
|
boolean solid = mapTile.isSolid();
|
||||||
log.debug("[{}][{}] is {} ({})", row, col, solid ? "solid" : " not solid", mapTile.getValue());
|
// log.debug("[{}][{}] is {} ({})", row, col, solid ? "solid" : " not solid", mapTile.getValue());
|
||||||
return solid;
|
return solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTileImage(Point destination) {
|
public boolean removeTileImage(Point screen) {
|
||||||
int row = screenToRow(destination);
|
if (screen == null || mapData == null) {
|
||||||
int col = screenToCol(destination);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int row = screenToRow(screen);
|
||||||
|
int col = screenToCol(screen);
|
||||||
|
|
||||||
|
if (row < 0 || row >= mapData.length || col < 0 || col >= mapData[0].length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MapTile tile = mapData[row][col];
|
MapTile tile = mapData[row][col];
|
||||||
if(tile.getValue() == 0) tile.setImage(null);
|
if (tile != null && tile.getValue() == 0 && tile.getImage() != null) {
|
||||||
|
tile.setImage(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int screenToCol(Point point) {
|
private static int screenToCol(Point point) {
|
||||||
@ -230,11 +238,12 @@ public class GameMap {
|
|||||||
return new Point(screen.x - OFFSET_X, screen.y - OFFSET_Y);
|
return new Point(screen.x - OFFSET_X, screen.y - OFFSET_Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int screenToCol(int screenX) {
|
public static int screenToCol(int screenX) {
|
||||||
return (screenX - OFFSET_X) / MAP_TILESIZE;
|
return (screenX - OFFSET_X) / MAP_TILESIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int screenToRow(int screenY) {
|
public static int screenToRow(int screenY) {
|
||||||
return (screenY - OFFSET_Y) / MAP_TILESIZE;
|
return (screenY - OFFSET_Y) / MAP_TILESIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,8 +50,7 @@ public class GhostManager {
|
|||||||
ghosts.add(blinky);
|
ghosts.add(blinky);
|
||||||
ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(),new ScatterToTopLeft(), image[2]));
|
ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(),new ScatterToTopLeft(), image[2]));
|
||||||
ghosts.add(new Ghost(ghostCollisionChecker,new InkyStrategy(blinky), new ScatterToBottomRight(), image[1]));
|
ghosts.add(new Ghost(ghostCollisionChecker,new InkyStrategy(blinky), new ScatterToBottomRight(), image[1]));
|
||||||
Ghost clyde = new Ghost(ghostCollisionChecker, new ClydeStrategy(), new ScatterToBottomLeft(), image[3]);
|
ghosts.add(new Ghost(ghostCollisionChecker, new ClydeStrategy(), new ScatterToBottomLeft(), image[3]));
|
||||||
ghosts.add(clyde);
|
|
||||||
setMode(GhostMode.CHASE);
|
setMode(GhostMode.CHASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ public class GhostManager {
|
|||||||
|
|
||||||
public void setMode(GhostMode mode) {
|
public void setMode(GhostMode mode) {
|
||||||
this.globalMode = mode;
|
this.globalMode = mode;
|
||||||
log.debug("Mode changed to {}", globalMode);
|
log.info("Mode changed to {}", globalMode);
|
||||||
for (Ghost g : ghosts) {
|
for (Ghost g : ghosts) {
|
||||||
g.setMode(mode);
|
g.setMode(mode);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package se.urmo.game.state;
|
package se.urmo.game.state;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import se.urmo.game.collision.CollisionChecker;
|
import se.urmo.game.collision.CollisionChecker;
|
||||||
import se.urmo.game.collision.GhostCollisionChecker;
|
import se.urmo.game.collision.GhostCollisionChecker;
|
||||||
import se.urmo.game.entities.BlinkyStrategy;
|
|
||||||
import se.urmo.game.entities.Ghost;
|
import se.urmo.game.entities.Ghost;
|
||||||
import se.urmo.game.entities.PacMan;
|
import se.urmo.game.entities.PacMan;
|
||||||
import se.urmo.game.main.Game;
|
import se.urmo.game.main.Game;
|
||||||
@ -12,14 +12,19 @@ import se.urmo.game.util.Direction;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class PlayingState implements GameState {
|
public class PlayingState implements GameState {
|
||||||
private final Game game;
|
private final Game game;
|
||||||
private final GameStateManager gameStateManager;
|
private final GameStateManager gameStateManager;
|
||||||
private final GhostManager ghostManager;
|
private final GhostManager ghostManager;
|
||||||
|
private final Font arcadeFont;
|
||||||
private PacMan pacman;
|
private PacMan pacman;
|
||||||
@Getter
|
@Getter
|
||||||
private GameMap map;
|
private GameMap map;
|
||||||
|
private int score;
|
||||||
|
private int lives = 3;
|
||||||
|
|
||||||
public PlayingState(Game game, GameStateManager gameStateManager) {
|
public PlayingState(Game game, GameStateManager gameStateManager) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
@ -27,12 +32,24 @@ public class PlayingState implements GameState {
|
|||||||
this.map = new GameMap();
|
this.map = new GameMap();
|
||||||
this.pacman = new PacMan(game, new CollisionChecker(map));
|
this.pacman = new PacMan(game, new CollisionChecker(map));
|
||||||
this.ghostManager = new GhostManager(new GhostCollisionChecker(map));
|
this.ghostManager = new GhostManager(new GhostCollisionChecker(map));
|
||||||
|
this.arcadeFont = loadArcadeFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
pacman.update();
|
pacman.update();
|
||||||
ghostManager.update(pacman, map);
|
ghostManager.update(pacman, map);
|
||||||
|
checkCollisions();
|
||||||
|
handleDots();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDots() {
|
||||||
|
Point pacmanScreenPos = pacman.getPosition();
|
||||||
|
boolean wasRemoved = map.removeTileImage(pacmanScreenPos);
|
||||||
|
|
||||||
|
if(wasRemoved){
|
||||||
|
score+=10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -40,6 +57,24 @@ public class PlayingState implements GameState {
|
|||||||
map.draw(g);
|
map.draw(g);
|
||||||
pacman.draw(g);
|
pacman.draw(g);
|
||||||
ghostManager.draw(g);
|
ghostManager.draw(g);
|
||||||
|
drawUI(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawUI(Graphics2D g) {
|
||||||
|
g.setColor(Color.WHITE);
|
||||||
|
g.setFont(arcadeFont);
|
||||||
|
|
||||||
|
// Score (above map, left)
|
||||||
|
g.drawString("Your Score", 48, 48);
|
||||||
|
g.drawString("" + score, 48, 72);
|
||||||
|
|
||||||
|
// Lives (below map, left)
|
||||||
|
for (int i = 1; i < lives; i++) {
|
||||||
|
g.drawImage(pacman.getLifeIcon(),
|
||||||
|
6 * GameMap.OFFSET_X - i * 24,
|
||||||
|
map.rows() * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y,
|
||||||
|
null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -60,6 +95,31 @@ public class PlayingState implements GameState {
|
|||||||
@Override
|
@Override
|
||||||
public void keyReleased(KeyEvent e) {
|
public void keyReleased(KeyEvent e) {
|
||||||
pacman.setMoving(false);
|
pacman.setMoving(false);
|
||||||
pacman.setDirection(Direction.NONE);
|
}
|
||||||
|
|
||||||
|
private void checkCollisions() {
|
||||||
|
for (Ghost ghost : ghostManager.getGhosts()) {
|
||||||
|
double dist = pacman.distanceTo(ghost.getPosition());
|
||||||
|
if (dist < GameMap.MAP_TILESIZE / 2.0) {
|
||||||
|
if (ghost.isFrightened()) {
|
||||||
|
// Pac-Man eats ghost
|
||||||
|
score += 200;
|
||||||
|
ghost.resetPosition();
|
||||||
|
} else {
|
||||||
|
// Pac-Man loses a life
|
||||||
|
lives--;
|
||||||
|
if(lives == 0)System.exit(1);
|
||||||
|
else pacman.resetPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Font loadArcadeFont() {
|
||||||
|
try (InputStream is = getClass().getResourceAsStream("/fonts/PressStart2P-Regular.ttf")) {
|
||||||
|
return Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(16f);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new Font("Monospaced", Font.BOLD, 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,21 @@ import java.io.InputStream;
|
|||||||
|
|
||||||
public class LoadSave {
|
public class LoadSave {
|
||||||
|
|
||||||
|
|
||||||
|
public static BufferedImage[][] loadSprites(final String fileName, final int rows, final int columns, int mapTilesize) {
|
||||||
|
BufferedImage spriteSheet = LoadSave.GetSpriteAtlas(fileName);
|
||||||
|
if(spriteSheet.getHeight() != rows * mapTilesize) throw new IllegalStateException("Wrong sprite row size");
|
||||||
|
if(spriteSheet.getWidth() != columns * mapTilesize) throw new IllegalStateException("Wrong sprite colum size");
|
||||||
|
BufferedImage[][] spriteBuffer = new BufferedImage[rows][columns];
|
||||||
|
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
for (int col = 0; col < columns; col++) {
|
||||||
|
spriteBuffer[row][col] = spriteSheet.getSubimage(mapTilesize * col, mapTilesize * row, mapTilesize, mapTilesize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spriteBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage GetSpriteAtlas(String fileName) {
|
public static BufferedImage GetSpriteAtlas(String fileName) {
|
||||||
BufferedImage img = null;
|
BufferedImage img = null;
|
||||||
InputStream is = LoadSave.class.getResourceAsStream("/" + fileName);
|
InputStream is = LoadSave.class.getResourceAsStream("/" + fileName);
|
||||||
|
|||||||
BIN
src/main/resources/fonts/PressStart2P-Regular.ttf
Normal file
BIN
src/main/resources/fonts/PressStart2P-Regular.ttf
Normal file
Binary file not shown.
Reference in New Issue
Block a user