diff --git a/android/assets/high_scores_background.png b/android/assets/high_scores_background.png new file mode 100644 index 0000000..646d514 Binary files /dev/null and b/android/assets/high_scores_background.png differ diff --git a/core/src/com/me/pacman/Assets.java b/core/src/com/me/pacman/Assets.java index ce9f425..fc0b6e8 100644 --- a/core/src/com/me/pacman/Assets.java +++ b/core/src/com/me/pacman/Assets.java @@ -18,6 +18,7 @@ public class Assets { private Texture levelBackground; private Texture levelWinBackground; private Texture menuBackground; + private Texture highScoresBackground; public TextureRegion[][] font; public TextureRegion[][] level; @@ -44,6 +45,7 @@ public class Assets { manager.load("level_background.png", Texture.class); manager.load("level_background_win.png", Texture.class); manager.load("menu_background.png", Texture.class); + manager.load("high_scores_background.png", Texture.class); manager.load("logo.png", Texture.class); manager.load("sprites/font.png", Texture.class); @@ -75,6 +77,7 @@ public class Assets { levelBackground = manager.get("level_background.png", Texture.class); levelWinBackground = manager.get("level_background_win.png", Texture.class); menuBackground = manager.get("menu_background.png", Texture.class); + highScoresBackground = manager.get("high_scores_background.png", Texture.class); // cache our texture regions font = TextureRegion.split(manager.get("sprites/font.png", Texture.class), 8, 8); @@ -114,6 +117,8 @@ public class Assets { return menuBackground; } + public Texture getHighScoresBackground() { return highScoresBackground; } + public Texture getLogo() { return manager.get("logo.png", Texture.class); } diff --git a/core/src/com/me/pacman/FontRenderer.java b/core/src/com/me/pacman/FontRenderer.java index 75f1e94..0ec9a11 100644 --- a/core/src/com/me/pacman/FontRenderer.java +++ b/core/src/com/me/pacman/FontRenderer.java @@ -7,8 +7,8 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion; public class FontRenderer { - private static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz0123456789!?()[]<>$&*:#^~-_/\\".toCharArray(); - private static final int[] CHAR_TO_INDEX = new int[256]; + public static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz0123456789!?()[]<>$&*:#^~-_/\\".toCharArray(); + public static final int[] CHAR_TO_INDEX = new int[256]; private Color color = Color.WHITE; private TextureRegion[][] sprites; diff --git a/core/src/com/me/pacman/HighScores.java b/core/src/com/me/pacman/HighScores.java new file mode 100644 index 0000000..192113a --- /dev/null +++ b/core/src/com/me/pacman/HighScores.java @@ -0,0 +1,67 @@ +package com.me.pacman; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.Array; + +/** + * A simple class for retrieving and saving high scores to persistent storage. + * + * Scores are saved in a simple text file + */ +public class HighScores { + + public static final String FILE = "scores.dat"; + + private FileHandle file; + + private Array scores = new Array<>(10); + + public HighScores() { + file = Gdx.files.local(FILE); + loadScores(); + } + + private void loadScores() { + if (!file.exists()) { + return; + } + + String data = file.readString(); + String[] lines = data.split("\n"); + + for (String line : lines) { + String[] parts = line.split("\t"); + scores.add(new Score(parts[0], Integer.parseInt(parts[1]))); + } + } + + private void saveScores() { + for (int i = 0; i < scores.size; i++) { + Score score = scores.get(i); + boolean append = i > 0; + file.writeString(String.format("%s\t%d\n", score.scorer, score.score), append); + } + } + + private void sortScores() { + scores.sort(Score.SCORE_SORTER); + } + + public Score getCurrentHighScore() { + return scores.size > 0 ? scores.get(0) : null; + } + + public Score[] getHighScores(int count) { + Score[] ret = new Score[Math.min(count, scores.size)]; + System.arraycopy(scores.items, 0, ret, 0, ret.length); + return ret; + } + + public void addScore(Score score) { + scores.add(score); + sortScores(); + saveScores(); + } + +} diff --git a/core/src/com/me/pacman/PacDude.java b/core/src/com/me/pacman/PacDude.java index 89bec36..052cce2 100644 --- a/core/src/com/me/pacman/PacDude.java +++ b/core/src/com/me/pacman/PacDude.java @@ -2,12 +2,15 @@ package com.me.pacman; import com.badlogic.gdx.Game; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Screen; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.utils.viewport.FitViewport; import com.badlogic.gdx.utils.viewport.Viewport; +import com.me.pacman.state.HighScoresState; import com.me.pacman.state.MenuState; +import com.me.pacman.state.State; public class PacDude extends Game { @@ -19,6 +22,8 @@ public class PacDude extends Game { public static final int LEVEL_WIDTH = 224; public static final int LEVEL_HEIGHT = 288; + public HighScores highScores; + public Assets assets; public Sound sound; @@ -28,12 +33,16 @@ public class PacDude extends Game { public OrthographicCamera cam; public Viewport viewport; + private State nextState; + @Override public void create () { cam = new OrthographicCamera(); viewport = new FitViewport(LEVEL_WIDTH, LEVEL_HEIGHT, cam); viewport.apply(true); + highScores = new HighScores(); + assets = new Assets(); assets.loadAssets(); sound = new Sound(this); @@ -42,13 +51,21 @@ public class PacDude extends Game { fontRenderer = new FontRenderer(assets.font); Gdx.gl.glClearColor(0, 0, 0, 1); - setScreen(new MenuState(this)); + setNextState(new MenuState(this)); } @Override public void render () { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + if (nextState != null) { + Screen currScreen = getScreen(); + if (currScreen != null) currScreen.dispose(); + nextState.setup(); + super.setScreen(nextState); + nextState = null; + } + batch.begin(); if (DEBUG) { fontRenderer.draw(batch, "fps:" + Gdx.graphics.getFramesPerSecond(), 19 * 8, 34 * 8); @@ -71,4 +88,14 @@ public class PacDude extends Game { batch.dispose(); assets.dispose(); } + + @Override + public void setScreen(Screen screen) { + throw new IllegalStateException("Use setNextState instead."); + } + + public void setNextState(State state) { + this.nextState = state; + } + } diff --git a/core/src/com/me/pacman/Score.java b/core/src/com/me/pacman/Score.java new file mode 100644 index 0000000..05f3662 --- /dev/null +++ b/core/src/com/me/pacman/Score.java @@ -0,0 +1,22 @@ +package com.me.pacman; + +import java.util.Comparator; + +public final class Score { + + public String scorer; + public int score; + + public static final Comparator SCORE_SORTER = new Comparator() { + @Override + public int compare(Score a, Score b) { + return a.score < b.score? 1 : (a.score == b.score ? 0 : -1); + } + }; + + public Score(String scorer, int score) { + this.scorer = scorer; + this.score = score; + } + +} diff --git a/core/src/com/me/pacman/state/HighScoreEntryState.java b/core/src/com/me/pacman/state/HighScoreEntryState.java new file mode 100644 index 0000000..456b835 --- /dev/null +++ b/core/src/com/me/pacman/state/HighScoreEntryState.java @@ -0,0 +1,124 @@ +package com.me.pacman.state; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.InputAdapter; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.me.pacman.FontRenderer; +import com.me.pacman.Score; +import com.me.pacman.PacDude; + +public class HighScoreEntryState extends State { + + private Texture background; + private Score score; + + private int currLetter; + private char[] name; + + private boolean flash; + private float elapsed; + + public HighScoreEntryState(PacDude game, Score score) { + super(game); + this.score = score; + this.currLetter = 0; + this.flash = false; + this.elapsed = 0f; + this.name = score.scorer.toCharArray(); + } + + @Override + public void setup() { + this.background = game.assets.getHighScoresBackground(); + Gdx.input.setInputProcessor(this.new Controller()); + } + + @Override + public void render() { + game.batch.draw(background, 0, 16); + + game.fontRenderer.setColor(Color.YELLOW); + game.fontRenderer.draw(game.batch, "high scores", (8 * 8) + 4, 25 * 8); + + game.fontRenderer.setColor(Color.CHARTREUSE); + game.fontRenderer.draw(game.batch, "score name", (8 * 8) + 4, 23 * 8); + + + game.fontRenderer.setColor(Color.BLUE); + game.fontRenderer.draw(game.batch, "" + score.score, (8 * 8) + 4, 18 * 9); + for (int i = 0; i < 3; i++) { + game.fontRenderer.draw(game.batch, String.valueOf((i == currLetter && flash)? '_' : name[i]), (16 + i) * 8 + 4, 18 * 9); + } + + game.fontRenderer.setColor(Color.WHITE); + Score[] scores = game.highScores.getHighScores(9); + for (int i = 0; i < scores.length; i++) { + Score score = scores[i]; + game.fontRenderer.draw(game.batch, String.format("%-8d%s", score.score, score.scorer), (8 * 8) + 4, (16 - i) * 9); + } + } + + @Override + public void update(float dt) { + elapsed += dt; + if (elapsed >= 1/3f) { + elapsed = 0f; + flash = !flash; + } + } + + @Override + public void dispose() { + Gdx.input.setInputProcessor(null); + } + + private final class Controller extends InputAdapter { + + @Override + public boolean keyDown(int keycode) { + switch(keycode) { + case Input.Keys.DOWN: + int nextIndex = FontRenderer.CHAR_TO_INDEX[name[currLetter]] - 1; + if (nextIndex < 0) { + nextIndex = 25; + } + flash = false; + name[currLetter] = FontRenderer.CHARS[nextIndex]; + break; + case Input.Keys.UP: + nextIndex = FontRenderer.CHAR_TO_INDEX[name[currLetter]] + 1; + if (nextIndex > 25) { + nextIndex = 0; + } + flash = false; + name[currLetter] = FontRenderer.CHARS[nextIndex]; + break; + case Input.Keys.RIGHT: + currLetter++; + if (currLetter > 2) { + currLetter = 0; + } + flash = true; + break; + case Input.Keys.LEFT: + currLetter--; + if (currLetter < 0) { + currLetter = 2; + } + flash = true; + break; + case Input.Keys.ENTER: + score.scorer = String.valueOf(name); + game.highScores.addScore(score); + game.setNextState(new HighScoresState(game)); + break; + } + elapsed = 0f; + return super.keyDown(keycode); + } + + } + +} diff --git a/core/src/com/me/pacman/state/HighScoresState.java b/core/src/com/me/pacman/state/HighScoresState.java new file mode 100644 index 0000000..d61087a --- /dev/null +++ b/core/src/com/me/pacman/state/HighScoresState.java @@ -0,0 +1,67 @@ +package com.me.pacman.state; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.InputAdapter; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.me.pacman.PacDude; +import com.me.pacman.Score; + +public class HighScoresState extends State { + + + private Texture background; + + public HighScoresState(PacDude game) { + super(game); + } + + @Override + public void setup() { + this.background = game.assets.getHighScoresBackground(); + Gdx.input.setInputProcessor(this.new Controller()); + } + + @Override + public void render() { + game.batch.draw(background, 0, 16); + + game.fontRenderer.setColor(Color.YELLOW); + game.fontRenderer.draw(game.batch, "high scores", (8 * 8) + 4, 25 * 8); + + game.fontRenderer.setColor(Color.CHARTREUSE); + game.fontRenderer.draw(game.batch, "score name", (8 * 8) + 4, 23 * 8); + + game.fontRenderer.setColor(Color.WHITE); + Score[] scores = game.highScores.getHighScores(10); + for (int i = 0; i < scores.length; i++) { + Score score = scores[i]; + game.fontRenderer.draw(game.batch, String.format("%-8d%s", score.score, score.scorer), (8 * 8) + 4, (17 - i) * 9); + } + } + + @Override + public void dispose() { + Gdx.input.setInputProcessor(null); + } + + @Override + public void update(float dt) {} + + private final class Controller extends InputAdapter { + + @Override + public boolean keyDown(int keycode) { + switch(keycode) { + case Input.Keys.ENTER: + case Input.Keys.ESCAPE: + game.setNextState(new MenuState(game)); + break; + } + return super.keyDown(keycode); + } + + } + +} diff --git a/core/src/com/me/pacman/state/MenuState.java b/core/src/com/me/pacman/state/MenuState.java index 2e42adb..a2087b2 100644 --- a/core/src/com/me/pacman/state/MenuState.java +++ b/core/src/com/me/pacman/state/MenuState.java @@ -11,7 +11,7 @@ import com.me.pacman.Sound; public class MenuState extends LevelState { - private Texture levelBackground; + private Texture background; private Texture logo; @@ -29,7 +29,7 @@ public class MenuState extends LevelState { @Override public void setup() { - levelBackground = game.assets.getMenuBackground(); + background = game.assets.getMenuBackground(); logo = game.assets.getLogo(); logo.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); @@ -48,7 +48,7 @@ public class MenuState extends LevelState { @Override public void render() { - game.batch.draw(levelBackground, 0, 16); + game.batch.draw(background, 0, 16); game.batch.draw(logo, 0, 140, 224, 120); @@ -80,10 +80,10 @@ public class MenuState extends LevelState { case Input.Keys.ENTER: switch (selectedOption) { case NEW_GAME: - game.setScreen(new PlayState(game)); + game.setNextState(new PlayState(game)); break; case HIGH_SCORES: -// game.setScreen(new HighScores(game)); + game.setNextState(new HighScoresState(game)); break; } break; @@ -106,9 +106,9 @@ public class MenuState extends LevelState { public boolean touchUp(int screenX, int screenY, int pointer, int button) { Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY)); if (selectedOption == 0 && NEW_GAME_BOX.contains(coords)) { - game.setScreen(new PlayState(game)); + game.setNextState(new PlayState(game)); } else if (selectedOption == 1 && HIGH_SCORE_BOX.contains(coords)) { - selectedOption = -1; + game.setNextState(new HighScoresState(game)); } else { selectedOption = -1; } diff --git a/core/src/com/me/pacman/state/State.java b/core/src/com/me/pacman/state/State.java index 30087a9..5d28e44 100644 --- a/core/src/com/me/pacman/state/State.java +++ b/core/src/com/me/pacman/state/State.java @@ -10,7 +10,6 @@ public abstract class State extends ScreenAdapter { public State(PacDude game) { this.game = game; - this.setup(); } @Override