package se.urmo.game.entities.ghost; import lombok.Getter; 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.mode.ChaseGhostMode; import se.urmo.game.entities.ghost.mode.EatenGhostMode; import se.urmo.game.entities.ghost.mode.FrightenedGhostMode; import se.urmo.game.entities.ghost.mode.FrozenGhostMode; import se.urmo.game.entities.ghost.mode.GhostMode; import se.urmo.game.entities.ghost.mode.GhostState; import se.urmo.game.entities.ghost.mode.HouseGhostMode; import se.urmo.game.entities.ghost.mode.ScatterGhostMode; import se.urmo.game.entities.ghost.strategy.GhostStrategy; import se.urmo.game.entities.pacman.PacMan; import se.urmo.game.main.GhostManager; import se.urmo.game.main.LevelManager; import se.urmo.game.map.GameMap; import se.urmo.game.util.Direction; import se.urmo.game.util.MyPoint; import java.awt.Graphics; import java.util.EnumMap; import java.util.Map; @Slf4j public class Ghost extends BaseAnimated { public static final double BASE_SPEED = 0.40; public static final int GHOST_SIZE = 32; private static final int ANIMATION_UPDATE_FREQUENCY = 25; @Getter private final GhostCollisionChecker collisionChecker; @Getter private final int animation; @Getter private final LevelManager levelManager; @Getter private final GhostStrategy scaterStrategy; private GhostState currentState; private final Map states = new EnumMap<>(GhostMode.class); // Movement-related state @Getter @Setter protected MyPoint position; @Getter @Setter protected Direction direction; @Getter @Setter protected Direction prevDirection; @Getter private static final MyPoint startPosition = new MyPoint( GameMap.colToScreen(13) + ((double) GameMap.MAP_TILESIZE / 2), GameMap.rowToScreen(12) + ((double) GameMap.MAP_TILESIZE / 2)); @Getter private static final MyPoint houseEntrance = new MyPoint( GameMap.colToScreen(13) + ((double) GameMap.MAP_TILESIZE / 2), GameMap.rowToScreen(10) + ((double) GameMap.MAP_TILESIZE / 2)); public Ghost(GhostCollisionChecker collisionChecker, GhostStrategy chaseStrategy, GhostStrategy scaterStrategy, int animation, LevelManager levelManager) { super(ANIMATION_UPDATE_FREQUENCY, GhostManager.MAX_SPRITE_FRAMES); this.collisionChecker = collisionChecker; this.scaterStrategy = scaterStrategy; this.animation = animation; this.levelManager = levelManager; this.position = startPosition; states.put(GhostMode.CHASE, new ChaseGhostMode(this, chaseStrategy)); states.put(GhostMode.SCATTER, new ScatterGhostMode(this, scaterStrategy)); states.put(GhostMode.FRIGHTENED, new FrightenedGhostMode(this)); states.put(GhostMode.EATEN, new EatenGhostMode(this)); states.put(GhostMode.FROZEN, new FrozenGhostMode(this)); states.put(GhostMode.HOUSE, new HouseGhostMode(this)); } public void draw(Graphics g) { g.drawImage( currentState.getAnimation()[aniIndex], (int) position.x - GHOST_SIZE / 2, (int) position.y - GHOST_SIZE / 2, GHOST_SIZE, GHOST_SIZE, null); // g.setColor(Color.YELLOW); // g.fillRect((int) Ghost.startPosition.x, (int) Ghost.startPosition.y, 2, 2); // g.fillRect((int) Ghost.houseEntrance.x, (int) Ghost.houseEntrance.y, 2, 2); } public void update(PacMan pacman, GameMap map) { currentState.update(this, pacman, map); } public void setMode(GhostMode mode) { GhostMode currentMode = currentMode(); if (currentMode == null || mode.ordinal() < currentMode.ordinal()) { // only if new mode has higher prio currentState = states.get(mode); } } /** * Used by a state itself to request a transition to any other state * This bypasses the priority system since the state itself is requesting the change */ public void requestModeChange(GhostMode mode) { currentState = states.get(mode); } public boolean isFrightened() { return states.get(GhostMode.FRIGHTENED) == currentState; } public boolean isEaten() { return states.get(GhostMode.EATEN) == currentState; } public GhostMode currentMode() { return states.entrySet().stream() .filter(s -> s.getValue() == currentState) .map(Map.Entry::getKey) .findFirst() .orElse(null); } public void reset() { currentState = states.get(GhostMode.CHASE); ((ChaseGhostMode) currentState).resetPosition(); } }