Add time mode scorer

This commit is contained in:
Lonami Exo 2017-02-04 19:35:02 +01:00
parent cafe83ad80
commit 47301864eb
5 changed files with 168 additions and 8 deletions

View file

@ -75,6 +75,25 @@ public class GameLayout {
area.width * 0.5f - cupSize * 0.5f, area.height); area.width * 0.5f - cupSize * 0.5f, area.height);
} }
void update(TimeScorer scorer) {
float cupSize = Math.min(scoreHeight, scorer.cupTexture.getHeight());
final Rectangle area = new Rectangle(
marginWidth, pieceHolderHeight + boardHeight,
availableWidth, scoreHeight);
scorer.cupArea.set(
area.x + area.width * 0.5f - cupSize * 0.5f, area.y,
cupSize, cupSize);
scorer.timeLeftLabel.setBounds(
area.x, area.y,
area.width * 0.5f - cupSize * 0.5f, area.height);
scorer.highTimeLabel.setBounds(
area.x + area.width * 0.5f + cupSize * 0.5f, area.y,
area.width * 0.5f - cupSize * 0.5f, area.height);
}
void update(Board board) { void update(Board board) {
// We can't leave our area, so pick the minimum between available // We can't leave our area, so pick the minimum between available
// height and width to determine an appropriated cell size // height and width to determine an appropriated cell size

View file

@ -0,0 +1,119 @@
package io.github.lonamiwebs.klooni.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.TimeUtils;
import io.github.lonamiwebs.klooni.Klooni;
public class TimeScorer {
//region Members
private final long startTime;
// Maximum time alive, in seconds
private int maxTimeScore;
// Indicates where we would die in time. Score adds to this, so we take
// longer to die. To get the "score" we simply calculate `deadTime - startTime`
private long deadTime;
final Label timeLeftLabel;
final Label highTimeLabel;
final Texture cupTexture;
final Rectangle cupArea;
private final Color cupColor;
private static final long START_TIME = 20 * 1000000000L;
//endregion
//region Constructor
// The board size is required when calculating the score
public TimeScorer(final Klooni game, GameLayout layout) {
startTime = TimeUtils.nanoTime();
deadTime = startTime + START_TIME;
cupTexture = new Texture(Gdx.files.internal("ui/cup.png"));
cupColor = Klooni.theme.currentScore.cpy();
cupArea = new Rectangle();
Label.LabelStyle labelStyle = new Label.LabelStyle();
labelStyle.font = game.skin.getFont("font");
timeLeftLabel = new Label("0", labelStyle);
timeLeftLabel.setColor(Klooni.theme.currentScore);
timeLeftLabel.setAlignment(Align.right);
highTimeLabel = new Label(Integer.toString(nanosToSeconds(maxTimeScore)), labelStyle);
highTimeLabel.setColor(Klooni.theme.highScore);
layout.update(this);
}
//endregion
//region Private methods
private void addScore(int score) {
deadTime += scoreToNanos(score);
}
private int calculateClearScore(int stripsCleared, int boardSize) {
if (stripsCleared < 1) return 0;
if (stripsCleared == 1) return boardSize;
else return boardSize * stripsCleared + calculateClearScore(stripsCleared - 1, boardSize);
}
private int nanosToSeconds(long nano) {
return MathUtils.ceil((float)(nano * 1e-09));
}
private long scoreToNanos(int score) {
// 1s/4p seems fair enough
return (long)((score / 4.0) * 1e+09);
}
public boolean isGameOver() {
return TimeUtils.nanoTime() > deadTime;
}
//endregion
//region Public methods
// Adds the score a given piece would give
public void addPieceScore(int areaPut) {
addScore(areaPut);
}
// Adds the score given by the board, this is, the count of cleared strips
public void addBoardScore(int stripsCleared, int boardSize) {
addScore(calculateClearScore(stripsCleared, boardSize));
}
public void draw(SpriteBatch batch) {
int timeLeft = Math.max(nanosToSeconds(deadTime - TimeUtils.nanoTime()), 0);
timeLeftLabel.setText(Integer.toString(timeLeft));
// If we beat a new record, the cup color will linear interpolate to the high score color
//cupColor.lerp(newRecord ? Klooni.theme.highScore : Klooni.theme.currentScore, 0.05f);
batch.setColor(cupColor);
batch.draw(cupTexture, cupArea.x, cupArea.y, cupArea.width, cupArea.height);
timeLeftLabel.draw(batch, 1f);
highTimeLabel.draw(batch, 1f);
}
//endregion
}

View file

@ -14,6 +14,7 @@ import io.github.lonamiwebs.klooni.game.GameLayout;
import io.github.lonamiwebs.klooni.game.Piece; import io.github.lonamiwebs.klooni.game.Piece;
import io.github.lonamiwebs.klooni.game.PieceHolder; import io.github.lonamiwebs.klooni.game.PieceHolder;
import io.github.lonamiwebs.klooni.game.Scorer; import io.github.lonamiwebs.klooni.game.Scorer;
import io.github.lonamiwebs.klooni.game.TimeScorer;
// Main game screen. Here the board, piece holder and score are shown // Main game screen. Here the board, piece holder and score are shown
class GameScreen implements Screen, InputProcessor { class GameScreen implements Screen, InputProcessor {
@ -21,6 +22,8 @@ class GameScreen implements Screen, InputProcessor {
//region Members //region Members
private final Scorer scorer; private final Scorer scorer;
private final TimeScorer timeScorer;
private final Board board; private final Board board;
private final PieceHolder holder; private final PieceHolder holder;
@ -29,6 +32,10 @@ class GameScreen implements Screen, InputProcessor {
private final PauseMenuStage pauseMenu; private final PauseMenuStage pauseMenu;
// TODO Perhaps make an abstract base class for the game screen and game modes
// by implementing different "isGameOver" etc. logic instead using an integer?
private final int gameMode;
//endregion //endregion
//region Static members //region Static members
@ -36,15 +43,21 @@ class GameScreen implements Screen, InputProcessor {
private final static int BOARD_SIZE = 10; private final static int BOARD_SIZE = 10;
private final static int HOLDER_PIECE_COUNT = 3; private final static int HOLDER_PIECE_COUNT = 3;
final static int GAME_MODE_SCORE = 0;
final static int GAME_MODE_TIME = 1;
//endregion //endregion
//region Constructor //region Constructor
GameScreen(final Klooni game) { GameScreen(final Klooni game, final int gameMode) {
batch = new SpriteBatch(); batch = new SpriteBatch();
this.gameMode = gameMode;
final GameLayout layout = new GameLayout(); final GameLayout layout = new GameLayout();
scorer = new Scorer(game, layout); scorer = new Scorer(game, layout);
timeScorer = new TimeScorer(game, layout);
board = new Board(layout, BOARD_SIZE); board = new Board(layout, BOARD_SIZE);
holder = new PieceHolder(layout, HOLDER_PIECE_COUNT, board.cellSize); holder = new PieceHolder(layout, HOLDER_PIECE_COUNT, board.cellSize);
pauseMenu = new PauseMenuStage(layout, game, scorer); pauseMenu = new PauseMenuStage(layout, game, scorer);
@ -82,9 +95,17 @@ class GameScreen implements Screen, InputProcessor {
Klooni.theme.glClearBackground(); Klooni.theme.glClearBackground();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// With the time mode, we always need to check whether it's game over or not
if (timeScorer.isGameOver() && !pauseMenu.isShown()) {
pauseMenu.show(true);
if (Klooni.soundsEnabled())
gameOverSound.play();
}
batch.begin(); batch.begin();
scorer.draw(batch); //scorer.draw(batch);
timeScorer.draw(batch);
board.draw(batch); board.draw(batch);
holder.update(); holder.update();
holder.draw(batch); holder.draw(batch);
@ -127,8 +148,10 @@ class GameScreen implements Screen, InputProcessor {
return false; return false;
if (action == PieceHolder.ON_BOARD_DROP) { if (action == PieceHolder.ON_BOARD_DROP) {
scorer.addPieceScore(area); //scorer.addPieceScore(area);
scorer.addBoardScore(board.clearComplete(), board.cellCount); //scorer.addBoardScore(board.clearComplete(), board.cellCount);
timeScorer.addPieceScore(area);
timeScorer.addBoardScore(board.clearComplete(), board.cellCount);
// After the piece was put, check if it's game over // After the piece was put, check if it's game over
if (isGameOver()) { if (isGameOver()) {

View file

@ -7,7 +7,6 @@ import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
@ -46,7 +45,8 @@ public class MainMenuScreen extends InputListener implements Screen {
final SoftButton playButton = new SoftButton(0, "play_texture"); final SoftButton playButton = new SoftButton(0, "play_texture");
playButton.addListener(new ChangeListener() { playButton.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) { public void changed (ChangeEvent event, Actor actor) {
MainMenuScreen.this.game.setScreen(new GameScreen(MainMenuScreen.this.game)); MainMenuScreen.this.game.setScreen(
new GameScreen(MainMenuScreen.this.game, GameScreen.GAME_MODE_SCORE));
dispose(); dispose();
} }
}); });

View file

@ -11,7 +11,6 @@ import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions; import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction; import com.badlogic.gdx.scenes.scene2d.actions.RunnableAction;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
@ -73,7 +72,7 @@ class PauseMenuStage extends Stage {
replayButton.addListener(new ChangeListener() { replayButton.addListener(new ChangeListener() {
@Override @Override
public void changed(ChangeEvent event, Actor actor) { public void changed(ChangeEvent event, Actor actor) {
game.setScreen(new GameScreen(game)); game.setScreen(new GameScreen(game, GameScreen.GAME_MODE_SCORE));
dispose(); dispose();
} }
}); });