diff --git a/src/main/java/se/urmo/game/entities/ghost/Ghost.java b/src/main/java/se/urmo/game/entities/ghost/Ghost.java index 8e64253..cb16644 100644 --- a/src/main/java/se/urmo/game/entities/ghost/Ghost.java +++ b/src/main/java/se/urmo/game/entities/ghost/Ghost.java @@ -4,6 +4,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.GhostCollisionChecker; import se.urmo.game.entities.BaseAnimated; +import se.urmo.game.entities.ghost.strategy.EatenStrategy; import se.urmo.game.entities.ghost.strategy.FearStrategy; import se.urmo.game.entities.ghost.strategy.GhostStrategy; import se.urmo.game.entities.pacman.PacMan; @@ -37,6 +38,8 @@ public class Ghost extends BaseAnimated { private final BufferedImage[] fearAnimation; private final BufferedImage[] baseAnimation; private final LevelManager levelManager; + private final BufferedImage[] eatenAnimation; + private double speed; private MyPoint position; private final GhostStrategy scaterStrategy; @@ -50,6 +53,7 @@ public class Ghost extends BaseAnimated { private boolean isBlinking = false; @Setter private boolean frozen; + private GhostStrategy eatenStrategy; public Ghost(GhostCollisionChecker collisionChecker, GhostStrategy strategy, GhostStrategy scaterStrategy, int animation, LevelManager levelManager) { @@ -59,6 +63,7 @@ public class Ghost extends BaseAnimated { this.scaterStrategy = scaterStrategy; this.baseAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(animation); this.fearAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(8); + this.eatenAnimation = SpriteSheetManager.get(SpriteLocation.GHOST).getAnimation(9); position = new MyPoint( 13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X + ((double) GameMap.MAP_TILESIZE / 2), @@ -67,6 +72,8 @@ public class Ghost extends BaseAnimated { this.currentStrategy = chaseStrategy; this.animation = baseAnimation; this.levelManager = levelManager; + this.eatenStrategy = new EatenStrategy(); + this.speed = BASE_SPEED * levelManager.getGhostSpeed(); } public void draw(Graphics g) { @@ -83,6 +90,11 @@ public class Ghost extends BaseAnimated { if (mode == GhostMode.FRIGHTENED) { updateInFrightendMode(); } + if (mode == GhostMode.EATEN) { + if (position.asPoint().distance(startPos.asPoint()) < 10) { + setMode(GhostMode.CHASE); + } + } updatePosition(pacman, map); } @@ -98,11 +110,12 @@ public class Ghost extends BaseAnimated { } private void updatePosition(PacMan pacman, GameMap map) { - if (map.isAligned(new Point((int) position.x, (int) position.y))) { + if (map.isAligned(position.asPoint())) { prevDirection = direction; direction = chooseDirection( - prioritize(collisionChecker.calculateDirectionAlternatives(position)), + prioritizeDirections(collisionChecker.calculateDirectionAlternatives(position)), currentStrategy.chooseTarget(this, pacman, map)); + log.debug("Ghost moving to {}", direction); } moveTo(getNewPosition()); @@ -122,7 +135,7 @@ public class Ghost extends BaseAnimated { } private double getSpeed() { - return BASE_SPEED * levelManager.getGhostSpeed(); + return speed; } private void moveTo(MyPoint newPosition) { @@ -140,7 +153,7 @@ public class Ghost extends BaseAnimated { * @param directions a list of potential movement directions * @return a map where keys are valid directions and values are their associated priority */ - private Map prioritize(List directions) { + private Map prioritizeDirections(List directions) { return directions.stream() .filter(d -> d != Direction.NONE) .collect(Collectors.toMap( @@ -186,6 +199,7 @@ public class Ghost extends BaseAnimated { best = d; } } + log.debug("Ghost comming from {}, choosing {}, from {} (dist={})", prevDirection, best, directions, bestDist); return best; } @@ -195,6 +209,7 @@ public class Ghost extends BaseAnimated { case CHASE -> { animation = baseAnimation; currentStrategy = chaseStrategy; + speed = BASE_SPEED * levelManager.getGhostSpeed(); } case SCATTER -> currentStrategy = scaterStrategy; case FRIGHTENED -> { @@ -203,7 +218,11 @@ public class Ghost extends BaseAnimated { animation = fearAnimation; currentStrategy = fearStrategy; } - case EATEN -> currentStrategy = null; + case EATEN -> { + //speed = 1.2; + currentStrategy = eatenStrategy; + animation = eatenAnimation; + } } } @@ -211,8 +230,9 @@ public class Ghost extends BaseAnimated { return mode == GhostMode.FRIGHTENED; } - public void resetPosition() { + public void reset() { position = startPos; + this.speed = BASE_SPEED * levelManager.getGhostSpeed(); } public Point getPosition() { @@ -223,4 +243,7 @@ public class Ghost extends BaseAnimated { mode = GhostMode.CHASE; } + public boolean isEaten() { + return mode == GhostMode.EATEN; + } } diff --git a/src/main/java/se/urmo/game/entities/ghost/strategy/EatenStrategy.java b/src/main/java/se/urmo/game/entities/ghost/strategy/EatenStrategy.java new file mode 100644 index 0000000..58ee49c --- /dev/null +++ b/src/main/java/se/urmo/game/entities/ghost/strategy/EatenStrategy.java @@ -0,0 +1,16 @@ +package se.urmo.game.entities.ghost.strategy; + +import se.urmo.game.entities.ghost.Ghost; +import se.urmo.game.entities.pacman.PacMan; +import se.urmo.game.map.GameMap; + +import java.awt.Point; + +public class EatenStrategy implements GhostStrategy { + @Override + public Point chooseTarget(Ghost ghost, PacMan pacman, GameMap map) { + return new Point( + 13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X + (GameMap.MAP_TILESIZE / 2), + 4 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2)); + } +} diff --git a/src/main/java/se/urmo/game/main/GhostManager.java b/src/main/java/se/urmo/game/main/GhostManager.java index 098804c..92c82a9 100644 --- a/src/main/java/se/urmo/game/main/GhostManager.java +++ b/src/main/java/se/urmo/game/main/GhostManager.java @@ -6,12 +6,6 @@ import se.urmo.game.collision.GhostCollisionChecker; import se.urmo.game.entities.ghost.Ghost; import se.urmo.game.entities.ghost.GhostMode; import se.urmo.game.entities.ghost.strategy.BlinkyStrategy; -import se.urmo.game.entities.ghost.strategy.ClydeStrategy; -import se.urmo.game.entities.ghost.strategy.InkyStrategy; -import se.urmo.game.entities.ghost.strategy.PinkyStrategy; -import se.urmo.game.entities.ghost.strategy.ScatterToBottomLeft; -import se.urmo.game.entities.ghost.strategy.ScatterToBottomRight; -import se.urmo.game.entities.ghost.strategy.ScatterToTopLeft; import se.urmo.game.entities.ghost.strategy.ScatterToTopRight; import se.urmo.game.entities.pacman.PacMan; import se.urmo.game.map.GameMap; @@ -48,9 +42,9 @@ public class GhostManager { // Create ghosts with their strategies Ghost blinky = new Ghost(ghostCollisionChecker, new BlinkyStrategy(), new ScatterToTopRight(), BLINKY_ANIMATION, levelManager); ghosts.add(blinky); - ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(), new ScatterToTopLeft(), PINKY_ANIMATION, levelManager)); - ghosts.add(new Ghost(ghostCollisionChecker,new InkyStrategy(blinky), new ScatterToBottomRight(), INKY_ANIMATION, levelManager)); - ghosts.add(new Ghost(ghostCollisionChecker, new ClydeStrategy(), new ScatterToBottomLeft(), CLYDE_ANIMATION, levelManager)); +// ghosts.add(new Ghost(ghostCollisionChecker, new PinkyStrategy(), new ScatterToTopLeft(), PINKY_ANIMATION, levelManager)); +// ghosts.add(new Ghost(ghostCollisionChecker,new InkyStrategy(blinky), new ScatterToBottomRight(), INKY_ANIMATION, levelManager)); +// ghosts.add(new Ghost(ghostCollisionChecker, new ClydeStrategy(), new ScatterToBottomLeft(), CLYDE_ANIMATION, levelManager)); ghosts.forEach(animationManager::register); @@ -97,10 +91,14 @@ public class GhostManager { public void reset() { phaseIndex = 0; setMode(GhostMode.SCATTER); - ghosts.forEach(Ghost::resetPosition); + ghosts.forEach(Ghost::reset); } public void setFrozen(boolean frozen) { this.ghosts.forEach(ghost -> ghost.setFrozen(frozen)); } + + public int isFrightened() { + return (int) ghosts.stream().filter(Ghost::isFrightened).count(); + } } diff --git a/src/main/java/se/urmo/game/state/PlayingState.java b/src/main/java/se/urmo/game/state/PlayingState.java index 6ea7981..eec60a9 100644 --- a/src/main/java/se/urmo/game/state/PlayingState.java +++ b/src/main/java/se/urmo/game/state/PlayingState.java @@ -52,6 +52,7 @@ public class PlayingState implements GameState { private RoundPhase phase = RoundPhase.PLAYING; private long phaseStartMs = System.currentTimeMillis(); private boolean deathInProgress; + private int frightMultiplier; public PlayingState(GameStateManager gameStateManager, GameOverState gameOverState) { this.gameStateManager = gameStateManager; @@ -129,6 +130,7 @@ public class PlayingState implements GameState { boolean wasRemoved = map.removeTileImage(pacmanScreenPos); if (wasRemoved && tile.getTileType() == TileType.LARGE_PELLET) { ghostManager.setFrightMode(); + frightMultiplier = 1; } if (wasRemoved) { dotsEaten++; @@ -211,11 +213,12 @@ public class PlayingState implements GameState { //if(overlap(pacman, ghost) double dist = pacman.distanceTo(ghost.getPosition()); if (dist < GameMap.MAP_TILESIZE / 2.0) { + if (ghost.isEaten()) return; if (ghost.isFrightened()) { // Pac-Man eats ghost - score += 200; - ghost.resetPosition(); - ghost.setMode(GhostMode.CHASE); // end frightend + score += 200 * (1 << (ghostManager.getGhosts().size() - ghostManager.isFrightened())); + + ghost.setMode(GhostMode.EATEN); } else { ghostManager.setFrozen(true); pacman.startDeathAnimation(); diff --git a/src/main/java/se/urmo/game/util/MyPoint.java b/src/main/java/se/urmo/game/util/MyPoint.java index 87dc677..1f60e90 100644 --- a/src/main/java/se/urmo/game/util/MyPoint.java +++ b/src/main/java/se/urmo/game/util/MyPoint.java @@ -1,5 +1,7 @@ package se.urmo.game.util; +import java.awt.Point; + public class MyPoint { public final double x; public final double y; @@ -9,6 +11,10 @@ public class MyPoint { this.y = y; } + public Point asPoint() { + return new Point((int) x, (int) y); + } + @Override public String toString() { return "MyPoint{" +