Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add leaderboard functionality #20

Merged
merged 11 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions game/core/src/com/eng1/game/game/Score.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
package com.eng1.game.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Json;
import com.eng1.game.game.player.Statistics;

import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@UtilityClass
public class Score {

private static final String SCORE_FILE = "scores.json"; // Change file extension to .json
private static final Json json = new Json(); // Create a Json instance

@Contract(pure = true)
public static @NotNull String getClassification(float scorePercentage) {
if (scorePercentage < 40) {
Expand All @@ -22,4 +37,75 @@
return "First Class with Distinction";
}
}

/**
* Retrieves the top 10 scores from the scores file.
*
* @return A list of the top 10 scores in the format Name,Score
*/
public static List<ScoreEntry> getTop10Scores() {
ArrayList<ScoreEntry> scores = new ArrayList<>();
FileHandle file = Gdx.files.local(SCORE_FILE);
if (file.exists()) {
scores = json.fromJson(ArrayList.class, Score.ScoreEntry.class, file);

Check warning on line 50 in game/core/src/com/eng1/game/game/Score.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unchecked warning

Unchecked assignment: 'java.util.ArrayList' to 'java.util.ArrayList'

Check warning on line 50 in game/core/src/com/eng1/game/game/Score.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Unchecked warning

Unchecked assignment: 'java.util.ArrayList' to 'java.util.ArrayList'
} else {
System.out.println("File does not exist: " + SCORE_FILE);
}
scores.sort(Comparator.comparingDouble(ScoreEntry::getScore).reversed());
return scores.size() > 10 ? scores.subList(0, 10) : scores;
}

public static int getLastScore() {
List<Score.ScoreEntry> topScores = Score.getTop10Scores();
if (topScores.size() < 10) {
return 0;
} else {
return topScores.get(topScores.size() - 1).getScore();
}
}

/**
* Saves a new high score to the score file.
*
* @param playerName The name of the player
* @param scorePercentage The score percentage achieved by the player
*/
public static void saveScore(String playerName, int scorePercentage) {
List<ScoreEntry> scores = getTop10Scores();
scores.add(new ScoreEntry(playerName, scorePercentage));
scores.sort(Comparator.comparingDouble(ScoreEntry::getScore).reversed());
FileHandle file = Gdx.files.local(SCORE_FILE);
// Write scores to the JSON file
file.writeString(json.prettyPrint(scores), false);
}

/**
* Calculates the final score percentage based on player statistics.
*
* @return The score percentage
*/
public static int calculateScorePercentage() {
float scoreTotal = Arrays.stream(Statistics.PlayerStatistics.values())
.map(Statistics.PlayerStatistics::getTotal)
.reduce(0f, Float::sum);
float maxTotal = Statistics.MAX_SCORE;
float score = (scoreTotal / maxTotal) * 0.8f;
return (int) Math.floor(score * 100);
}

@Getter
public static class ScoreEntry {
private final String playerName;
private final int score;

public ScoreEntry() {
this("", 0);
}

public ScoreEntry(String playerName, int score) {
this.playerName = playerName;
this.score = score;
}

}
}
116 changes: 42 additions & 74 deletions game/core/src/com/eng1/game/screens/EndScreen.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.eng1.game.screens;

import com.badlogic.gdx.Screen;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
Expand All @@ -12,119 +11,88 @@
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.eng1.game.game.Score;
import com.eng1.game.game.player.Statistics;

import java.util.Arrays;
import java.util.Map;

