diff --git a/src/main/java/se/urmo/game/entities/BlinkyStrategy.java b/src/main/java/se/urmo/game/entities/BlinkyStrategy.java index 5f38846..5d8236e 100644 --- a/src/main/java/se/urmo/game/entities/BlinkyStrategy.java +++ b/src/main/java/se/urmo/game/entities/BlinkyStrategy.java @@ -6,7 +6,7 @@ import java.awt.Point; public class BlinkyStrategy implements GhostStrategy { @Override - public Point chooseTarget(PacMan pacman, GameMap map) { + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { return pacman.getTilePosition(); } } diff --git a/src/main/java/se/urmo/game/entities/ClydeStrategy.java b/src/main/java/se/urmo/game/entities/ClydeStrategy.java new file mode 100644 index 0000000..7db2c14 --- /dev/null +++ b/src/main/java/se/urmo/game/entities/ClydeStrategy.java @@ -0,0 +1,21 @@ +package se.urmo.game.entities; + +import se.urmo.game.map.GameMap; + +import java.awt.Point; + +public class ClydeStrategy implements GhostStrategy { + @Override + public Point chooseTarget(Ghost clyde, PacMan pacman, GameMap map) { + Point pacTile = pacman.getTilePosition(); + Point clydeTile = clyde.getPosition(); // ghost’s current tile + + double distance = pacTile.distance(clydeTile); + + if (distance > 8) { + return pacTile; // chase Pac-Man + } else { + return new Point(0, map.getHeight() - 1); // retreat to corner + } + } +} diff --git a/src/main/java/se/urmo/game/entities/Ghost.java b/src/main/java/se/urmo/game/entities/Ghost.java index 4634f24..d795cce 100644 --- a/src/main/java/se/urmo/game/entities/Ghost.java +++ b/src/main/java/se/urmo/game/entities/Ghost.java @@ -1,5 +1,6 @@ package se.urmo.game.entities; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.GhostCollisionChecker; import se.urmo.game.map.GameMap; @@ -11,8 +12,6 @@ import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.awt.image.BufferedImage; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -29,6 +28,7 @@ public class Ghost { private final GhostCollisionChecker collisionChecker; private final GhostStrategy chaseStrategy; + @Getter private Point position; private boolean moving = true; @@ -81,7 +81,7 @@ public class Ghost { prevDirection = direction; direction = chooseDirection( prioritize(collisionChecker.calculateDirectionAlternatives(position)), - currentStrategy.chooseTarget(pacman, map)); + currentStrategy.chooseTarget(this, pacman, map)); log.info("selecting direction {}", direction); } @@ -167,4 +167,5 @@ public class Ghost { case EATEN -> currentStrategy = null; } } + } diff --git a/src/main/java/se/urmo/game/entities/GhostStrategy.java b/src/main/java/se/urmo/game/entities/GhostStrategy.java index b8339fe..53e26e8 100644 --- a/src/main/java/se/urmo/game/entities/GhostStrategy.java +++ b/src/main/java/se/urmo/game/entities/GhostStrategy.java @@ -5,5 +5,5 @@ import se.urmo.game.map.GameMap; import java.awt.Point; public interface GhostStrategy { - Point chooseTarget(PacMan pacman, GameMap map); + Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map); } diff --git a/src/main/java/se/urmo/game/entities/InkyStrategy.java b/src/main/java/se/urmo/game/entities/InkyStrategy.java new file mode 100644 index 0000000..7b874f6 --- /dev/null +++ b/src/main/java/se/urmo/game/entities/InkyStrategy.java @@ -0,0 +1,42 @@ +package se.urmo.game.entities; + +import se.urmo.game.map.GameMap; +import se.urmo.game.util.Direction; + +import java.awt.Point; + +public class InkyStrategy implements GhostStrategy { + private final Ghost blinky; + + public InkyStrategy(Ghost blinky) { + this.blinky = blinky; + } + @Override + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { + // 1. Two tiles ahead of pacman + Direction pacmanDir = pacman.getDirection(); + Point pacmanPos = pacman.getTilePosition(); + Point ahead = switch (pacmanDir){ + 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 DOWN -> new Point(pacmanPos.x, pacmanPos.y + 8 * GameMap.MAP_TILESIZE); + case UP -> new Point(pacmanPos.x, pacmanPos.y - 8 * GameMap.MAP_TILESIZE); + case NONE -> pacmanPos; + }; + + // 2. Vector from blinky to that tile + Point blinkyPos = blinky.getPosition(); + int vx = ahead.x - blinkyPos.x; + int vy = ahead.y - blinkyPos.y; + + // 3. Double the vector + Point target = new Point( + blinkyPos.x + 2 * vx, + blinkyPos.y + 2 * vy + ); + + return target; + // (Optional: clamp inside map boundaries) + //return map.clampToBounds(target); + } +} diff --git a/src/main/java/se/urmo/game/entities/PinkyStrategy.java b/src/main/java/se/urmo/game/entities/PinkyStrategy.java index fdde5c2..bc16a42 100644 --- a/src/main/java/se/urmo/game/entities/PinkyStrategy.java +++ b/src/main/java/se/urmo/game/entities/PinkyStrategy.java @@ -7,14 +7,14 @@ import java.awt.Point; public class PinkyStrategy implements GhostStrategy{ @Override - public Point chooseTarget(PacMan pacman, GameMap map) { + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { Direction pacmanDir = pacman.getDirection(); Point pacmanPos = pacman.getTilePosition(); return switch (pacmanDir){ - case RIGHT -> new Point(pacmanPos.x + 4 * GameMap.MAP_TILESIZE, pacmanPos.y); - case LEFT -> new Point(pacmanPos.x - 4 * GameMap.MAP_TILESIZE, pacmanPos.y); - case DOWN -> new Point(pacmanPos.x, pacmanPos.y + 4 * GameMap.MAP_TILESIZE); - case UP -> new Point(pacmanPos.x, pacmanPos.y - 4 * GameMap.MAP_TILESIZE); + 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 DOWN -> new Point(pacmanPos.x, pacmanPos.y + 8 * GameMap.MAP_TILESIZE); + case UP -> new Point(pacmanPos.x, pacmanPos.y - 8 * GameMap.MAP_TILESIZE); case NONE -> pacmanPos; }; } diff --git a/src/main/java/se/urmo/game/entities/ScatterToBottomLeft.java b/src/main/java/se/urmo/game/entities/ScatterToBottomLeft.java new file mode 100644 index 0000000..b041a45 --- /dev/null +++ b/src/main/java/se/urmo/game/entities/ScatterToBottomLeft.java @@ -0,0 +1,12 @@ +package se.urmo.game.entities; + +import se.urmo.game.map.GameMap; + +import java.awt.Point; + +public class ScatterToBottomLeft implements GhostStrategy { + @Override + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { + return new Point(GameMap.OFFSET_X, (map.columns() * GameMap.MAP_TILESIZE) + GameMap.OFFSET_Y); + } +} diff --git a/src/main/java/se/urmo/game/entities/ScatterToBottomRight.java b/src/main/java/se/urmo/game/entities/ScatterToBottomRight.java new file mode 100644 index 0000000..d1abdd5 --- /dev/null +++ b/src/main/java/se/urmo/game/entities/ScatterToBottomRight.java @@ -0,0 +1,12 @@ +package se.urmo.game.entities; + +import se.urmo.game.map.GameMap; + +import java.awt.Point; + +public class ScatterToBottomRight implements GhostStrategy { + @Override + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { + return new Point(map.getHeight() - 1, map.getHeight() - 1); + } +} diff --git a/src/main/java/se/urmo/game/entities/ScatterToTopLeft.java b/src/main/java/se/urmo/game/entities/ScatterToTopLeft.java new file mode 100644 index 0000000..5866dc5 --- /dev/null +++ b/src/main/java/se/urmo/game/entities/ScatterToTopLeft.java @@ -0,0 +1,13 @@ +package se.urmo.game.entities; + +import se.urmo.game.map.GameMap; + +import java.awt.Point; + +public class ScatterToTopLeft implements GhostStrategy { + + @Override + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { + return new Point(GameMap.OFFSET_X, GameMap.OFFSET_Y); + } +} diff --git a/src/main/java/se/urmo/game/entities/ScatterToTopRight.java b/src/main/java/se/urmo/game/entities/ScatterToTopRight.java index a569bce..264335c 100644 --- a/src/main/java/se/urmo/game/entities/ScatterToTopRight.java +++ b/src/main/java/se/urmo/game/entities/ScatterToTopRight.java @@ -6,7 +6,7 @@ import java.awt.Point; public class ScatterToTopRight implements GhostStrategy{ @Override - public Point chooseTarget(PacMan pacman, GameMap map) { - return new Point((map.columns() -1) * GameMap.MAP_TILESIZE, 0); + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { + return new Point((map.columns() -1) * GameMap.MAP_TILESIZE + GameMap.OFFSET_X, 0); } } diff --git a/src/main/java/se/urmo/game/map/GameMap.java b/src/main/java/se/urmo/game/map/GameMap.java index d01248e..9fc1c15 100644 --- a/src/main/java/se/urmo/game/map/GameMap.java +++ b/src/main/java/se/urmo/game/map/GameMap.java @@ -195,6 +195,9 @@ public class GameMap { } public int columns() { - return (GamePanel.SCREEN_WIDTH - 2 * OFFSET_X) / MAP_TILESIZE; + return mapData[0].length; + } + public int rows() { + return mapData.length; } } diff --git a/src/main/java/se/urmo/game/state/GhostManager.java b/src/main/java/se/urmo/game/state/GhostManager.java index aeeda53..eda3485 100644 --- a/src/main/java/se/urmo/game/state/GhostManager.java +++ b/src/main/java/se/urmo/game/state/GhostManager.java @@ -4,10 +4,15 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.GhostCollisionChecker; import se.urmo.game.entities.BlinkyStrategy; +import se.urmo.game.entities.ClydeStrategy; import se.urmo.game.entities.Ghost; import se.urmo.game.entities.GhostMode; +import se.urmo.game.entities.InkyStrategy; import se.urmo.game.entities.PacMan; import se.urmo.game.entities.PinkyStrategy; +import se.urmo.game.entities.ScatterToBottomLeft; +import se.urmo.game.entities.ScatterToBottomRight; +import se.urmo.game.entities.ScatterToTopLeft; import se.urmo.game.entities.ScatterToTopRight; import se.urmo.game.map.GameMap; import se.urmo.game.util.LoadSave; @@ -39,11 +44,14 @@ public class GhostManager { public GhostManager(GhostCollisionChecker ghostCollisionChecker) { loadAnimation(); + // Create ghosts with their strategies - ghosts.add(new Ghost(ghostCollisionChecker, new BlinkyStrategy(),new ScatterToTopLeft(), image[0])); - ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(),new ScatterToTopRight(), image[1])); - //ghosts.add(new Ghost(240, 200, new InkyStrategy(), loader.getSprite("inky"))); - //ghosts.add(new Ghost(260, 200, new ClydeStrategy(), loader.getSprite("clyde"))); + Ghost blinky = new Ghost(ghostCollisionChecker, new BlinkyStrategy(), new ScatterToTopRight(), image[0]); + ghosts.add(blinky); + ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(),new ScatterToTopLeft(), image[2])); + 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(clyde); setMode(GhostMode.CHASE); } diff --git a/src/main/java/se/urmo/game/state/ScatterToTopLeft.java b/src/main/java/se/urmo/game/state/ScatterToTopLeft.java deleted file mode 100644 index a090b1f..0000000 --- a/src/main/java/se/urmo/game/state/ScatterToTopLeft.java +++ /dev/null @@ -1,15 +0,0 @@ -package se.urmo.game.state; - -import se.urmo.game.entities.GhostStrategy; -import se.urmo.game.entities.PacMan; -import se.urmo.game.map.GameMap; - -import java.awt.Point; - -public class ScatterToTopLeft implements GhostStrategy { - - @Override - public Point chooseTarget(PacMan pacman, GameMap map) { - return new Point(1, 0); - } -}