package org.mafagafogigante.dungeon.achievements;
import org.mafagafogigante.dungeon.date.Date;
import org.mafagafogigante.dungeon.game.DungeonString;
import org.mafagafogigante.dungeon.game.Id;
import org.mafagafogigante.dungeon.io.Writer;
import org.mafagafogigante.dungeon.logging.DungeonLogger;
import org.mafagafogigante.dungeon.stats.Statistics;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* AchievementTracker that tracks the unlocked achievements.
*/
public class AchievementTracker implements Serializable {
private final Statistics statistics;
private final Map<Id, UnlockedAchievement> unlockedAchievements = new HashMap<>();
public AchievementTracker(@NotNull Statistics statistics) {
this.statistics = statistics;
}
/**
* Writes an achievement unlocked message with some information about the unlocked achievement.
*/
private static void writeAchievementUnlock(Achievement achievement, DungeonString builder) {
String format = "You unlocked the achievement %s because you %s.";
builder.append(String.format(format, achievement.getName(), achievement.getText()));
}
/**
* Returns how many unlocked achievements there are in this AchievementTracker.
*/
public int getUnlockedCount() {
return unlockedAchievements.size();
}
/**
* Unlock a specific Achievement.
*
* <p>If there already is an UnlockedAchievement with the same ID, a warning will be logged.
*
* @param achievement the Achievement to be unlocked.
*/
private void unlock(Achievement achievement, Date date, DungeonString builder) {
if (hasNotBeenUnlocked(achievement)) {
unlockedAchievements.put(achievement.getId(), new UnlockedAchievement(achievement, date));
writeAchievementUnlock(achievement, builder);
} else {
DungeonLogger.warning("Tried to unlock an already unlocked achievement.");
}
}
/**
* Return the UnlockedAchievement object that corresponds to a specific Achievement.
*
* @param achievement an Achievement object.
* @return the UnlockedAchievement that corresponds to this Achievement.
*/
private UnlockedAchievement getUnlockedAchievement(Achievement achievement) {
return unlockedAchievements.get(achievement.getId());
}
/**
* Returns a List with all the UnlockedAchievements in this AchievementTracker sorted using the provided Comparator.
*
* @param comparator a Comparator of UnlockedAchievements, not null
* @return a sorted List with all the UnlockedAchievements in this AchievementTracker
*/
public List<UnlockedAchievement> getUnlockedAchievements(Comparator<UnlockedAchievement> comparator) {
if (comparator == null) {
throw new IllegalArgumentException("comparator is null.");
}
List<UnlockedAchievement> list = new ArrayList<>(unlockedAchievements.values());
Collections.sort(list, comparator);
return list;
}
/**
* Return true if a given Achievement has not been unlocked yet.
*/
public boolean hasNotBeenUnlocked(@NotNull Achievement achievement) {
return getUnlockedAchievement(achievement) == null;
}
/**
* Updates this AchievementTracker by iterating over the achievements and unlocking the ones that are fulfilled but
* not yet added to the unlocked list of this tracker.
*
* <p>Before writing the first achievement unlock message, if there is one, a new line is written.
*/
public void update(AchievementStore achievementStore, Date date) {
DungeonString dungeonString = new DungeonString();
boolean wroteNewLine = false; // If we are going to write anything at all, we must start with a blank line.
for (Achievement achievement : achievementStore.getAchievements()) {
if (hasNotBeenUnlocked(achievement) && achievement.isFulfilled(statistics)) {
if (!wroteNewLine) {
dungeonString.append("\n");
wroteNewLine = true;
}
unlock(achievement, date, dungeonString);
dungeonString.append("\n");
}
}
if (dungeonString.getLength() != 0) {
Writer.write(dungeonString);
}
}
}