/**
* Represents the Endscreen screen of the game.
* Allows the player see their final score
*/
public class EndScreen implements Screen {
private final Stage stage = new Stage(new ScreenViewport());
private final Skin skin;
private int scorePercentage;

private final Stage stage;


/**
* Constructor for the EndScreen class.
* Initializes the parent orchestrator and creates a new stage for UI rendering.
*/
public EndScreen() {
// create stage and set it as input processor
stage = new Stage(new ScreenViewport());
this.skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
}

@Override
public void show() {
stage.clear();
Gdx.input.setInputProcessor(stage);

// Create a table that fills the screen. Everything else will go inside this table.
// Calculate the scorePercentage
this.scorePercentage = Score.calculateScorePercentage();

showGameOverScreen();
}

private void showGameOverScreen() {
stage.clear();
Table table = new Table();
table.setFillParent(true);
stage.addActor(table);

// temporary until we have asset manager in
Skin skin = new Skin(Gdx.files.internal("skin/uiskin.json"));

float scoreTotal = Arrays.stream(Statistics.PlayerStatistics.values()).map(Statistics.PlayerStatistics::getTotal).reduce(0f, Float::sum);
float maxTotal = Statistics.MAX_SCORE;

float score = (scoreTotal / maxTotal) * 0.8f;
int scorePercentage = (int) Math.floor(score * 100);
Label gameOverLabel = new Label("Game Over! You got " + scorePercentage + "% on your test!", skin);
gameOverLabel.setFontScale(1.5F);
table.add(gameOverLabel).colspan(2);
table.row().pad(10.0F, 0.0F, 0.0F, 10.0F);

// Add labels
Label titleLabel = new Label("Heslington Hustle", skin);
Label scoreLabel = new Label("Score: " + scorePercentage + "%", skin); // Retrieve the final score from Activity
Label classification = new Label("Classification: " + Score.getClassification(scorePercentage), skin);
Label classificationLabel = new Label("Classification: " + Score.getClassification((float) scorePercentage), skin);
classificationLabel.setFontScale(1.2F);
table.add(classificationLabel).colspan(2);
table.row().pad(10.0F, 0.0F, 0.0F, 10.0F);

Label achievementLabel = new Label("Achievements:", skin);
achievementLabel.setFontScale(1.2F);
table.add(achievementLabel).colspan(2);
table.row().pad(10.0F, 0.0F, 0.0F, 10.0F);

// Add actors to the table
table.add(titleLabel).colspan(2);
table.row().pad(10, 0, 0, 10);
TextButton continueButton = new TextButton("Continue", skin);
continueButton.getLabel().setFontScale(1.2F);
table.add(continueButton).colspan(2);

// Display completed activities
Map<String, Integer> completedActivities = Map.of();
for (String type : completedActivities.keySet()) {
table.add(new Label(type + ": " + completedActivities.get(type), skin)).left().pad(10);
table.row();
}
table.row().pad(10, 0, 0, 10);

table.add(scoreLabel).row();
table.add(classification).row();

// quit game button
final TextButton quitButton = new TextButton("Quit", skin);
quitButton.addListener(new ChangeListener() {
continueButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeEvent event, Actor actor) {
Gdx.app.exit();
if (scorePercentage > Score.getLastScore()) {
Screens.HIGHSCORE.setAsCurrent();
} else {
Screens.LEADERBOARD.setAsCurrent();
}
}
});

table.row().pad(10, 0, 0, 10);
table.add(quitButton).colspan(50);
stage.setKeyboardFocus(continueButton);
}


@Override
public void render(float delta) {
// clear the screen ready for next set of images to be drawn
Gdx.gl.glClearColor(0f, 0f, 0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

// tell our stage to do actions and draw itself
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
Gdx.gl.glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
Gdx.gl.glClear(16384);
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 0.033333335F));
stage.draw();

}

@Override
public void resize(int width, int height) {
// change the stage's viewport when the screen size is changed
stage.getViewport().update(width, height, true);
}

@Override
public void pause() {
// Not needed
}
public void pause() {}

@Override
public void resume() {
// Not needed
}
public void resume() {}

@Override
public void hide() {
// Not needed
}
public void hide() {}

@Override
public void dispose() {
// Not needed
}

public void dispose() {}
}
99 changes: 99 additions & 0 deletions game/core/src/com/eng1/game/screens/HighScoreEntryScreen.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.eng1.game.screens;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.*;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.eng1.game.game.Score;

public class HighScoreEntryScreen implements Screen {
private final Stage stage;
private final Skin skin;
private final int scorePercentage;

public HighScoreEntryScreen(int scorePercentage) {
this.stage = new Stage(new ScreenViewport());
this.skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
this.scorePercentage = scorePercentage;
}

@Override
public void show() {
stage.clear();
Gdx.input.setInputProcessor(stage);

Table table = new Table();
table.setFillParent(true);
stage.addActor(table);

Label highScoreLabel = new Label("Congratulations! You got a high score!", skin);
highScoreLabel.setFontScale(1.5f);
table.add(highScoreLabel).colspan(2).padBottom(20.0f);
table.row().pad(10.0f, 0.0f, 0.0f, 10.0f);

Label nameLabel = new Label("Enter your name:", skin);
nameLabel.setFontScale(1.2f);
table.add(nameLabel).colspan(2);
table.row().pad(10.0f, 0.0f, 0.0f, 10.0f);

final TextField nameField = new TextField("", skin);
nameField.getStyle().font.getData().setScale(1.2f);
table.add(nameField).colspan(2);
table.row().pad(10.0f, 0.0f, 0.0f, 10.0f);

TextButton submitButton = new TextButton("Save to leaderboard", skin);
submitButton.getLabel().setFontScale(1.2f);
table.add(submitButton).colspan(2);

submitButton.addListener(new ChangeListener() {
@Override
public void changed(ChangeListener.ChangeEvent event, Actor actor) {
String playerName = nameField.getText().trim().toLowerCase(); // Trim and convert to lowercase
if (playerName.isEmpty()) {
Dialog dialog = new Dialog("Error", skin, "dialog") {
{
text("Please enter your name.").pad(20);
button("OK");
}
};
dialog.getContentTable().pad(10);
dialog.show(stage);
} else {
Score.saveScore(playerName, scorePercentage);
Screens.LEADERBOARD.setAsCurrent();
}
}
});
stage.setKeyboardFocus(submitButton);
}

@Override
public void render(float delta) {
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Gdx.gl.glClear(16384);
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 0.033333335f));
stage.draw();
}

@Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}

@Override
public void pause() {}

@Override
public void resume() {}

@Override
public void hide() {}

@Override
public void dispose() {
stage.dispose();
}
}
Loading
Loading