package se.urmo.game.entities; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import se.urmo.game.collision.CollisionChecker; import se.urmo.game.util.Direction; import se.urmo.game.main.Game; import se.urmo.game.map.GameMap; import se.urmo.game.util.LoadSave; import se.urmo.game.util.MiscUtil; import java.awt.*; import java.awt.image.BufferedImage; import java.util.Arrays; @Slf4j public class PacMan { public static final int PACMAN_SIZE = 32; public static final int PACMAN_OFFSET = PACMAN_SIZE / 2; private static final int COLLISION_BOX_SIZE = 16; private static final int COLLISION_BOX_OFFSET = (PACMAN_SIZE - COLLISION_BOX_SIZE) / 2; private final Game game; private final Point startPosition; private int aniTick = 0; private int aniIndex = 0; private static final int ANIMATION_UPDATE_FREQUENCY = 10; private int speed = 1; @Setter private boolean moving; private final BufferedImage[][] movmentImages = new BufferedImage[4][4]; @Getter private Point position; private static final BufferedImage COLLISION_BOX = MiscUtil.createOutlinedBox(COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, Color.yellow, 2); private final CollisionChecker collisionChecker; @Setter @Getter private Direction direction = Direction.NONE; public PacMan(Game game, CollisionChecker collisionChecker) { this.game = game; this.collisionChecker = collisionChecker; this.position = new Point( 26 * GameMap.MAP_TILESIZE + GameMap.OFFSET_X, 13 * GameMap.MAP_TILESIZE + GameMap.OFFSET_Y + (GameMap.MAP_TILESIZE / 2)); this.startPosition = this.position; loadAnimation(); } private void loadAnimation() { BufferedImage[][] image = new BufferedImage[3][4]; BufferedImage img = LoadSave.GetSpriteAtlas("sprites/PacManAssets-PacMan.png"); for (int row = 0; row < 3; row++) { for (int col = 0; col < 4; col++) { image[row][col] = img.getSubimage(PACMAN_SIZE * col, PACMAN_SIZE * row, PACMAN_SIZE, PACMAN_SIZE); } } movmentImages[Direction.RIGHT.ordinal()] = image[0]; movmentImages[Direction.LEFT.ordinal()] = Arrays.stream(image[0]) .map(i -> LoadSave.rotate(i, 180)) .toArray(BufferedImage[]::new); movmentImages[Direction.DOWN.ordinal()] = Arrays.stream(image[0]) .map(i -> LoadSave.rotate(i, 90)) .toArray(BufferedImage[]::new); movmentImages[Direction.UP.ordinal()] = Arrays.stream(image[0]) .map(i -> LoadSave.rotate(i, 270)) .toArray(BufferedImage[]::new); } public void draw(Graphics g) { g.drawImage( movmentImages[direction==Direction.NONE ? 0 : direction.ordinal()][aniIndex], position.x - PACMAN_OFFSET, position.y - PACMAN_OFFSET, PACMAN_SIZE, PACMAN_SIZE, null); g.drawImage(COLLISION_BOX, position.x - COLLISION_BOX_OFFSET, position.y - COLLISION_BOX_OFFSET, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE, null); //g.setColor(Color.BLUE); //g.fillRect(position.x-1, position.y-1, 3, 3); } public void update() { updateAnimationTick(); if(moving) { Point newPosition = switch (direction) { case RIGHT -> new Point(position.x + speed, position.y); case LEFT -> new Point(position.x - speed, position.y); case UP -> new Point(position.x, position.y - speed); case DOWN -> new Point(position.x, position.y + speed); default -> throw new IllegalStateException("Unexpected value: " + direction); }; log.debug("At: {},trying to move {} to {}", position, direction.name(), newPosition); Point destination = collisionChecker.getValidDestination(direction, newPosition, COLLISION_BOX_SIZE, COLLISION_BOX_SIZE); if (destination != null) { position = destination; } } } private void updateAnimationTick() { if (moving) { aniTick++; if (aniTick >= ANIMATION_UPDATE_FREQUENCY) { aniTick = 0; aniIndex++; if (aniIndex >= 4) { aniIndex = 0; } } } } public double distanceTo(Point point) { return position.distance(point); } public void loseLife() { } public void resetPosition() { position = startPosition; } public Image getLifeIcon() { return movmentImages[0][1]; } }