Added visuals to RoundPhase
This commit is contained in:
@ -219,4 +219,8 @@ public class Ghost extends BaseAnimated {
|
|||||||
public Point getPosition() {
|
public Point getPosition() {
|
||||||
return new Point((int) position.x, (int) position.y);
|
return new Point((int) position.x, (int) position.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetModes() {
|
||||||
|
mode = GhostMode.CHASE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,6 +109,11 @@ public class PacMan extends BaseAnimated {
|
|||||||
position = startPosition;
|
position = startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
resetPosition();
|
||||||
|
aniIndex = 0; // reset animation to start
|
||||||
|
}
|
||||||
|
|
||||||
public Image getLifeIcon() {
|
public Image getLifeIcon() {
|
||||||
return spriteSheets[0][1];
|
return spriteSheets[0][1];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -253,7 +253,7 @@ public class GameMap {
|
|||||||
|
|
||||||
public long numberOfDots() {
|
public long numberOfDots() {
|
||||||
//return this.numberOfDots;
|
//return this.numberOfDots;
|
||||||
return 10;
|
return 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ public class GameStateManager {
|
|||||||
setState(GameStateType.PLAYING);
|
setState(GameStateType.PLAYING);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setState(GameStateType type) {
|
public void setState(GameStateType type) {
|
||||||
currentState = states.get(type);
|
currentState = states.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
package se.urmo.game.state;
|
package se.urmo.game.state;
|
||||||
|
|
||||||
public enum GameStateType {
|
public enum GameStateType {
|
||||||
PLAYING,
|
PLAYING, GAME_OVER,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import java.util.List;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GhostManager {
|
public class GhostManager {
|
||||||
public static final int SPRITE_SHEET_ROWS = 10;
|
|
||||||
public static final int MAX_SPRITE_FRAMES = 4;
|
public static final int MAX_SPRITE_FRAMES = 4;
|
||||||
public static final int BLINKY_ANIMATION = 0;
|
public static final int BLINKY_ANIMATION = 0;
|
||||||
public static final int PINKY_ANIMATION = 2;
|
public static final int PINKY_ANIMATION = 2;
|
||||||
@ -90,10 +89,12 @@ public class GhostManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setFrightMode() {
|
public void setFrightMode() {
|
||||||
this.setMode(GhostMode.FRIGHTENED);
|
setMode(GhostMode.FRIGHTENED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
|
phaseIndex = 0;
|
||||||
|
setMode(GhostMode.SCATTER);
|
||||||
ghosts.forEach(Ghost::resetPosition);
|
ghosts.forEach(Ghost::resetPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,20 +19,35 @@ import java.io.InputStream;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PlayingState implements GameState {
|
public class PlayingState implements GameState {
|
||||||
|
public static final int REMAINING_LIVES = 0;
|
||||||
private final Game game;
|
private final Game game;
|
||||||
private final GameStateManager gameStateManager;
|
private final GameStateManager gameStateManager;
|
||||||
private final GhostManager ghostManager;
|
|
||||||
private final Font arcadeFont;
|
private final Font arcadeFont;
|
||||||
|
|
||||||
|
// Core components
|
||||||
|
private final GhostManager ghostManager;
|
||||||
private final FruitManager fruitManager;
|
private final FruitManager fruitManager;
|
||||||
private final LevelManager levelManager;
|
private final LevelManager levelManager;
|
||||||
private final AnimationManager animationManager;
|
private final AnimationManager animationManager;
|
||||||
private PacMan pacman;
|
private PacMan pacman;
|
||||||
@Getter
|
@Getter
|
||||||
private GameMap map;
|
private GameMap map;
|
||||||
private int score;
|
|
||||||
|
// Durations (tune to taste)
|
||||||
|
private static final int READY_MS = 1500;
|
||||||
|
private static final int LEVEL_COMPLETE_MS= 1500;
|
||||||
|
private static final int LIFE_LOST_MS = 2000;
|
||||||
|
|
||||||
|
// Score/Lives
|
||||||
|
private int score = 0;
|
||||||
private int lives = 3;
|
private int lives = 3;
|
||||||
private int dotsEaten = 0;
|
private int dotsEaten = 0;
|
||||||
|
|
||||||
|
// Phase + timers
|
||||||
|
private RoundPhase phase = RoundPhase.PLAYING;
|
||||||
|
private long phaseStartMs = System.currentTimeMillis();
|
||||||
|
private boolean deathInProgress;
|
||||||
|
|
||||||
public PlayingState(Game game, GameStateManager gameStateManager) {
|
public PlayingState(Game game, GameStateManager gameStateManager) {
|
||||||
this.game = game;
|
this.game = game;
|
||||||
this.gameStateManager = gameStateManager;
|
this.gameStateManager = gameStateManager;
|
||||||
@ -48,12 +63,59 @@ public class PlayingState implements GameState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
animationManager.updateAll();
|
switch (phase) {
|
||||||
pacman.update();
|
case READY -> {
|
||||||
ghostManager.update(pacman, map);
|
// Freeze everything during READY
|
||||||
fruitManager.update(pacman, this);
|
if (phaseElapsed() >= READY_MS) {
|
||||||
checkCollisions();
|
setPhase(RoundPhase.PLAYING);
|
||||||
handleDots();
|
}
|
||||||
|
}
|
||||||
|
case PLAYING -> {
|
||||||
|
animationManager.updateAll();
|
||||||
|
pacman.update();
|
||||||
|
ghostManager.update(pacman, map);
|
||||||
|
fruitManager.update(pacman, this);
|
||||||
|
checkCollisions();
|
||||||
|
handleDots();
|
||||||
|
}
|
||||||
|
case LEVEL_COMPLETE -> {
|
||||||
|
// Freeze, then advance level
|
||||||
|
if (phaseElapsed() >= LEVEL_COMPLETE_MS) {
|
||||||
|
advanceLevel();
|
||||||
|
setPhase(RoundPhase.READY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case LIFE_LOST -> {
|
||||||
|
// Freeze, then reset round (keep dot state)
|
||||||
|
if (phaseElapsed() >= LIFE_LOST_MS) {
|
||||||
|
resetAfterLifeLost();
|
||||||
|
setPhase(RoundPhase.READY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetAfterLifeLost() {
|
||||||
|
pacman.reset(); // to start tile, direction stopped
|
||||||
|
ghostManager.reset(); // to house
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advanceLevel() {
|
||||||
|
levelManager.nextLevel();
|
||||||
|
map.reset();
|
||||||
|
ghostManager.reset();
|
||||||
|
fruitManager.reset();
|
||||||
|
pacman.reset();
|
||||||
|
dotsEaten = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPhase(RoundPhase next) {
|
||||||
|
phase = next;
|
||||||
|
phaseStartMs = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long phaseElapsed() {
|
||||||
|
return System.currentTimeMillis() - phaseStartMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDots() {
|
private void handleDots() {
|
||||||
@ -68,12 +130,7 @@ public class PlayingState implements GameState {
|
|||||||
fruitManager.dotEaten(dotsEaten);
|
fruitManager.dotEaten(dotsEaten);
|
||||||
score+=tile.getTileType().getScore();
|
score+=tile.getTileType().getScore();
|
||||||
if(dotsEaten == map.numberOfDots()){
|
if(dotsEaten == map.numberOfDots()){
|
||||||
levelManager.nextLevel();
|
setPhase(RoundPhase.LEVEL_COMPLETE);
|
||||||
map.reset();
|
|
||||||
ghostManager.reset();
|
|
||||||
fruitManager.reset();
|
|
||||||
pacman.resetPosition();
|
|
||||||
dotsEaten = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,6 +142,23 @@ public class PlayingState implements GameState {
|
|||||||
ghostManager.draw(g);
|
ghostManager.draw(g);
|
||||||
fruitManager.draw(g);
|
fruitManager.draw(g);
|
||||||
drawUI(g);
|
drawUI(g);
|
||||||
|
|
||||||
|
// Phase overlays
|
||||||
|
switch (phase) {
|
||||||
|
case READY -> drawCenterText(g, "READY!");
|
||||||
|
case LEVEL_COMPLETE -> drawCenterText(g, "LEVEL COMPLETE!");
|
||||||
|
case LIFE_LOST -> drawCenterText(g, "LIFE LOST");
|
||||||
|
default -> { /* no overlay */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawCenterText(Graphics2D g, String text) {
|
||||||
|
g.setFont(arcadeFont.deriveFont(18F));
|
||||||
|
g.setColor(Color.YELLOW);
|
||||||
|
var fm = g.getFontMetrics();
|
||||||
|
int cx = GameMap.OFFSET_X + map.columns() * GameMap.MAP_TILESIZE/2;
|
||||||
|
int cy = GameMap.OFFSET_Y + map.rows() * GameMap.MAP_TILESIZE/2;
|
||||||
|
g.drawString(text, cx - fm.stringWidth(text)/2, cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawUI(Graphics2D g) {
|
private void drawUI(Graphics2D g) {
|
||||||
@ -106,6 +180,7 @@ public class PlayingState implements GameState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void keyPressed(KeyEvent e) {
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (phase != RoundPhase.PLAYING) return;
|
||||||
switch (e.getKeyCode()) {
|
switch (e.getKeyCode()) {
|
||||||
case KeyEvent.VK_W -> move(Direction.UP);
|
case KeyEvent.VK_W -> move(Direction.UP);
|
||||||
case KeyEvent.VK_S -> move(Direction.DOWN);
|
case KeyEvent.VK_S -> move(Direction.DOWN);
|
||||||
@ -126,6 +201,7 @@ public class PlayingState implements GameState {
|
|||||||
|
|
||||||
private void checkCollisions() {
|
private void checkCollisions() {
|
||||||
for (Ghost ghost : ghostManager.getGhosts()) {
|
for (Ghost ghost : ghostManager.getGhosts()) {
|
||||||
|
if (deathInProgress) return; // guard
|
||||||
double dist = pacman.distanceTo(ghost.getPosition());
|
double dist = pacman.distanceTo(ghost.getPosition());
|
||||||
if (dist < GameMap.MAP_TILESIZE / 2.0) {
|
if (dist < GameMap.MAP_TILESIZE / 2.0) {
|
||||||
if (ghost.isFrightened()) {
|
if (ghost.isFrightened()) {
|
||||||
@ -134,15 +210,23 @@ public class PlayingState implements GameState {
|
|||||||
ghost.resetPosition();
|
ghost.resetPosition();
|
||||||
ghost.setMode(GhostMode.CHASE); // end frightend
|
ghost.setMode(GhostMode.CHASE); // end frightend
|
||||||
} else {
|
} else {
|
||||||
|
deathInProgress = true;
|
||||||
// Pac-Man loses a life
|
// Pac-Man loses a life
|
||||||
lives--;
|
lives--;
|
||||||
if(lives == 0)System.exit(1);
|
if(lives >= REMAINING_LIVES)
|
||||||
else pacman.resetPosition();
|
setPhase(RoundPhase.LIFE_LOST);
|
||||||
|
else
|
||||||
|
endGame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void endGame() {
|
||||||
|
setPhase(RoundPhase.GAME_OVER);
|
||||||
|
gameStateManager.setState(GameStateType.GAME_OVER);
|
||||||
|
}
|
||||||
|
|
||||||
private Font loadArcadeFont() {
|
private Font loadArcadeFont() {
|
||||||
try (InputStream is = getClass().getResourceAsStream("/fonts/PressStart2P-Regular.ttf")) {
|
try (InputStream is = getClass().getResourceAsStream("/fonts/PressStart2P-Regular.ttf")) {
|
||||||
return Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(16f);
|
return Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(16f);
|
||||||
|
|||||||
9
src/main/java/se/urmo/game/state/RoundPhase.java
Normal file
9
src/main/java/se/urmo/game/state/RoundPhase.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package se.urmo.game.state;
|
||||||
|
|
||||||
|
public enum RoundPhase {
|
||||||
|
READY, // "READY!" shown briefly before play starts
|
||||||
|
PLAYING, // normal gameplay
|
||||||
|
LEVEL_COMPLETE, // all dots eaten; freeze briefly then advance level
|
||||||
|
LIFE_LOST, // Pac-Man hit; freeze briefly then reset positions
|
||||||
|
GAME_OVER
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user